summaryrefslogtreecommitdiff
path: root/utils/rbutilqt/mspack
diff options
context:
space:
mode:
Diffstat (limited to 'utils/rbutilqt/mspack')
-rw-r--r--utils/rbutilqt/mspack/COPYING.LIB504
-rw-r--r--utils/rbutilqt/mspack/README.ROCKBOX6
-rw-r--r--utils/rbutilqt/mspack/cab.h140
-rw-r--r--utils/rbutilqt/mspack/cabc.c24
-rw-r--r--utils/rbutilqt/mspack/cabd.c1508
-rw-r--r--utils/rbutilqt/mspack/chm.h122
-rw-r--r--utils/rbutilqt/mspack/chmc.c24
-rw-r--r--utils/rbutilqt/mspack/chmd.c1377
-rw-r--r--utils/rbutilqt/mspack/des.h15
-rw-r--r--utils/rbutilqt/mspack/hlp.h33
-rw-r--r--utils/rbutilqt/mspack/hlpc.c24
-rw-r--r--utils/rbutilqt/mspack/hlpd.c24
-rw-r--r--utils/rbutilqt/mspack/kwaj.h118
-rw-r--r--utils/rbutilqt/mspack/kwajc.c24
-rw-r--r--utils/rbutilqt/mspack/kwajd.c566
-rw-r--r--utils/rbutilqt/mspack/lit.h35
-rw-r--r--utils/rbutilqt/mspack/litc.c24
-rw-r--r--utils/rbutilqt/mspack/litd.c24
-rw-r--r--utils/rbutilqt/mspack/lzss.h66
-rw-r--r--utils/rbutilqt/mspack/lzssd.c93
-rw-r--r--utils/rbutilqt/mspack/lzx.h221
-rw-r--r--utils/rbutilqt/mspack/lzxc.c18
-rw-r--r--utils/rbutilqt/mspack/lzxd.c905
-rw-r--r--utils/rbutilqt/mspack/mspack.h2385
-rw-r--r--utils/rbutilqt/mspack/mszip.h126
-rw-r--r--utils/rbutilqt/mspack/mszipc.c18
-rw-r--r--utils/rbutilqt/mspack/mszipd.c515
-rw-r--r--utils/rbutilqt/mspack/qtm.h128
-rw-r--r--utils/rbutilqt/mspack/qtmd.c490
-rw-r--r--utils/rbutilqt/mspack/readbits.h207
-rw-r--r--utils/rbutilqt/mspack/readhuff.h172
-rw-r--r--utils/rbutilqt/mspack/sha.h15
-rw-r--r--utils/rbutilqt/mspack/system-mspack.c240
-rw-r--r--utils/rbutilqt/mspack/system-mspack.h129
-rw-r--r--utils/rbutilqt/mspack/szdd.h39
-rw-r--r--utils/rbutilqt/mspack/szddc.c24
-rw-r--r--utils/rbutilqt/mspack/szddd.c247
37 files changed, 10630 insertions, 0 deletions
diff --git a/utils/rbutilqt/mspack/COPYING.LIB b/utils/rbutilqt/mspack/COPYING.LIB
new file mode 100644
index 0000000000..b1e3f5a263
--- /dev/null
+++ b/utils/rbutilqt/mspack/COPYING.LIB
@@ -0,0 +1,504 @@
1 GNU LESSER GENERAL PUBLIC LICENSE
2 Version 2.1, February 1999
3
4 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
5 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed.
8
9[This is the first released version of the Lesser GPL. It also counts
10 as the successor of the GNU Library Public License, version 2, hence
11 the version number 2.1.]
12
13 Preamble
14
15 The licenses for most software are designed to take away your
16freedom to share and change it. By contrast, the GNU General Public
17Licenses are intended to guarantee your freedom to share and change
18free software--to make sure the software is free for all its users.
19
20 This license, the Lesser General Public License, applies to some
21specially designated software packages--typically libraries--of the
22Free Software Foundation and other authors who decide to use it. You
23can use it too, but we suggest you first think carefully about whether
24this license or the ordinary General Public License is the better
25strategy to use in any particular case, based on the explanations below.
26
27 When we speak of free software, we are referring to freedom of use,
28not price. Our General Public Licenses are designed to make sure that
29you have the freedom to distribute copies of free software (and charge
30for this service if you wish); that you receive source code or can get
31it if you want it; that you can change the software and use pieces of
32it in new free programs; and that you are informed that you can do
33these things.
34
35 To protect your rights, we need to make restrictions that forbid
36distributors to deny you these rights or to ask you to surrender these
37rights. These restrictions translate to certain responsibilities for
38you if you distribute copies of the library or if you modify it.
39
40 For example, if you distribute copies of the library, whether gratis
41or for a fee, you must give the recipients all the rights that we gave
42you. You must make sure that they, too, receive or can get the source
43code. If you link other code with the library, you must provide
44complete object files to the recipients, so that they can relink them
45with the library after making changes to the library and recompiling
46it. And you must show them these terms so they know their rights.
47
48 We protect your rights with a two-step method: (1) we copyright the
49library, and (2) we offer you this license, which gives you legal
50permission to copy, distribute and/or modify the library.
51
52 To protect each distributor, we want to make it very clear that
53there is no warranty for the free library. Also, if the library is
54modified by someone else and passed on, the recipients should know
55that what they have is not the original version, so that the original
56author's reputation will not be affected by problems that might be
57introduced by others.
58
59 Finally, software patents pose a constant threat to the existence of
60any free program. We wish to make sure that a company cannot
61effectively restrict the users of a free program by obtaining a
62restrictive license from a patent holder. Therefore, we insist that
63any patent license obtained for a version of the library must be
64consistent with the full freedom of use specified in this license.
65
66 Most GNU software, including some libraries, is covered by the
67ordinary GNU General Public License. This license, the GNU Lesser
68General Public License, applies to certain designated libraries, and
69is quite different from the ordinary General Public License. We use
70this license for certain libraries in order to permit linking those
71libraries into non-free programs.
72
73 When a program is linked with a library, whether statically or using
74a shared library, the combination of the two is legally speaking a
75combined work, a derivative of the original library. The ordinary
76General Public License therefore permits such linking only if the
77entire combination fits its criteria of freedom. The Lesser General
78Public License permits more lax criteria for linking other code with
79the library.
80
81 We call this license the "Lesser" General Public License because it
82does Less to protect the user's freedom than the ordinary General
83Public License. It also provides other free software developers Less
84of an advantage over competing non-free programs. These disadvantages
85are the reason we use the ordinary General Public License for many
86libraries. However, the Lesser license provides advantages in certain
87special circumstances.
88
89 For example, on rare occasions, there may be a special need to
90encourage the widest possible use of a certain library, so that it becomes
91a de-facto standard. To achieve this, non-free programs must be
92allowed to use the library. A more frequent case is that a free
93library does the same job as widely used non-free libraries. In this
94case, there is little to gain by limiting the free library to free
95software only, so we use the Lesser General Public License.
96
97 In other cases, permission to use a particular library in non-free
98programs enables a greater number of people to use a large body of
99free software. For example, permission to use the GNU C Library in
100non-free programs enables many more people to use the whole GNU
101operating system, as well as its variant, the GNU/Linux operating
102system.
103
104 Although the Lesser General Public License is Less protective of the
105users' freedom, it does ensure that the user of a program that is
106linked with the Library has the freedom and the wherewithal to run
107that program using a modified version of the Library.
108
109 The precise terms and conditions for copying, distribution and
110modification follow. Pay close attention to the difference between a
111"work based on the library" and a "work that uses the library". The
112former contains code derived from the library, whereas the latter must
113be combined with the library in order to run.
114
115 GNU LESSER GENERAL PUBLIC LICENSE
116 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
117
118 0. This License Agreement applies to any software library or other
119program which contains a notice placed by the copyright holder or
120other authorized party saying it may be distributed under the terms of
121this Lesser General Public License (also called "this License").
122Each licensee is addressed as "you".
123
124 A "library" means a collection of software functions and/or data
125prepared so as to be conveniently linked with application programs
126(which use some of those functions and data) to form executables.
127
128 The "Library", below, refers to any such software library or work
129which has been distributed under these terms. A "work based on the
130Library" means either the Library or any derivative work under
131copyright law: that is to say, a work containing the Library or a
132portion of it, either verbatim or with modifications and/or translated
133straightforwardly into another language. (Hereinafter, translation is
134included without limitation in the term "modification".)
135
136 "Source code" for a work means the preferred form of the work for
137making modifications to it. For a library, complete source code means
138all the source code for all modules it contains, plus any associated
139interface definition files, plus the scripts used to control compilation
140and installation of the library.
141
142 Activities other than copying, distribution and modification are not
143covered by this License; they are outside its scope. The act of
144running a program using the Library is not restricted, and output from
145such a program is covered only if its contents constitute a work based
146on the Library (independent of the use of the Library in a tool for
147writing it). Whether that is true depends on what the Library does
148and what the program that uses the Library does.
149
150 1. You may copy and distribute verbatim copies of the Library's
151complete source code as you receive it, in any medium, provided that
152you conspicuously and appropriately publish on each copy an
153appropriate copyright notice and disclaimer of warranty; keep intact
154all the notices that refer to this License and to the absence of any
155warranty; and distribute a copy of this License along with the
156Library.
157
158 You may charge a fee for the physical act of transferring a copy,
159and you may at your option offer warranty protection in exchange for a
160fee.
161
162 2. You may modify your copy or copies of the Library or any portion
163of it, thus forming a work based on the Library, and copy and
164distribute such modifications or work under the terms of Section 1
165above, provided that you also meet all of these conditions:
166
167 a) The modified work must itself be a software library.
168
169 b) You must cause the files modified to carry prominent notices
170 stating that you changed the files and the date of any change.
171
172 c) You must cause the whole of the work to be licensed at no
173 charge to all third parties under the terms of this License.
174
175 d) If a facility in the modified Library refers to a function or a
176 table of data to be supplied by an application program that uses
177 the facility, other than as an argument passed when the facility
178 is invoked, then you must make a good faith effort to ensure that,
179 in the event an application does not supply such function or
180 table, the facility still operates, and performs whatever part of
181 its purpose remains meaningful.
182
183 (For example, a function in a library to compute square roots has
184 a purpose that is entirely well-defined independent of the
185 application. Therefore, Subsection 2d requires that any
186 application-supplied function or table used by this function must
187 be optional: if the application does not supply it, the square
188 root function must still compute square roots.)
189
190These requirements apply to the modified work as a whole. If
191identifiable sections of that work are not derived from the Library,
192and can be reasonably considered independent and separate works in
193themselves, then this License, and its terms, do not apply to those
194sections when you distribute them as separate works. But when you
195distribute the same sections as part of a whole which is a work based
196on the Library, the distribution of the whole must be on the terms of
197this License, whose permissions for other licensees extend to the
198entire whole, and thus to each and every part regardless of who wrote
199it.
200
201Thus, it is not the intent of this section to claim rights or contest
202your rights to work written entirely by you; rather, the intent is to
203exercise the right to control the distribution of derivative or
204collective works based on the Library.
205
206In addition, mere aggregation of another work not based on the Library
207with the Library (or with a work based on the Library) on a volume of
208a storage or distribution medium does not bring the other work under
209the scope of this License.
210
211 3. You may opt to apply the terms of the ordinary GNU General Public
212License instead of this License to a given copy of the Library. To do
213this, you must alter all the notices that refer to this License, so
214that they refer to the ordinary GNU General Public License, version 2,
215instead of to this License. (If a newer version than version 2 of the
216ordinary GNU General Public License has appeared, then you can specify
217that version instead if you wish.) Do not make any other change in
218these notices.
219
220 Once this change is made in a given copy, it is irreversible for
221that copy, so the ordinary GNU General Public License applies to all
222subsequent copies and derivative works made from that copy.
223
224 This option is useful when you wish to copy part of the code of
225the Library into a program that is not a library.
226
227 4. You may copy and distribute the Library (or a portion or
228derivative of it, under Section 2) in object code or executable form
229under the terms of Sections 1 and 2 above provided that you accompany
230it with the complete corresponding machine-readable source code, which
231must be distributed under the terms of Sections 1 and 2 above on a
232medium customarily used for software interchange.
233
234 If distribution of object code is made by offering access to copy
235from a designated place, then offering equivalent access to copy the
236source code from the same place satisfies the requirement to
237distribute the source code, even though third parties are not
238compelled to copy the source along with the object code.
239
240 5. A program that contains no derivative of any portion of the
241Library, but is designed to work with the Library by being compiled or
242linked with it, is called a "work that uses the Library". Such a
243work, in isolation, is not a derivative work of the Library, and
244therefore falls outside the scope of this License.
245
246 However, linking a "work that uses the Library" with the Library
247creates an executable that is a derivative of the Library (because it
248contains portions of the Library), rather than a "work that uses the
249library". The executable is therefore covered by this License.
250Section 6 states terms for distribution of such executables.
251
252 When a "work that uses the Library" uses material from a header file
253that is part of the Library, the object code for the work may be a
254derivative work of the Library even though the source code is not.
255Whether this is true is especially significant if the work can be
256linked without the Library, or if the work is itself a library. The
257threshold for this to be true is not precisely defined by law.
258
259 If such an object file uses only numerical parameters, data
260structure layouts and accessors, and small macros and small inline
261functions (ten lines or less in length), then the use of the object
262file is unrestricted, regardless of whether it is legally a derivative
263work. (Executables containing this object code plus portions of the
264Library will still fall under Section 6.)
265
266 Otherwise, if the work is a derivative of the Library, you may
267distribute the object code for the work under the terms of Section 6.
268Any executables containing that work also fall under Section 6,
269whether or not they are linked directly with the Library itself.
270
271 6. As an exception to the Sections above, you may also combine or
272link a "work that uses the Library" with the Library to produce a
273work containing portions of the Library, and distribute that work
274under terms of your choice, provided that the terms permit
275modification of the work for the customer's own use and reverse
276engineering for debugging such modifications.
277
278 You must give prominent notice with each copy of the work that the
279Library is used in it and that the Library and its use are covered by
280this License. You must supply a copy of this License. If the work
281during execution displays copyright notices, you must include the
282copyright notice for the Library among them, as well as a reference
283directing the user to the copy of this License. Also, you must do one
284of these things:
285
286 a) Accompany the work with the complete corresponding
287 machine-readable source code for the Library including whatever
288 changes were used in the work (which must be distributed under
289 Sections 1 and 2 above); and, if the work is an executable linked
290 with the Library, with the complete machine-readable "work that
291 uses the Library", as object code and/or source code, so that the
292 user can modify the Library and then relink to produce a modified
293 executable containing the modified Library. (It is understood
294 that the user who changes the contents of definitions files in the
295 Library will not necessarily be able to recompile the application
296 to use the modified definitions.)
297
298 b) Use a suitable shared library mechanism for linking with the
299 Library. A suitable mechanism is one that (1) uses at run time a
300 copy of the library already present on the user's computer system,
301 rather than copying library functions into the executable, and (2)
302 will operate properly with a modified version of the library, if
303 the user installs one, as long as the modified version is
304 interface-compatible with the version that the work was made with.
305
306 c) Accompany the work with a written offer, valid for at
307 least three years, to give the same user the materials
308 specified in Subsection 6a, above, for a charge no more
309 than the cost of performing this distribution.
310
311 d) If distribution of the work is made by offering access to copy
312 from a designated place, offer equivalent access to copy the above
313 specified materials from the same place.
314
315 e) Verify that the user has already received a copy of these
316 materials or that you have already sent this user a copy.
317
318 For an executable, the required form of the "work that uses the
319Library" must include any data and utility programs needed for
320reproducing the executable from it. However, as a special exception,
321the materials to be distributed need not include anything that is
322normally distributed (in either source or binary form) with the major
323components (compiler, kernel, and so on) of the operating system on
324which the executable runs, unless that component itself accompanies
325the executable.
326
327 It may happen that this requirement contradicts the license
328restrictions of other proprietary libraries that do not normally
329accompany the operating system. Such a contradiction means you cannot
330use both them and the Library together in an executable that you
331distribute.
332
333 7. You may place library facilities that are a work based on the
334Library side-by-side in a single library together with other library
335facilities not covered by this License, and distribute such a combined
336library, provided that the separate distribution of the work based on
337the Library and of the other library facilities is otherwise
338permitted, and provided that you do these two things:
339
340 a) Accompany the combined library with a copy of the same work
341 based on the Library, uncombined with any other library
342 facilities. This must be distributed under the terms of the
343 Sections above.
344
345 b) Give prominent notice with the combined library of the fact
346 that part of it is a work based on the Library, and explaining
347 where to find the accompanying uncombined form of the same work.
348
349 8. You may not copy, modify, sublicense, link with, or distribute
350the Library except as expressly provided under this License. Any
351attempt otherwise to copy, modify, sublicense, link with, or
352distribute the Library is void, and will automatically terminate your
353rights under this License. However, parties who have received copies,
354or rights, from you under this License will not have their licenses
355terminated so long as such parties remain in full compliance.
356
357 9. You are not required to accept this License, since you have not
358signed it. However, nothing else grants you permission to modify or
359distribute the Library or its derivative works. These actions are
360prohibited by law if you do not accept this License. Therefore, by
361modifying or distributing the Library (or any work based on the
362Library), you indicate your acceptance of this License to do so, and
363all its terms and conditions for copying, distributing or modifying
364the Library or works based on it.
365
366 10. Each time you redistribute the Library (or any work based on the
367Library), the recipient automatically receives a license from the
368original licensor to copy, distribute, link with or modify the Library
369subject to these terms and conditions. You may not impose any further
370restrictions on the recipients' exercise of the rights granted herein.
371You are not responsible for enforcing compliance by third parties with
372this License.
373
374 11. If, as a consequence of a court judgment or allegation of patent
375infringement or for any other reason (not limited to patent issues),
376conditions are imposed on you (whether by court order, agreement or
377otherwise) that contradict the conditions of this License, they do not
378excuse you from the conditions of this License. If you cannot
379distribute so as to satisfy simultaneously your obligations under this
380License and any other pertinent obligations, then as a consequence you
381may not distribute the Library at all. For example, if a patent
382license would not permit royalty-free redistribution of the Library by
383all those who receive copies directly or indirectly through you, then
384the only way you could satisfy both it and this License would be to
385refrain entirely from distribution of the Library.
386
387If any portion of this section is held invalid or unenforceable under any
388particular circumstance, the balance of the section is intended to apply,
389and the section as a whole is intended to apply in other circumstances.
390
391It is not the purpose of this section to induce you to infringe any
392patents or other property right claims or to contest validity of any
393such claims; this section has the sole purpose of protecting the
394integrity of the free software distribution system which is
395implemented by public license practices. Many people have made
396generous contributions to the wide range of software distributed
397through that system in reliance on consistent application of that
398system; it is up to the author/donor to decide if he or she is willing
399to distribute software through any other system and a licensee cannot
400impose that choice.
401
402This section is intended to make thoroughly clear what is believed to
403be a consequence of the rest of this License.
404
405 12. If the distribution and/or use of the Library is restricted in
406certain countries either by patents or by copyrighted interfaces, the
407original copyright holder who places the Library under this License may add
408an explicit geographical distribution limitation excluding those countries,
409so that distribution is permitted only in or among countries not thus
410excluded. In such case, this License incorporates the limitation as if
411written in the body of this License.
412
413 13. The Free Software Foundation may publish revised and/or new
414versions of the Lesser General Public License from time to time.
415Such new versions will be similar in spirit to the present version,
416but may differ in detail to address new problems or concerns.
417
418Each version is given a distinguishing version number. If the Library
419specifies a version number of this License which applies to it and
420"any later version", you have the option of following the terms and
421conditions either of that version or of any later version published by
422the Free Software Foundation. If the Library does not specify a
423license version number, you may choose any version ever published by
424the Free Software Foundation.
425
426 14. If you wish to incorporate parts of the Library into other free
427programs whose distribution conditions are incompatible with these,
428write to the author to ask for permission. For software which is
429copyrighted by the Free Software Foundation, write to the Free
430Software Foundation; we sometimes make exceptions for this. Our
431decision will be guided by the two goals of preserving the free status
432of all derivatives of our free software and of promoting the sharing
433and reuse of software generally.
434
435 NO WARRANTY
436
437 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
438WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
439EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
440OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
441KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
442IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
443PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
444LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
445THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
446
447 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
448WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
449AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
450FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
451CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
452LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
453RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
454FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
455SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
456DAMAGES.
457
458 END OF TERMS AND CONDITIONS
459
460 How to Apply These Terms to Your New Libraries
461
462 If you develop a new library, and you want it to be of the greatest
463possible use to the public, we recommend making it free software that
464everyone can redistribute and change. You can do so by permitting
465redistribution under these terms (or, alternatively, under the terms of the
466ordinary General Public License).
467
468 To apply these terms, attach the following notices to the library. It is
469safest to attach them to the start of each source file to most effectively
470convey the exclusion of warranty; and each file should have at least the
471"copyright" line and a pointer to where the full notice is found.
472
473 <one line to give the library's name and a brief idea of what it does.>
474 Copyright (C) <year> <name of author>
475
476 This library is free software; you can redistribute it and/or
477 modify it under the terms of the GNU Lesser General Public
478 License as published by the Free Software Foundation; either
479 version 2.1 of the License, or (at your option) any later version.
480
481 This library is distributed in the hope that it will be useful,
482 but WITHOUT ANY WARRANTY; without even the implied warranty of
483 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
484 Lesser General Public License for more details.
485
486 You should have received a copy of the GNU Lesser General Public
487 License along with this library; if not, write to the Free Software
488 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
489
490Also add information on how to contact you by electronic and paper mail.
491
492You should also get your employer (if you work as a programmer) or your
493school, if any, to sign a "copyright disclaimer" for the library, if
494necessary. Here is a sample; alter the names:
495
496 Yoyodyne, Inc., hereby disclaims all copyright interest in the
497 library `Frob' (a library for tweaking knobs) written by James Random Hacker.
498
499 <signature of Ty Coon>, 1 April 1990
500 Ty Coon, President of Vice
501
502That's all there is to it!
503
504
diff --git a/utils/rbutilqt/mspack/README.ROCKBOX b/utils/rbutilqt/mspack/README.ROCKBOX
new file mode 100644
index 0000000000..220691af2c
--- /dev/null
+++ b/utils/rbutilqt/mspack/README.ROCKBOX
@@ -0,0 +1,6 @@
1This folder contains the mspack project for MS files compression/decompression.
2These files are distributed under the LGPL.
3
4The source files have been last synced with libmspack-0.10.1alpha
5https://www.cabextract.org.uk/libmspack/ on June 8, 2020
6
diff --git a/utils/rbutilqt/mspack/cab.h b/utils/rbutilqt/mspack/cab.h
new file mode 100644
index 0000000000..79d9951252
--- /dev/null
+++ b/utils/rbutilqt/mspack/cab.h
@@ -0,0 +1,140 @@
1/* This file is part of libmspack.
2 * (C) 2003-2018 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifndef MSPACK_CAB_H
11#define MSPACK_CAB_H 1
12
13/* generic CAB definitions */
14
15/* structure offsets */
16#define cfhead_Signature (0x00)
17#define cfhead_CabinetSize (0x08)
18#define cfhead_FileOffset (0x10)
19#define cfhead_MinorVersion (0x18)
20#define cfhead_MajorVersion (0x19)
21#define cfhead_NumFolders (0x1A)
22#define cfhead_NumFiles (0x1C)
23#define cfhead_Flags (0x1E)
24#define cfhead_SetID (0x20)
25#define cfhead_CabinetIndex (0x22)
26#define cfhead_SIZEOF (0x24)
27#define cfheadext_HeaderReserved (0x00)
28#define cfheadext_FolderReserved (0x02)
29#define cfheadext_DataReserved (0x03)
30#define cfheadext_SIZEOF (0x04)
31#define cffold_DataOffset (0x00)
32#define cffold_NumBlocks (0x04)
33#define cffold_CompType (0x06)
34#define cffold_SIZEOF (0x08)
35#define cffile_UncompressedSize (0x00)
36#define cffile_FolderOffset (0x04)
37#define cffile_FolderIndex (0x08)
38#define cffile_Date (0x0A)
39#define cffile_Time (0x0C)
40#define cffile_Attribs (0x0E)
41#define cffile_SIZEOF (0x10)
42#define cfdata_CheckSum (0x00)
43#define cfdata_CompressedSize (0x04)
44#define cfdata_UncompressedSize (0x06)
45#define cfdata_SIZEOF (0x08)
46
47/* flags */
48#define cffoldCOMPTYPE_MASK (0x000f)
49#define cffoldCOMPTYPE_NONE (0x0000)
50#define cffoldCOMPTYPE_MSZIP (0x0001)
51#define cffoldCOMPTYPE_QUANTUM (0x0002)
52#define cffoldCOMPTYPE_LZX (0x0003)
53#define cfheadPREV_CABINET (0x0001)
54#define cfheadNEXT_CABINET (0x0002)
55#define cfheadRESERVE_PRESENT (0x0004)
56#define cffileCONTINUED_FROM_PREV (0xFFFD)
57#define cffileCONTINUED_TO_NEXT (0xFFFE)
58#define cffileCONTINUED_PREV_AND_NEXT (0xFFFF)
59
60/* CAB data blocks are <= 32768 bytes in uncompressed form. Uncompressed
61 * blocks have zero growth. MSZIP guarantees that it won't grow above
62 * uncompressed size by more than 12 bytes. LZX guarantees it won't grow
63 * more than 6144 bytes. Quantum has no documentation, but the largest
64 * block seen in the wild is 337 bytes above uncompressed size.
65 */
66#define CAB_BLOCKMAX (32768)
67#define CAB_INPUTMAX (CAB_BLOCKMAX+6144)
68
69/* input buffer needs to be CAB_INPUTMAX + 1 byte to allow for max-sized block
70 * plus 1 trailer byte added by cabd_sys_read_block() for Quantum alignment.
71 *
72 * When MSCABD_PARAM_SALVAGE is set, block size is not checked so can be
73 * up to 65535 bytes, so max input buffer size needed is 65535 + 1
74 */
75#define CAB_INPUTMAX_SALVAGE (65535)
76#define CAB_INPUTBUF (CAB_INPUTMAX_SALVAGE + 1)
77
78/* There are no more than 65535 data blocks per folder, so a folder cannot
79 * be more than 32768*65535 bytes in length. As files cannot span more than
80 * one folder, this is also their max offset, length and offset+length limit.
81 */
82#define CAB_FOLDERMAX (65535)
83#define CAB_LENGTHMAX (CAB_BLOCKMAX * CAB_FOLDERMAX)
84
85/* CAB compression definitions */
86
87struct mscab_compressor_p {
88 struct mscab_compressor base;
89 struct mspack_system *system;
90 /* todo */
91};
92
93/* CAB decompression definitions */
94
95struct mscabd_decompress_state {
96 struct mscabd_folder_p *folder; /* current folder we're extracting from */
97 struct mscabd_folder_data *data; /* current folder split we're in */
98 unsigned int offset; /* uncompressed offset within folder */
99 unsigned int block; /* which block are we decompressing? */
100 off_t outlen; /* cumulative sum of block output sizes */
101 struct mspack_system sys; /* special I/O code for decompressor */
102 int comp_type; /* type of compression used by folder */
103 int (*decompress)(void *, off_t); /* decompressor code */
104 void *state; /* decompressor state */
105 struct mscabd_cabinet_p *incab; /* cabinet where input data comes from */
106 struct mspack_file *infh; /* input file handle */
107 struct mspack_file *outfh; /* output file handle */
108 unsigned char *i_ptr, *i_end; /* input data consumed, end */
109 unsigned char input[CAB_INPUTBUF]; /* one input block of data */
110};
111
112struct mscab_decompressor_p {
113 struct mscab_decompressor base;
114 struct mscabd_decompress_state *d;
115 struct mspack_system *system;
116 int buf_size, searchbuf_size, fix_mszip, salvage; /* params */
117 int error, read_error;
118};
119
120struct mscabd_cabinet_p {
121 struct mscabd_cabinet base;
122 off_t blocks_off; /* offset to data blocks */
123 int block_resv; /* reserved space in data blocks */
124};
125
126/* there is one of these for every cabinet a folder spans */
127struct mscabd_folder_data {
128 struct mscabd_folder_data *next;
129 struct mscabd_cabinet_p *cab; /* cabinet file of this folder span */
130 off_t offset; /* cabinet offset of first datablock */
131};
132
133struct mscabd_folder_p {
134 struct mscabd_folder base;
135 struct mscabd_folder_data data; /* where are the data blocks? */
136 struct mscabd_file *merge_prev; /* first file needing backwards merge */
137 struct mscabd_file *merge_next; /* first file needing forwards merge */
138};
139
140#endif
diff --git a/utils/rbutilqt/mspack/cabc.c b/utils/rbutilqt/mspack/cabc.c
new file mode 100644
index 0000000000..eb85011056
--- /dev/null
+++ b/utils/rbutilqt/mspack/cabc.c
@@ -0,0 +1,24 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10/* CAB compression implementation */
11
12#include "system-mspack.h"
13#include "cab.h"
14
15struct mscab_compressor *
16 mspack_create_cab_compressor(struct mspack_system *sys)
17{
18 /* todo */
19 return NULL;
20}
21
22void mspack_destroy_cab_compressor(struct mscab_compressor *self) {
23 /* todo */
24}
diff --git a/utils/rbutilqt/mspack/cabd.c b/utils/rbutilqt/mspack/cabd.c
new file mode 100644
index 0000000000..ae66769b24
--- /dev/null
+++ b/utils/rbutilqt/mspack/cabd.c
@@ -0,0 +1,1508 @@
1/* This file is part of libmspack.
2 * (C) 2003-2018 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10/* Cabinet (.CAB) files are a form of file archive. Each cabinet contains
11 * "folders", which are compressed spans of data. Each cabinet has
12 * "files", whose metadata is in the cabinet header, but whose actual data
13 * is stored compressed in one of the "folders". Cabinets can span more
14 * than one physical file on disk, in which case they are a "cabinet set",
15 * and usually the last folder of each cabinet extends into the next
16 * cabinet.
17 *
18 * For a complete description of the format, see the MSDN site:
19 * http://msdn.microsoft.com/en-us/library/bb267310.aspx
20 */
21
22/* CAB decompression implementation */
23
24#include "system-mspack.h"
25#include "cab.h"
26#include "mszip.h"
27#include "lzx.h"
28#include "qtm.h"
29
30/* Notes on compliance with cabinet specification:
31 *
32 * One of the main changes between cabextract 0.6 and libmspack's cab
33 * decompressor is the move from block-oriented decompression to
34 * stream-oriented decompression.
35 *
36 * cabextract would read one data block from disk, decompress it with the
37 * appropriate method, then write the decompressed data. The CAB
38 * specification is specifically designed to work like this, as it ensures
39 * compression matches do not span the maximum decompressed block size
40 * limit of 32kb.
41 *
42 * However, the compression algorithms used are stream oriented, with
43 * specific hacks added to them to enforce the "individual 32kb blocks"
44 * rule in CABs. In other file formats, they do not have this limitation.
45 *
46 * In order to make more generalised decompressors, libmspack's CAB
47 * decompressor has moved from being block-oriented to more stream
48 * oriented. This also makes decompression slightly faster.
49 *
50 * However, this leads to incompliance with the CAB specification. The
51 * CAB controller can no longer ensure each block of input given to the
52 * decompressors is matched with their output. The "decompressed size" of
53 * each individual block is thrown away.
54 *
55 * Each CAB block is supposed to be seen as individually compressed. This
56 * means each consecutive data block can have completely different
57 * "uncompressed" sizes, ranging from 1 to 32768 bytes. However, in
58 * reality, all data blocks in a folder decompress to exactly 32768 bytes,
59 * excepting the final block.
60 *
61 * Given this situation, the decompression algorithms are designed to
62 * realign their input bitstreams on 32768 output-byte boundaries, and
63 * various other special cases have been made. libmspack will not
64 * correctly decompress LZX or Quantum compressed folders where the blocks
65 * do not follow this "32768 bytes until last block" pattern. It could be
66 * implemented if needed, but hopefully this is not necessary -- it has
67 * not been seen in over 3Gb of CAB archives.
68 */
69
70/* prototypes */
71static struct mscabd_cabinet * cabd_open(
72 struct mscab_decompressor *base, const char *filename);
73static void cabd_close(
74 struct mscab_decompressor *base, struct mscabd_cabinet *origcab);
75static int cabd_read_headers(
76 struct mspack_system *sys, struct mspack_file *fh,
77 struct mscabd_cabinet_p *cab, off_t offset, int salvage, int quiet);
78static char *cabd_read_string(
79 struct mspack_system *sys, struct mspack_file *fh, int *error);
80
81static struct mscabd_cabinet *cabd_search(
82 struct mscab_decompressor *base, const char *filename);
83static int cabd_find(
84 struct mscab_decompressor_p *self, unsigned char *buf,
85 struct mspack_file *fh, const char *filename, off_t flen,
86 off_t *firstlen, struct mscabd_cabinet_p **firstcab);
87
88static int cabd_prepend(
89 struct mscab_decompressor *base, struct mscabd_cabinet *cab,
90 struct mscabd_cabinet *prevcab);
91static int cabd_append(
92 struct mscab_decompressor *base, struct mscabd_cabinet *cab,
93 struct mscabd_cabinet *nextcab);
94static int cabd_merge(
95 struct mscab_decompressor *base, struct mscabd_cabinet *lcab,
96 struct mscabd_cabinet *rcab);
97static int cabd_can_merge_folders(
98 struct mspack_system *sys, struct mscabd_folder_p *lfol,
99 struct mscabd_folder_p *rfol);
100
101static int cabd_extract(
102 struct mscab_decompressor *base, struct mscabd_file *file,
103 const char *filename);
104static int cabd_init_decomp(
105 struct mscab_decompressor_p *self, unsigned int ct);
106static void cabd_free_decomp(
107 struct mscab_decompressor_p *self);
108static int cabd_sys_read(
109 struct mspack_file *file, void *buffer, int bytes);
110static int cabd_sys_write(
111 struct mspack_file *file, void *buffer, int bytes);
112static int cabd_sys_read_block(
113 struct mspack_system *sys, struct mscabd_decompress_state *d, int *out,
114 int ignore_cksum, int ignore_blocksize);
115static unsigned int cabd_checksum(
116 unsigned char *data, unsigned int bytes, unsigned int cksum);
117static struct noned_state *noned_init(
118 struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out,
119 int bufsize);
120
121static int noned_decompress(
122 struct noned_state *s, off_t bytes);
123static void noned_free(
124 struct noned_state *state);
125
126static int cabd_param(
127 struct mscab_decompressor *base, int param, int value);
128
129static int cabd_error(
130 struct mscab_decompressor *base);
131
132
133/***************************************
134 * MSPACK_CREATE_CAB_DECOMPRESSOR
135 ***************************************
136 * constructor
137 */
138struct mscab_decompressor *
139 mspack_create_cab_decompressor(struct mspack_system *sys)
140{
141 struct mscab_decompressor_p *self = NULL;
142
143 if (!sys) sys = mspack_default_system;
144 if (!mspack_valid_system(sys)) return NULL;
145
146 if ((self = (struct mscab_decompressor_p *) sys->alloc(sys, sizeof(struct mscab_decompressor_p)))) {
147 self->base.open = &cabd_open;
148 self->base.close = &cabd_close;
149 self->base.search = &cabd_search;
150 self->base.extract = &cabd_extract;
151 self->base.prepend = &cabd_prepend;
152 self->base.append = &cabd_append;
153 self->base.set_param = &cabd_param;
154 self->base.last_error = &cabd_error;
155 self->system = sys;
156 self->d = NULL;
157 self->error = MSPACK_ERR_OK;
158
159 self->searchbuf_size = 32768;
160 self->fix_mszip = 0;
161 self->buf_size = 4096;
162 self->salvage = 0;
163 }
164 return (struct mscab_decompressor *) self;
165}
166
167/***************************************
168 * MSPACK_DESTROY_CAB_DECOMPRESSOR
169 ***************************************
170 * destructor
171 */
172void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) {
173 struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
174 if (self) {
175 struct mspack_system *sys = self->system;
176 if (self->d) {
177 if (self->d->infh) sys->close(self->d->infh);
178 cabd_free_decomp(self);
179 sys->free(self->d);
180 }
181 sys->free(self);
182 }
183}
184
185
186/***************************************
187 * CABD_OPEN
188 ***************************************
189 * opens a file and tries to read it as a cabinet file
190 */
191static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base,
192 const char *filename)
193{
194 struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
195 struct mscabd_cabinet_p *cab = NULL;
196 struct mspack_system *sys;
197 struct mspack_file *fh;
198 int error;
199
200 if (!base) return NULL;
201 sys = self->system;
202
203 if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) {
204 if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) {
205 cab->base.filename = filename;
206 error = cabd_read_headers(sys, fh, cab, (off_t) 0, self->salvage, 0);
207 if (error) {
208 cabd_close(base, (struct mscabd_cabinet *) cab);
209 cab = NULL;
210 }
211 self->error = error;
212 }
213 else {
214 self->error = MSPACK_ERR_NOMEMORY;
215 }
216 sys->close(fh);
217 }
218 else {
219 self->error = MSPACK_ERR_OPEN;
220 }
221 return (struct mscabd_cabinet *) cab;
222}
223
224/***************************************
225 * CABD_CLOSE
226 ***************************************
227 * frees all memory associated with a given mscabd_cabinet.
228 */
229static void cabd_close(struct mscab_decompressor *base,
230 struct mscabd_cabinet *origcab)
231{
232 struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
233 struct mscabd_folder_data *dat, *ndat;
234 struct mscabd_cabinet *cab, *ncab;
235 struct mscabd_folder *fol, *nfol;
236 struct mscabd_file *fi, *nfi;
237 struct mspack_system *sys;
238
239 if (!base) return;
240 sys = self->system;
241
242 self->error = MSPACK_ERR_OK;
243
244 while (origcab) {
245 /* free files */
246 for (fi = origcab->files; fi; fi = nfi) {
247 nfi = fi->next;
248 sys->free(fi->filename);
249 sys->free(fi);
250 }
251
252 /* free folders */
253 for (fol = origcab->folders; fol; fol = nfol) {
254 nfol = fol->next;
255
256 /* free folder decompression state if it has been decompressed */
257 if (self->d && (self->d->folder == (struct mscabd_folder_p *) fol)) {
258 if (self->d->infh) sys->close(self->d->infh);
259 cabd_free_decomp(self);
260 sys->free(self->d);
261 self->d = NULL;
262 }
263
264 /* free folder data segments */
265 for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) {
266 ndat = dat->next;
267 sys->free(dat);
268 }
269 sys->free(fol);
270 }
271
272 /* free predecessor cabinets (and the original cabinet's strings) */
273 for (cab = origcab; cab; cab = ncab) {
274 ncab = cab->prevcab;
275 sys->free(cab->prevname);
276 sys->free(cab->nextname);
277 sys->free(cab->previnfo);
278 sys->free(cab->nextinfo);
279 if (cab != origcab) sys->free(cab);
280 }
281
282 /* free successor cabinets */
283 for (cab = origcab->nextcab; cab; cab = ncab) {
284 ncab = cab->nextcab;
285 sys->free(cab->prevname);
286 sys->free(cab->nextname);
287 sys->free(cab->previnfo);
288 sys->free(cab->nextinfo);
289 sys->free(cab);
290 }
291
292 /* free actual cabinet structure */
293 cab = origcab->next;
294 sys->free(origcab);
295
296 /* repeat full procedure again with the cab->next pointer (if set) */
297 origcab = cab;
298 }
299}
300
301/***************************************
302 * CABD_READ_HEADERS
303 ***************************************
304 * reads the cabinet file header, folder list and file list.
305 * fills out a pre-existing mscabd_cabinet structure, allocates memory
306 * for folders and files as necessary
307 */
308static int cabd_read_headers(struct mspack_system *sys,
309 struct mspack_file *fh,
310 struct mscabd_cabinet_p *cab,
311 off_t offset, int salvage, int quiet)
312{
313 int num_folders, num_files, folder_resv, i, x, err, fidx;
314 struct mscabd_folder_p *fol, *linkfol = NULL;
315 struct mscabd_file *file, *linkfile = NULL;
316 unsigned char buf[64];
317
318 /* initialise pointers */
319 cab->base.next = NULL;
320 cab->base.files = NULL;
321 cab->base.folders = NULL;
322 cab->base.prevcab = cab->base.nextcab = NULL;
323 cab->base.prevname = cab->base.nextname = NULL;
324 cab->base.previnfo = cab->base.nextinfo = NULL;
325
326 cab->base.base_offset = offset;
327
328 /* seek to CFHEADER */
329 if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) {
330 return MSPACK_ERR_SEEK;
331 }
332
333 /* read in the CFHEADER */
334 if (sys->read(fh, &buf[0], cfhead_SIZEOF) != cfhead_SIZEOF) {
335 return MSPACK_ERR_READ;
336 }
337
338 /* check for "MSCF" signature */
339 if (EndGetI32(&buf[cfhead_Signature]) != 0x4643534D) {
340 return MSPACK_ERR_SIGNATURE;
341 }
342
343 /* some basic header fields */
344 cab->base.length = EndGetI32(&buf[cfhead_CabinetSize]);
345 cab->base.set_id = EndGetI16(&buf[cfhead_SetID]);
346 cab->base.set_index = EndGetI16(&buf[cfhead_CabinetIndex]);
347
348 /* get the number of folders */
349 num_folders = EndGetI16(&buf[cfhead_NumFolders]);
350 if (num_folders == 0) {
351 if (!quiet) sys->message(fh, "no folders in cabinet.");
352 return MSPACK_ERR_DATAFORMAT;
353 }
354
355 /* get the number of files */
356 num_files = EndGetI16(&buf[cfhead_NumFiles]);
357 if (num_files == 0) {
358 if (!quiet) sys->message(fh, "no files in cabinet.");
359 return MSPACK_ERR_DATAFORMAT;
360 }
361
362 /* check cabinet version */
363 if ((buf[cfhead_MajorVersion] != 1) && (buf[cfhead_MinorVersion] != 3)) {
364 if (!quiet) sys->message(fh, "WARNING; cabinet version is not 1.3");
365 }
366
367 /* read the reserved-sizes part of header, if present */
368 cab->base.flags = EndGetI16(&buf[cfhead_Flags]);
369
370 if (cab->base.flags & cfheadRESERVE_PRESENT) {
371 if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) {
372 return MSPACK_ERR_READ;
373 }
374 cab->base.header_resv = EndGetI16(&buf[cfheadext_HeaderReserved]);
375 folder_resv = buf[cfheadext_FolderReserved];
376 cab->block_resv = buf[cfheadext_DataReserved];
377
378 if (cab->base.header_resv > 60000) {
379 if (!quiet) sys->message(fh, "WARNING; reserved header > 60000.");
380 }
381
382 /* skip the reserved header */
383 if (cab->base.header_resv) {
384 if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) {
385 return MSPACK_ERR_SEEK;
386 }
387 }
388 }
389 else {
390 cab->base.header_resv = 0;
391 folder_resv = 0;
392 cab->block_resv = 0;
393 }
394
395 /* read name and info of preceeding cabinet in set, if present */
396 if (cab->base.flags & cfheadPREV_CABINET) {
397 cab->base.prevname = cabd_read_string(sys, fh, &err);
398 if (err) return err;
399 cab->base.previnfo = cabd_read_string(sys, fh, &err);
400 if (err) return err;
401 }
402
403 /* read name and info of next cabinet in set, if present */
404 if (cab->base.flags & cfheadNEXT_CABINET) {
405 cab->base.nextname = cabd_read_string(sys, fh, &err);
406 if (err) return err;
407 cab->base.nextinfo = cabd_read_string(sys, fh, &err);
408 if (err) return err;
409 }
410
411 /* read folders */
412 for (i = 0; i < num_folders; i++) {
413 if (sys->read(fh, &buf[0], cffold_SIZEOF) != cffold_SIZEOF) {
414 return MSPACK_ERR_READ;
415 }
416 if (folder_resv) {
417 if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) {
418 return MSPACK_ERR_SEEK;
419 }
420 }
421
422 if (!(fol = (struct mscabd_folder_p *) sys->alloc(sys, sizeof(struct mscabd_folder_p)))) {
423 return MSPACK_ERR_NOMEMORY;
424 }
425 fol->base.next = NULL;
426 fol->base.comp_type = EndGetI16(&buf[cffold_CompType]);
427 fol->base.num_blocks = EndGetI16(&buf[cffold_NumBlocks]);
428 fol->data.next = NULL;
429 fol->data.cab = (struct mscabd_cabinet_p *) cab;
430 fol->data.offset = offset + (off_t)
431 ( (unsigned int) EndGetI32(&buf[cffold_DataOffset]) );
432 fol->merge_prev = NULL;
433 fol->merge_next = NULL;
434
435 /* link folder into list of folders */
436 if (!linkfol) cab->base.folders = (struct mscabd_folder *) fol;
437 else linkfol->base.next = (struct mscabd_folder *) fol;
438 linkfol = fol;
439 }
440
441 /* read files */
442 for (i = 0; i < num_files; i++) {
443 if (sys->read(fh, &buf[0], cffile_SIZEOF) != cffile_SIZEOF) {
444 return MSPACK_ERR_READ;
445 }
446
447 if (!(file = (struct mscabd_file *) sys->alloc(sys, sizeof(struct mscabd_file)))) {
448 return MSPACK_ERR_NOMEMORY;
449 }
450
451 file->next = NULL;
452 file->length = EndGetI32(&buf[cffile_UncompressedSize]);
453 file->attribs = EndGetI16(&buf[cffile_Attribs]);
454 file->offset = EndGetI32(&buf[cffile_FolderOffset]);
455
456 /* set folder pointer */
457 fidx = EndGetI16(&buf[cffile_FolderIndex]);
458 if (fidx < cffileCONTINUED_FROM_PREV) {
459 /* normal folder index; count up to the correct folder */
460 if (fidx < num_folders) {
461 struct mscabd_folder *ifol = cab->base.folders;
462 while (fidx--) if (ifol) ifol = ifol->next;
463 file->folder = ifol;
464 }
465 else {
466 D(("invalid folder index"))
467 file->folder = NULL;
468 }
469 }
470 else {
471 /* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or
472 * CONTINUED_PREV_AND_NEXT */
473 if ((fidx == cffileCONTINUED_TO_NEXT) ||
474 (fidx == cffileCONTINUED_PREV_AND_NEXT))
475 {
476 /* get last folder */
477 struct mscabd_folder *ifol = cab->base.folders;
478 while (ifol->next) ifol = ifol->next;
479 file->folder = ifol;
480
481 /* set "merge next" pointer */
482 fol = (struct mscabd_folder_p *) ifol;
483 if (!fol->merge_next) fol->merge_next = file;
484 }
485
486 if ((fidx == cffileCONTINUED_FROM_PREV) ||
487 (fidx == cffileCONTINUED_PREV_AND_NEXT))
488 {
489 /* get first folder */
490 file->folder = cab->base.folders;
491
492 /* set "merge prev" pointer */
493 fol = (struct mscabd_folder_p *) file->folder;
494 if (!fol->merge_prev) fol->merge_prev = file;
495 }
496 }
497
498 /* get time */
499 x = EndGetI16(&buf[cffile_Time]);
500 file->time_h = x >> 11;
501 file->time_m = (x >> 5) & 0x3F;
502 file->time_s = (x << 1) & 0x3E;
503
504 /* get date */
505 x = EndGetI16(&buf[cffile_Date]);
506 file->date_d = x & 0x1F;
507 file->date_m = (x >> 5) & 0xF;
508 file->date_y = (x >> 9) + 1980;
509
510 /* get filename */
511 file->filename = cabd_read_string(sys, fh, &err);
512
513 /* if folder index or filename are bad, either skip it or fail */
514 if (err || !file->folder) {
515 sys->free(file->filename);
516 sys->free(file);
517 if (salvage) continue;
518 return err ? err : MSPACK_ERR_DATAFORMAT;
519 }
520
521 /* link file entry into file list */
522 if (!linkfile) cab->base.files = file;
523 else linkfile->next = file;
524 linkfile = file;
525 }
526
527 if (cab->base.files == NULL) {
528 /* We never actually added any files to the file list. Something went wrong.
529 * The file header may have been invalid */
530 D(("No files found, even though header claimed to have %d files", num_files))
531 return MSPACK_ERR_DATAFORMAT;
532 }
533
534 return MSPACK_ERR_OK;
535}
536
537static char *cabd_read_string(struct mspack_system *sys,
538 struct mspack_file *fh, int *error)
539{
540 off_t base = sys->tell(fh);
541 char buf[256], *str;
542 int len, i, ok;
543
544 /* read up to 256 bytes */
545 if ((len = sys->read(fh, &buf[0], 256)) <= 0) {
546 *error = MSPACK_ERR_READ;
547 return NULL;
548 }
549
550 /* search for a null terminator in the buffer */
551 for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; }
552 /* reject empty strings */
553 if (i == 0) ok = 0;
554
555 if (!ok) {
556 *error = MSPACK_ERR_DATAFORMAT;
557 return NULL;
558 }
559
560 len = i + 1;
561
562 /* set the data stream to just after the string and return */
563 if (sys->seek(fh, base + (off_t)len, MSPACK_SYS_SEEK_START)) {
564 *error = MSPACK_ERR_SEEK;
565 return NULL;
566 }
567
568 if (!(str = (char *) sys->alloc(sys, len))) {
569 *error = MSPACK_ERR_NOMEMORY;
570 return NULL;
571 }
572
573 sys->copy(&buf[0], str, len);
574 *error = MSPACK_ERR_OK;
575 return str;
576}
577
578/***************************************
579 * CABD_SEARCH, CABD_FIND
580 ***************************************
581 * cabd_search opens a file, finds its extent, allocates a search buffer,
582 * then reads through the whole file looking for possible cabinet headers.
583 * if it finds any, it tries to read them as real cabinets. returns a linked
584 * list of results
585 *
586 * cabd_find is the inner loop of cabd_search, to make it easier to
587 * break out of the loop and be sure that all resources are freed
588 */
589static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base,
590 const char *filename)
591{
592 struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
593 struct mscabd_cabinet_p *cab = NULL;
594 struct mspack_system *sys;
595 unsigned char *search_buf;
596 struct mspack_file *fh;
597 off_t filelen, firstlen = 0;
598
599 if (!base) return NULL;
600 sys = self->system;
601
602 /* allocate a search buffer */
603 search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->searchbuf_size);
604 if (!search_buf) {
605 self->error = MSPACK_ERR_NOMEMORY;
606 return NULL;
607 }
608
609 /* open file and get its full file length */
610 if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) {
611 if (!(self->error = mspack_sys_filelen(sys, fh, &filelen))) {
612 self->error = cabd_find(self, search_buf, fh, filename,
613 filelen, &firstlen, &cab);
614 }
615
616 /* truncated / extraneous data warning: */
617 if (firstlen && (firstlen != filelen) &&
618 (!cab || (cab->base.base_offset == 0)))
619 {
620 if (firstlen < filelen) {
621 sys->message(fh, "WARNING; possible %" LD
622 " extra bytes at end of file.",
623 filelen - firstlen);
624 }
625 else {
626 sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.",
627 firstlen - filelen);
628 }
629 }
630
631 sys->close(fh);
632 }
633 else {
634 self->error = MSPACK_ERR_OPEN;
635 }
636
637 /* free the search buffer */
638 sys->free(search_buf);
639
640 return (struct mscabd_cabinet *) cab;
641}
642
643static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
644 struct mspack_file *fh, const char *filename, off_t flen,
645 off_t *firstlen, struct mscabd_cabinet_p **firstcab)
646{
647 struct mscabd_cabinet_p *cab, *link = NULL;
648 off_t caboff, offset, length;
649 struct mspack_system *sys = self->system;
650 unsigned char *p, *pend, state = 0;
651 unsigned int cablen_u32 = 0, foffset_u32 = 0;
652 int false_cabs = 0;
653
654#if !LARGEFILE_SUPPORT
655 /* detect 32-bit off_t overflow */
656 if (flen < 0) {
657 sys->message(fh, largefile_msg);
658 return MSPACK_ERR_OK;
659 }
660#endif
661
662 /* search through the full file length */
663 for (offset = 0; offset < flen; offset += length) {
664 /* search length is either the full length of the search buffer, or the
665 * amount of data remaining to the end of the file, whichever is less. */
666 length = flen - offset;
667 if (length > self->searchbuf_size) {
668 length = self->searchbuf_size;
669 }
670
671 /* fill the search buffer with data from disk */
672 if (sys->read(fh, &buf[0], (int) length) != (int) length) {
673 return MSPACK_ERR_READ;
674 }
675
676 /* FAQ avoidance strategy */
677 if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) {
678 sys->message(fh, "WARNING; found InstallShield header. Use unshield "
679 "(https://github.com/twogood/unshield) to unpack this file");
680 }
681
682 /* read through the entire buffer. */
683 for (p = &buf[0], pend = &buf[length]; p < pend; ) {
684 switch (state) {
685 /* starting state */
686 case 0:
687 /* we spend most of our time in this while loop, looking for
688 * a leading 'M' of the 'MSCF' signature */
689 while (p < pend && *p != 0x4D) p++;
690 /* if we found tht 'M', advance state */
691 if (p++ < pend) state = 1;
692 break;
693
694 /* verify that the next 3 bytes are 'S', 'C' and 'F' */
695 case 1: state = (*p++ == 0x53) ? 2 : 0; break;
696 case 2: state = (*p++ == 0x43) ? 3 : 0; break;
697 case 3: state = (*p++ == 0x46) ? 4 : 0; break;
698
699 /* we don't care about bytes 4-7 (see default: for action) */
700
701 /* bytes 8-11 are the overall length of the cabinet */
702 case 8: cablen_u32 = *p++; state++; break;
703 case 9: cablen_u32 |= *p++ << 8; state++; break;
704 case 10: cablen_u32 |= *p++ << 16; state++; break;
705 case 11: cablen_u32 |= *p++ << 24; state++; break;
706
707 /* we don't care about bytes 12-15 (see default: for action) */
708
709 /* bytes 16-19 are the offset within the cabinet of the filedata */
710 case 16: foffset_u32 = *p++; state++; break;
711 case 17: foffset_u32 |= *p++ << 8; state++; break;
712 case 18: foffset_u32 |= *p++ << 16; state++; break;
713 case 19: foffset_u32 |= *p++ << 24;
714 /* now we have recieved 20 bytes of potential cab header. work out
715 * the offset in the file of this potential cabinet */
716 caboff = offset + (p - &buf[0]) - 20;
717
718 /* should reading cabinet fail, restart search just after 'MSCF' */
719 offset = caboff + 4;
720
721 /* capture the "length of cabinet" field if there is a cabinet at
722 * offset 0 in the file, regardless of whether the cabinet can be
723 * read correctly or not */
724 if (caboff == 0) *firstlen = (off_t) cablen_u32;
725
726 /* check that the files offset is less than the alleged length of
727 * the cabinet, and that the offset + the alleged length are
728 * 'roughly' within the end of overall file length. In salvage
729 * mode, don't check the alleged length, allow it to be garbage */
730 if ((foffset_u32 < cablen_u32) &&
731 ((caboff + (off_t) foffset_u32) < (flen + 32)) &&
732 (((caboff + (off_t) cablen_u32) < (flen + 32)) || self->salvage))
733 {
734 /* likely cabinet found -- try reading it */
735 if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) {
736 return MSPACK_ERR_NOMEMORY;
737 }
738 cab->base.filename = filename;
739 if (cabd_read_headers(sys, fh, cab, caboff, self->salvage, 1)) {
740 /* destroy the failed cabinet */
741 cabd_close((struct mscab_decompressor *) self,
742 (struct mscabd_cabinet *) cab);
743 false_cabs++;
744 }
745 else {
746 /* cabinet read correctly! */
747
748 /* link the cab into the list */
749 if (!link) *firstcab = cab;
750 else link->base.next = (struct mscabd_cabinet *) cab;
751 link = cab;
752
753 /* cause the search to restart after this cab's data. */
754 offset = caboff + (off_t) cablen_u32;
755
756#if !LARGEFILE_SUPPORT
757 /* detect 32-bit off_t overflow */
758 if (offset < caboff) {
759 sys->message(fh, largefile_msg);
760 return MSPACK_ERR_OK;
761 }
762#endif
763 }
764 }
765
766 /* restart search */
767 if (offset >= flen) return MSPACK_ERR_OK;
768 if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) {
769 return MSPACK_ERR_SEEK;
770 }
771 length = 0;
772 p = pend;
773 state = 0;
774 break;
775
776 /* for bytes 4-7 and 12-15, just advance state/pointer */
777 default:
778 p++, state++;
779 } /* switch(state) */
780 } /* for (... p < pend ...) */
781 } /* for (... offset < length ...) */
782
783 if (false_cabs) {
784 D(("%d false cabinets found", false_cabs))
785 }
786
787 return MSPACK_ERR_OK;
788}
789
790/***************************************
791 * CABD_MERGE, CABD_PREPEND, CABD_APPEND
792 ***************************************
793 * joins cabinets together, also merges split folders between these two
794 * cabinets only. This includes freeing the duplicate folder and file(s)
795 * and allocating a further mscabd_folder_data structure to append to the
796 * merged folder's data parts list.
797 */
798static int cabd_prepend(struct mscab_decompressor *base,
799 struct mscabd_cabinet *cab,
800 struct mscabd_cabinet *prevcab)
801{
802 return cabd_merge(base, prevcab, cab);
803}
804
805static int cabd_append(struct mscab_decompressor *base,
806 struct mscabd_cabinet *cab,
807 struct mscabd_cabinet *nextcab)
808{
809 return cabd_merge(base, cab, nextcab);
810}
811
812static int cabd_merge(struct mscab_decompressor *base,
813 struct mscabd_cabinet *lcab,
814 struct mscabd_cabinet *rcab)
815{
816 struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
817 struct mscabd_folder_data *data, *ndata;
818 struct mscabd_folder_p *lfol, *rfol;
819 struct mscabd_file *fi, *rfi, *lfi;
820 struct mscabd_cabinet *cab;
821 struct mspack_system *sys;
822
823 if (!self) return MSPACK_ERR_ARGS;
824 sys = self->system;
825
826 /* basic args check */
827 if (!lcab || !rcab || (lcab == rcab)) {
828 D(("lcab NULL, rcab NULL or lcab = rcab"))
829 return self->error = MSPACK_ERR_ARGS;
830 }
831
832 /* check there's not already a cabinet attached */
833 if (lcab->nextcab || rcab->prevcab) {
834 D(("cabs already joined"))
835 return self->error = MSPACK_ERR_ARGS;
836 }
837
838 /* do not create circular cabinet chains */
839 for (cab = lcab->prevcab; cab; cab = cab->prevcab) {
840 if (cab == rcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;}
841 }
842 for (cab = rcab->nextcab; cab; cab = cab->nextcab) {
843 if (cab == lcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;}
844 }
845
846 /* warn about odd set IDs or indices */
847 if (lcab->set_id != rcab->set_id) {
848 sys->message(NULL, "WARNING; merged cabinets with differing Set IDs.");
849 }
850
851 if (lcab->set_index > rcab->set_index) {
852 sys->message(NULL, "WARNING; merged cabinets with odd order.");
853 }
854
855 /* merging the last folder in lcab with the first folder in rcab */
856 lfol = (struct mscabd_folder_p *) lcab->folders;
857 rfol = (struct mscabd_folder_p *) rcab->folders;
858 while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next;
859
860 /* do we need to merge folders? */
861 if (!lfol->merge_next && !rfol->merge_prev) {
862 /* no, at least one of the folders is not for merging */
863
864 /* attach cabs */
865 lcab->nextcab = rcab;
866 rcab->prevcab = lcab;
867
868 /* attach folders */
869 lfol->base.next = (struct mscabd_folder *) rfol;
870
871 /* attach files */
872 fi = lcab->files;
873 while (fi->next) fi = fi->next;
874 fi->next = rcab->files;
875 }
876 else {
877 /* folder merge required - do the files match? */
878 if (! cabd_can_merge_folders(sys, lfol, rfol)) {
879 return self->error = MSPACK_ERR_DATAFORMAT;
880 }
881
882 /* allocate a new folder data structure */
883 if (!(data = (struct mscabd_folder_data *) sys->alloc(sys, sizeof(struct mscabd_folder_data)))) {
884 return self->error = MSPACK_ERR_NOMEMORY;
885 }
886
887 /* attach cabs */
888 lcab->nextcab = rcab;
889 rcab->prevcab = lcab;
890
891 /* append rfol's data to lfol */
892 ndata = &lfol->data;
893 while (ndata->next) ndata = ndata->next;
894 ndata->next = data;
895 *data = rfol->data;
896 rfol->data.next = NULL;
897
898 /* lfol becomes rfol.
899 * NOTE: special case, don't merge if rfol is merge prev and next,
900 * rfol->merge_next is going to be deleted, so keep lfol's version
901 * instead */
902 lfol->base.num_blocks += rfol->base.num_blocks - 1;
903 if ((rfol->merge_next == NULL) ||
904 (rfol->merge_next->folder != (struct mscabd_folder *) rfol))
905 {
906 lfol->merge_next = rfol->merge_next;
907 }
908
909 /* attach the rfol's folder (except the merge folder) */
910 while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next;
911 lfol->base.next = rfol->base.next;
912
913 /* free disused merge folder */
914 sys->free(rfol);
915
916 /* attach rfol's files */
917 fi = lcab->files;
918 while (fi->next) fi = fi->next;
919 fi->next = rcab->files;
920
921 /* delete all files from rfol's merge folder */
922 lfi = NULL;
923 for (fi = lcab->files; fi ; fi = rfi) {
924 rfi = fi->next;
925 /* if file's folder matches the merge folder, unlink and free it */
926 if (fi->folder == (struct mscabd_folder *) rfol) {
927 if (lfi) lfi->next = rfi; else lcab->files = rfi;
928 sys->free(fi->filename);
929 sys->free(fi);
930 }
931 else lfi = fi;
932 }
933 }
934
935 /* all done! fix files and folders pointers in all cabs so they all
936 * point to the same list */
937 for (cab = lcab->prevcab; cab; cab = cab->prevcab) {
938 cab->files = lcab->files;
939 cab->folders = lcab->folders;
940 }
941
942 for (cab = lcab->nextcab; cab; cab = cab->nextcab) {
943 cab->files = lcab->files;
944 cab->folders = lcab->folders;
945 }
946
947 return self->error = MSPACK_ERR_OK;
948}
949
950/* decides if two folders are OK to merge */
951static int cabd_can_merge_folders(struct mspack_system *sys,
952 struct mscabd_folder_p *lfol,
953 struct mscabd_folder_p *rfol)
954{
955 struct mscabd_file *lfi, *rfi, *l, *r;
956 int matching = 1;
957
958 /* check that both folders use the same compression method/settings */
959 if (lfol->base.comp_type != rfol->base.comp_type) {
960 D(("folder merge: compression type mismatch"))
961 return 0;
962 }
963
964 /* check there are not too many data blocks after merging */
965 if ((lfol->base.num_blocks + rfol->base.num_blocks) > CAB_FOLDERMAX) {
966 D(("folder merge: too many data blocks in merged folders"))
967 return 0;
968 }
969
970 if (!(lfi = lfol->merge_next) || !(rfi = rfol->merge_prev)) {
971 D(("folder merge: one cabinet has no files to merge"))
972 return 0;
973 }
974
975 /* for all files in lfol (which is the last folder in whichever cab and
976 * only has files to merge), compare them to the files from rfol. They
977 * should be identical in number and order. to verify this, check the
978 * offset and length of each file. */
979 for (l=lfi, r=rfi; l; l=l->next, r=r->next) {
980 if (!r || (l->offset != r->offset) || (l->length != r->length)) {
981 matching = 0;
982 break;
983 }
984 }
985
986 if (matching) return 1;
987
988 /* if rfol does not begin with an identical copy of the files in lfol, make
989 * make a judgement call; if at least ONE file from lfol is in rfol, allow
990 * the merge with a warning about missing files. */
991 matching = 0;
992 for (l = lfi; l; l = l->next) {
993 for (r = rfi; r; r = r->next) {
994 if (l->offset == r->offset && l->length == r->length) break;
995 }
996 if (r) matching = 1; else sys->message(NULL,
997 "WARNING; merged file %s not listed in both cabinets", l->filename);
998 }
999 return matching;
1000}
1001
1002
1003/***************************************
1004 * CABD_EXTRACT
1005 ***************************************
1006 * extracts a file from a cabinet
1007 */
1008static int cabd_extract(struct mscab_decompressor *base,
1009 struct mscabd_file *file, const char *filename)
1010{
1011 struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
1012 struct mscabd_folder_p *fol;
1013 struct mspack_system *sys;
1014 struct mspack_file *fh;
1015 off_t filelen;
1016
1017 if (!self) return MSPACK_ERR_ARGS;
1018 if (!file) return self->error = MSPACK_ERR_ARGS;
1019
1020 sys = self->system;
1021 fol = (struct mscabd_folder_p *) file->folder;
1022
1023 /* if offset is beyond 2GB, nothing can be extracted */
1024 if (file->offset > CAB_LENGTHMAX) {
1025 return self->error = MSPACK_ERR_DATAFORMAT;
1026 }
1027
1028 /* if file claims to go beyond 2GB either error out,
1029 * or in salvage mode reduce file length so it fits 2GB limit
1030 */
1031 filelen = file->length;
1032 if (filelen > CAB_LENGTHMAX || (file->offset + filelen) > CAB_LENGTHMAX) {
1033 if (self->salvage) {
1034 filelen = CAB_LENGTHMAX - file->offset;
1035 }
1036 else {
1037 return self->error = MSPACK_ERR_DATAFORMAT;
1038 }
1039 }
1040
1041 /* extraction impossible if no folder, or folder needs predecessor */
1042 if (!fol || fol->merge_prev) {
1043 sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, "
1044 "cabinet set is incomplete", file->filename);
1045 return self->error = MSPACK_ERR_DECRUNCH;
1046 }
1047
1048 /* if file goes beyond what can be decoded, given an error.
1049 * In salvage mode, don't assume block sizes, just try decoding
1050 */
1051 if (!self->salvage) {
1052 off_t maxlen = fol->base.num_blocks * CAB_BLOCKMAX;
1053 if ((file->offset + filelen) > maxlen) {
1054 sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, "
1055 "cabinet set is incomplete", file->filename);
1056 return self->error = MSPACK_ERR_DECRUNCH;
1057 }
1058 }
1059
1060 /* allocate generic decompression state */
1061 if (!self->d) {
1062 self->d = (struct mscabd_decompress_state *) sys->alloc(sys, sizeof(struct mscabd_decompress_state));
1063 if (!self->d) return self->error = MSPACK_ERR_NOMEMORY;
1064 self->d->folder = NULL;
1065 self->d->data = NULL;
1066 self->d->sys = *sys;
1067 self->d->sys.read = &cabd_sys_read;
1068 self->d->sys.write = &cabd_sys_write;
1069 self->d->state = NULL;
1070 self->d->infh = NULL;
1071 self->d->incab = NULL;
1072 }
1073
1074 /* do we need to change folder or reset the current folder? */
1075 if ((self->d->folder != fol) || (self->d->offset > file->offset) ||
1076 !self->d->state)
1077 {
1078 /* free any existing decompressor */
1079 cabd_free_decomp(self);
1080
1081 /* do we need to open a new cab file? */
1082 if (!self->d->infh || (fol->data.cab != self->d->incab)) {
1083 /* close previous file handle if from a different cab */
1084 if (self->d->infh) sys->close(self->d->infh);
1085 self->d->incab = fol->data.cab;
1086 self->d->infh = sys->open(sys, fol->data.cab->base.filename,
1087 MSPACK_SYS_OPEN_READ);
1088 if (!self->d->infh) return self->error = MSPACK_ERR_OPEN;
1089 }
1090 /* seek to start of data blocks */
1091 if (sys->seek(self->d->infh, fol->data.offset, MSPACK_SYS_SEEK_START)) {
1092 return self->error = MSPACK_ERR_SEEK;
1093 }
1094
1095 /* set up decompressor */
1096 if (cabd_init_decomp(self, (unsigned int) fol->base.comp_type)) {
1097 return self->error;
1098 }
1099
1100 /* initialise new folder state */
1101 self->d->folder = fol;
1102 self->d->data = &fol->data;
1103 self->d->offset = 0;
1104 self->d->block = 0;
1105 self->d->outlen = 0;
1106 self->d->i_ptr = self->d->i_end = &self->d->input[0];
1107
1108 /* read_error lasts for the lifetime of a decompressor */
1109 self->read_error = MSPACK_ERR_OK;
1110 }
1111
1112 /* open file for output */
1113 if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) {
1114 return self->error = MSPACK_ERR_OPEN;
1115 }
1116
1117 self->error = MSPACK_ERR_OK;
1118
1119 /* if file has more than 0 bytes */
1120 if (filelen) {
1121 off_t bytes;
1122 int error;
1123 /* get to correct offset.
1124 * - use NULL fh to say 'no writing' to cabd_sys_write()
1125 * - if cabd_sys_read() has an error, it will set self->read_error
1126 * and pass back MSPACK_ERR_READ
1127 */
1128 self->d->outfh = NULL;
1129 if ((bytes = file->offset - self->d->offset)) {
1130 error = self->d->decompress(self->d->state, bytes);
1131 self->error = (error == MSPACK_ERR_READ) ? self->read_error : error;
1132 }
1133
1134 /* if getting to the correct offset was error free, unpack file */
1135 if (!self->error) {
1136 self->d->outfh = fh;
1137 error = self->d->decompress(self->d->state, filelen);
1138 self->error = (error == MSPACK_ERR_READ) ? self->read_error : error;
1139 }
1140 }
1141
1142 /* close output file */
1143 sys->close(fh);
1144 self->d->outfh = NULL;
1145
1146 return self->error;
1147}
1148
1149/***************************************
1150 * CABD_INIT_DECOMP, CABD_FREE_DECOMP
1151 ***************************************
1152 * cabd_init_decomp initialises decompression state, according to which
1153 * decompression method was used. relies on self->d->folder being the same
1154 * as when initialised.
1155 *
1156 * cabd_free_decomp frees decompression state, according to which method
1157 * was used.
1158 */
1159static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct)
1160{
1161 struct mspack_file *fh = (struct mspack_file *) self;
1162
1163 self->d->comp_type = ct;
1164
1165 switch (ct & cffoldCOMPTYPE_MASK) {
1166 case cffoldCOMPTYPE_NONE:
1167 self->d->decompress = (int (*)(void *, off_t)) &noned_decompress;
1168 self->d->state = noned_init(&self->d->sys, fh, fh, self->buf_size);
1169 break;
1170 case cffoldCOMPTYPE_MSZIP:
1171 self->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress;
1172 self->d->state = mszipd_init(&self->d->sys, fh, fh, self->buf_size,
1173 self->fix_mszip);
1174 break;
1175 case cffoldCOMPTYPE_QUANTUM:
1176 self->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress;
1177 self->d->state = qtmd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f,
1178 self->buf_size);
1179 break;
1180 case cffoldCOMPTYPE_LZX:
1181 self->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress;
1182 self->d->state = lzxd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, 0,
1183 self->buf_size, (off_t)0,0);
1184 break;
1185 default:
1186 return self->error = MSPACK_ERR_DATAFORMAT;
1187 }
1188 return self->error = (self->d->state) ? MSPACK_ERR_OK : MSPACK_ERR_NOMEMORY;
1189}
1190
1191static void cabd_free_decomp(struct mscab_decompressor_p *self) {
1192 if (!self || !self->d || !self->d->state) return;
1193
1194 switch (self->d->comp_type & cffoldCOMPTYPE_MASK) {
1195 case cffoldCOMPTYPE_NONE: noned_free((struct noned_state *) self->d->state); break;
1196 case cffoldCOMPTYPE_MSZIP: mszipd_free((struct mszipd_stream *) self->d->state); break;
1197 case cffoldCOMPTYPE_QUANTUM: qtmd_free((struct qtmd_stream *) self->d->state); break;
1198 case cffoldCOMPTYPE_LZX: lzxd_free((struct lzxd_stream *) self->d->state); break;
1199 }
1200 self->d->decompress = NULL;
1201 self->d->state = NULL;
1202}
1203
1204/***************************************
1205 * CABD_SYS_READ, CABD_SYS_WRITE
1206 ***************************************
1207 * cabd_sys_read is the internal reader function which the decompressors
1208 * use. will read data blocks (and merge split blocks) from the cabinet
1209 * and serve the read bytes to the decompressors
1210 *
1211 * cabd_sys_write is the internal writer function which the decompressors
1212 * use. it either writes data to disk (self->d->outfh) with the real
1213 * sys->write() function, or does nothing with the data when
1214 * self->d->outfh == NULL. advances self->d->offset
1215 */
1216static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) {
1217 struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file;
1218 unsigned char *buf = (unsigned char *) buffer;
1219 struct mspack_system *sys = self->system;
1220 int avail, todo, outlen, ignore_cksum, ignore_blocksize;
1221
1222 ignore_cksum = self->salvage ||
1223 (self->fix_mszip &&
1224 ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP));
1225 ignore_blocksize = self->salvage;
1226
1227 todo = bytes;
1228 while (todo > 0) {
1229 avail = self->d->i_end - self->d->i_ptr;
1230
1231 /* if out of input data, read a new block */
1232 if (avail) {
1233 /* copy as many input bytes available as possible */
1234 if (avail > todo) avail = todo;
1235 sys->copy(self->d->i_ptr, buf, (size_t) avail);
1236 self->d->i_ptr += avail;
1237 buf += avail;
1238 todo -= avail;
1239 }
1240 else {
1241 /* out of data, read a new block */
1242
1243 /* check if we're out of input blocks, advance block counter */
1244 if (self->d->block++ >= self->d->folder->base.num_blocks) {
1245 if (!self->salvage) {
1246 self->read_error = MSPACK_ERR_DATAFORMAT;
1247 }
1248 else {
1249 D(("Ran out of CAB input blocks prematurely"))
1250 }
1251 break;
1252 }
1253
1254 /* read a block */
1255 self->read_error = cabd_sys_read_block(sys, self->d, &outlen,
1256 ignore_cksum, ignore_blocksize);
1257 if (self->read_error) return -1;
1258 self->d->outlen += outlen;
1259
1260 /* special Quantum hack -- trailer byte to allow the decompressor
1261 * to realign itself. CAB Quantum blocks, unlike LZX blocks, can have
1262 * anything from 0 to 4 trailing null bytes. */
1263 if ((self->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) {
1264 *self->d->i_end++ = 0xFF;
1265 }
1266
1267 /* is this the last block? */
1268 if (self->d->block >= self->d->folder->base.num_blocks) {
1269 if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) {
1270 /* special LZX hack -- on the last block, inform LZX of the
1271 * size of the output data stream. */
1272 lzxd_set_output_length((struct lzxd_stream *) self->d->state, self->d->outlen);
1273 }
1274 }
1275 } /* if (avail) */
1276 } /* while (todo > 0) */
1277 return bytes - todo;
1278}
1279
1280static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) {
1281 struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file;
1282 self->d->offset += bytes;
1283 if (self->d->outfh) {
1284 return self->system->write(self->d->outfh, buffer, bytes);
1285 }
1286 return bytes;
1287}
1288
1289/***************************************
1290 * CABD_SYS_READ_BLOCK
1291 ***************************************
1292 * reads a whole data block from a cab file. the block may span more than
1293 * one cab file, if it does then the fragments will be reassembled
1294 */
1295static int cabd_sys_read_block(struct mspack_system *sys,
1296 struct mscabd_decompress_state *d,
1297 int *out, int ignore_cksum,
1298 int ignore_blocksize)
1299{
1300 unsigned char hdr[cfdata_SIZEOF];
1301 unsigned int cksum;
1302 int len, full_len;
1303
1304 /* reset the input block pointer and end of block pointer */
1305 d->i_ptr = d->i_end = &d->input[0];
1306
1307 do {
1308 /* read the block header */
1309 if (sys->read(d->infh, &hdr[0], cfdata_SIZEOF) != cfdata_SIZEOF) {
1310 return MSPACK_ERR_READ;
1311 }
1312
1313 /* skip any reserved block headers */
1314 if (d->data->cab->block_resv &&
1315 sys->seek(d->infh, (off_t) d->data->cab->block_resv,
1316 MSPACK_SYS_SEEK_CUR))
1317 {
1318 return MSPACK_ERR_SEEK;
1319 }
1320
1321 /* blocks must not be over CAB_INPUTMAX in size */
1322 len = EndGetI16(&hdr[cfdata_CompressedSize]);
1323 full_len = (d->i_end - d->i_ptr) + len; /* include cab-spanning blocks */
1324 if (full_len > CAB_INPUTMAX) {
1325 D(("block size %d > CAB_INPUTMAX", full_len));
1326 /* in salvage mode, blocks can be 65535 bytes but no more than that */
1327 if (!ignore_blocksize || full_len > CAB_INPUTMAX_SALVAGE) {
1328 return MSPACK_ERR_DATAFORMAT;
1329 }
1330 }
1331
1332 /* blocks must not expand to more than CAB_BLOCKMAX */
1333 if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) {
1334 D(("block size > CAB_BLOCKMAX"))
1335 if (!ignore_blocksize) return MSPACK_ERR_DATAFORMAT;
1336 }
1337
1338 /* read the block data */
1339 if (sys->read(d->infh, d->i_end, len) != len) {
1340 return MSPACK_ERR_READ;
1341 }
1342
1343 /* perform checksum test on the block (if one is stored) */
1344 if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) {
1345 unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0);
1346 if (cabd_checksum(&hdr[4], 4, sum2) != cksum) {
1347 if (!ignore_cksum) return MSPACK_ERR_CHECKSUM;
1348 sys->message(d->infh, "WARNING; bad block checksum found");
1349 }
1350 }
1351
1352 /* advance end of block pointer to include newly read data */
1353 d->i_end += len;
1354
1355 /* uncompressed size == 0 means this block was part of a split block
1356 * and it continues as the first block of the next cabinet in the set.
1357 * otherwise, this is the last part of the block, and no more block
1358 * reading needs to be done.
1359 */
1360 /* EXIT POINT OF LOOP -- uncompressed size != 0 */
1361 if ((*out = EndGetI16(&hdr[cfdata_UncompressedSize]))) {
1362 return MSPACK_ERR_OK;
1363 }
1364
1365 /* otherwise, advance to next cabinet */
1366
1367 /* close current file handle */
1368 sys->close(d->infh);
1369 d->infh = NULL;
1370
1371 /* advance to next member in the cabinet set */
1372 if (!(d->data = d->data->next)) {
1373 sys->message(d->infh, "WARNING; ran out of cabinets in set. Are any missing?");
1374 return MSPACK_ERR_DATAFORMAT;
1375 }
1376
1377 /* open next cab file */
1378 d->incab = d->data->cab;
1379 if (!(d->infh = sys->open(sys, d->incab->base.filename,
1380 MSPACK_SYS_OPEN_READ)))
1381 {
1382 return MSPACK_ERR_OPEN;
1383 }
1384
1385 /* seek to start of data blocks */
1386 if (sys->seek(d->infh, d->data->offset, MSPACK_SYS_SEEK_START)) {
1387 return MSPACK_ERR_SEEK;
1388 }
1389 } while (1);
1390
1391 /* not reached */
1392 return MSPACK_ERR_OK;
1393}
1394
1395static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes,
1396 unsigned int cksum)
1397{
1398 unsigned int len, ul = 0;
1399
1400 for (len = bytes >> 2; len--; data += 4) {
1401 cksum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24));
1402 }
1403
1404 switch (bytes & 3) {
1405 case 3: ul |= *data++ << 16; /*@fallthrough@*/
1406 case 2: ul |= *data++ << 8; /*@fallthrough@*/
1407 case 1: ul |= *data;
1408 }
1409 cksum ^= ul;
1410
1411 return cksum;
1412}
1413
1414/***************************************
1415 * NONED_INIT, NONED_DECOMPRESS, NONED_FREE
1416 ***************************************
1417 * the "not compressed" method decompressor
1418 */
1419struct noned_state {
1420 struct mspack_system *sys;
1421 struct mspack_file *i;
1422 struct mspack_file *o;
1423 unsigned char *buf;
1424 int bufsize;
1425};
1426
1427static struct noned_state *noned_init(struct mspack_system *sys,
1428 struct mspack_file *in,
1429 struct mspack_file *out,
1430 int bufsize)
1431{
1432 struct noned_state *state = (struct noned_state *) sys->alloc(sys, sizeof(struct noned_state));
1433 unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) bufsize);
1434 if (state && buf) {
1435 state->sys = sys;
1436 state->i = in;
1437 state->o = out;
1438 state->buf = buf;
1439 state->bufsize = bufsize;
1440 }
1441 else {
1442 sys->free(buf);
1443 sys->free(state);
1444 state = NULL;
1445 }
1446 return state;
1447}
1448
1449static int noned_decompress(struct noned_state *s, off_t bytes) {
1450 int run;
1451 while (bytes > 0) {
1452 run = (bytes > s->bufsize) ? s->bufsize : (int) bytes;
1453 if (s->sys->read(s->i, &s->buf[0], run) != run) return MSPACK_ERR_READ;
1454 if (s->sys->write(s->o, &s->buf[0], run) != run) return MSPACK_ERR_WRITE;
1455 bytes -= run;
1456 }
1457 return MSPACK_ERR_OK;
1458}
1459
1460static void noned_free(struct noned_state *state) {
1461 struct mspack_system *sys;
1462 if (state) {
1463 sys = state->sys;
1464 sys->free(state->buf);
1465 sys->free(state);
1466 }
1467}
1468
1469
1470/***************************************
1471 * CABD_PARAM
1472 ***************************************
1473 * allows a parameter to be set
1474 */
1475static int cabd_param(struct mscab_decompressor *base, int param, int value) {
1476 struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
1477 if (!self) return MSPACK_ERR_ARGS;
1478
1479 switch (param) {
1480 case MSCABD_PARAM_SEARCHBUF:
1481 if (value < 4) return MSPACK_ERR_ARGS;
1482 self->searchbuf_size = value;
1483 break;
1484 case MSCABD_PARAM_FIXMSZIP:
1485 self->fix_mszip = value;
1486 break;
1487 case MSCABD_PARAM_DECOMPBUF:
1488 if (value < 4) return MSPACK_ERR_ARGS;
1489 self->buf_size = value;
1490 break;
1491 case MSCABD_PARAM_SALVAGE:
1492 self->salvage = value;
1493 break;
1494 default:
1495 return MSPACK_ERR_ARGS;
1496 }
1497 return MSPACK_ERR_OK;
1498}
1499
1500/***************************************
1501 * CABD_ERROR
1502 ***************************************
1503 * returns the last error that occurred
1504 */
1505static int cabd_error(struct mscab_decompressor *base) {
1506 struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
1507 return (self) ? self->error : MSPACK_ERR_ARGS;
1508}
diff --git a/utils/rbutilqt/mspack/chm.h b/utils/rbutilqt/mspack/chm.h
new file mode 100644
index 0000000000..4b19f1505a
--- /dev/null
+++ b/utils/rbutilqt/mspack/chm.h
@@ -0,0 +1,122 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifndef MSPACK_CHM_H
11#define MSPACK_CHM_H 1
12
13#include "lzx.h"
14
15/* generic CHM definitions */
16
17#define chmhead_Signature (0x0000)
18#define chmhead_Version (0x0004)
19#define chmhead_HeaderLen (0x0008)
20#define chmhead_Unknown1 (0x000C)
21#define chmhead_Timestamp (0x0010)
22#define chmhead_LanguageID (0x0014)
23#define chmhead_GUID1 (0x0018)
24#define chmhead_GUID2 (0x0028)
25#define chmhead_SIZEOF (0x0038)
26
27#define chmhst_OffsetHS0 (0x0000)
28#define chmhst_LengthHS0 (0x0008)
29#define chmhst_OffsetHS1 (0x0010)
30#define chmhst_LengthHS1 (0x0018)
31#define chmhst_SIZEOF (0x0020)
32#define chmhst3_OffsetCS0 (0x0020)
33#define chmhst3_SIZEOF (0x0028)
34
35#define chmhs0_Unknown1 (0x0000)
36#define chmhs0_Unknown2 (0x0004)
37#define chmhs0_FileLen (0x0008)
38#define chmhs0_Unknown3 (0x0010)
39#define chmhs0_Unknown4 (0x0014)
40#define chmhs0_SIZEOF (0x0018)
41
42#define chmhs1_Signature (0x0000)
43#define chmhs1_Version (0x0004)
44#define chmhs1_HeaderLen (0x0008)
45#define chmhs1_Unknown1 (0x000C)
46#define chmhs1_ChunkSize (0x0010)
47#define chmhs1_Density (0x0014)
48#define chmhs1_Depth (0x0018)
49#define chmhs1_IndexRoot (0x001C)
50#define chmhs1_FirstPMGL (0x0020)
51#define chmhs1_LastPMGL (0x0024)
52#define chmhs1_Unknown2 (0x0028)
53#define chmhs1_NumChunks (0x002C)
54#define chmhs1_LanguageID (0x0030)
55#define chmhs1_GUID (0x0034)
56#define chmhs1_Unknown3 (0x0044)
57#define chmhs1_Unknown4 (0x0048)
58#define chmhs1_Unknown5 (0x004C)
59#define chmhs1_Unknown6 (0x0050)
60#define chmhs1_SIZEOF (0x0054)
61
62#define pmgl_Signature (0x0000)
63#define pmgl_QuickRefSize (0x0004)
64#define pmgl_Unknown1 (0x0008)
65#define pmgl_PrevChunk (0x000C)
66#define pmgl_NextChunk (0x0010)
67#define pmgl_Entries (0x0014)
68#define pmgl_headerSIZEOF (0x0014)
69
70#define pmgi_Signature (0x0000)
71#define pmgi_QuickRefSize (0x0004)
72#define pmgi_Entries (0x0008)
73#define pmgi_headerSIZEOF (0x000C)
74
75#define lzxcd_Length (0x0000)
76#define lzxcd_Signature (0x0004)
77#define lzxcd_Version (0x0008)
78#define lzxcd_ResetInterval (0x000C)
79#define lzxcd_WindowSize (0x0010)
80#define lzxcd_CacheSize (0x0014)
81#define lzxcd_Unknown1 (0x0018)
82#define lzxcd_SIZEOF (0x001C)
83
84#define lzxrt_Unknown1 (0x0000)
85#define lzxrt_NumEntries (0x0004)
86#define lzxrt_EntrySize (0x0008)
87#define lzxrt_TableOffset (0x000C)
88#define lzxrt_UncompLen (0x0010)
89#define lzxrt_CompLen (0x0018)
90#define lzxrt_FrameLen (0x0020)
91#define lzxrt_Entries (0x0028)
92#define lzxrt_headerSIZEOF (0x0028)
93
94/* CHM compression definitions */
95
96struct mschm_compressor_p {
97 struct mschm_compressor base;
98 struct mspack_system *system;
99 char *temp_file;
100 int use_temp_file;
101 int error;
102};
103
104/* CHM decompression definitions */
105struct mschmd_decompress_state {
106 struct mschmd_header *chm; /* CHM file being decompressed */
107 off_t offset; /* uncompressed offset within folder */
108 off_t inoffset; /* offset in input file */
109 struct lzxd_stream *state; /* LZX decompressor state */
110 struct mspack_system sys; /* special I/O code for decompressor */
111 struct mspack_file *infh; /* input file handle */
112 struct mspack_file *outfh; /* output file handle */
113};
114
115struct mschm_decompressor_p {
116 struct mschm_decompressor base;
117 struct mspack_system *system;
118 struct mschmd_decompress_state *d;
119 int error;
120};
121
122#endif
diff --git a/utils/rbutilqt/mspack/chmc.c b/utils/rbutilqt/mspack/chmc.c
new file mode 100644
index 0000000000..b9a1d8856c
--- /dev/null
+++ b/utils/rbutilqt/mspack/chmc.c
@@ -0,0 +1,24 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10/* CHM compression implementation */
11
12#include "system-mspack.h"
13#include "chm.h"
14
15struct mschm_compressor *
16 mspack_create_chm_compressor(struct mspack_system *sys)
17{
18 /* todo */
19 return NULL;
20}
21
22void mspack_destroy_chm_compressor(struct mschm_compressor *self) {
23 /* todo */
24}
diff --git a/utils/rbutilqt/mspack/chmd.c b/utils/rbutilqt/mspack/chmd.c
new file mode 100644
index 0000000000..6c8481db14
--- /dev/null
+++ b/utils/rbutilqt/mspack/chmd.c
@@ -0,0 +1,1377 @@
1/* This file is part of libmspack.
2 * (C) 2003-2018 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10/* CHM decompression implementation */
11
12#include "system-mspack.h"
13#include "chm.h"
14
15/* prototypes */
16static struct mschmd_header * chmd_open(
17 struct mschm_decompressor *base, const char *filename);
18static struct mschmd_header * chmd_fast_open(
19 struct mschm_decompressor *base, const char *filename);
20static struct mschmd_header *chmd_real_open(
21 struct mschm_decompressor *base, const char *filename, int entire);
22static void chmd_close(
23 struct mschm_decompressor *base, struct mschmd_header *chm);
24static int chmd_read_headers(
25 struct mspack_system *sys, struct mspack_file *fh,
26 struct mschmd_header *chm, int entire);
27static int chmd_fast_find(
28 struct mschm_decompressor *base, struct mschmd_header *chm,
29 const char *filename, struct mschmd_file *f_ptr, int f_size);
30static unsigned char *read_chunk(
31 struct mschm_decompressor_p *self, struct mschmd_header *chm,
32 struct mspack_file *fh, unsigned int chunk);
33static int search_chunk(
34 struct mschmd_header *chm, const unsigned char *chunk, const char *filename,
35 const unsigned char **result, const unsigned char **result_end);
36static inline int compare(
37 const char *s1, const char *s2, int l1, int l2);
38static int chmd_extract(
39 struct mschm_decompressor *base, struct mschmd_file *file,
40 const char *filename);
41static int chmd_sys_write(
42 struct mspack_file *file, void *buffer, int bytes);
43static int chmd_init_decomp(
44 struct mschm_decompressor_p *self, struct mschmd_file *file);
45static int read_reset_table(
46 struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec,
47 unsigned int entry, off_t *length_ptr, off_t *offset_ptr);
48static int read_spaninfo(
49 struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec,
50 off_t *length_ptr);
51static int find_sys_file(
52 struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec,
53 struct mschmd_file **f_ptr, const char *name);
54static unsigned char *read_sys_file(
55 struct mschm_decompressor_p *self, struct mschmd_file *file);
56static int chmd_error(
57 struct mschm_decompressor *base);
58static int read_off64(
59 off_t *var, unsigned char *mem, struct mspack_system *sys,
60 struct mspack_file *fh);
61
62/* filenames of the system files used for decompression.
63 * Content and ControlData are essential.
64 * ResetTable is preferred, but SpanInfo can be used if not available
65 */
66static const char *content_name = "::DataSpace/Storage/MSCompressed/Content";
67static const char *control_name = "::DataSpace/Storage/MSCompressed/ControlData";
68static const char *spaninfo_name = "::DataSpace/Storage/MSCompressed/SpanInfo";
69static const char *rtable_name = "::DataSpace/Storage/MSCompressed/Transform/"
70 "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable";
71
72/***************************************
73 * MSPACK_CREATE_CHM_DECOMPRESSOR
74 ***************************************
75 * constructor
76 */
77struct mschm_decompressor *
78 mspack_create_chm_decompressor(struct mspack_system *sys)
79{
80 struct mschm_decompressor_p *self = NULL;
81
82 if (!sys) sys = mspack_default_system;
83 if (!mspack_valid_system(sys)) return NULL;
84
85 if ((self = (struct mschm_decompressor_p *) sys->alloc(sys, sizeof(struct mschm_decompressor_p)))) {
86 self->base.open = &chmd_open;
87 self->base.close = &chmd_close;
88 self->base.extract = &chmd_extract;
89 self->base.last_error = &chmd_error;
90 self->base.fast_open = &chmd_fast_open;
91 self->base.fast_find = &chmd_fast_find;
92 self->system = sys;
93 self->error = MSPACK_ERR_OK;
94 self->d = NULL;
95 }
96 return (struct mschm_decompressor *) self;
97}
98
99/***************************************
100 * MSPACK_DESTROY_CAB_DECOMPRESSOR
101 ***************************************
102 * destructor
103 */
104void mspack_destroy_chm_decompressor(struct mschm_decompressor *base) {
105 struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base;
106 if (self) {
107 struct mspack_system *sys = self->system;
108 if (self->d) {
109 if (self->d->infh) sys->close(self->d->infh);
110 if (self->d->state) lzxd_free(self->d->state);
111 sys->free(self->d);
112 }
113 sys->free(self);
114 }
115}
116
117/***************************************
118 * CHMD_OPEN
119 ***************************************
120 * opens a file and tries to read it as a CHM file.
121 * Calls chmd_real_open() with entire=1.
122 */
123static struct mschmd_header *chmd_open(struct mschm_decompressor *base,
124 const char *filename)
125{
126 return chmd_real_open(base, filename, 1);
127}
128
129/***************************************
130 * CHMD_FAST_OPEN
131 ***************************************
132 * opens a file and tries to read it as a CHM file, but does not read
133 * the file headers. Calls chmd_real_open() with entire=0
134 */
135static struct mschmd_header *chmd_fast_open(struct mschm_decompressor *base,
136 const char *filename)
137{
138 return chmd_real_open(base, filename, 0);
139}
140
141/***************************************
142 * CHMD_REAL_OPEN
143 ***************************************
144 * the real implementation of chmd_open() and chmd_fast_open(). It simply
145 * passes the "entire" parameter to chmd_read_headers(), which will then
146 * either read all headers, or a bare mininum.
147 */
148static struct mschmd_header *chmd_real_open(struct mschm_decompressor *base,
149 const char *filename, int entire)
150{
151 struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base;
152 struct mschmd_header *chm = NULL;
153 struct mspack_system *sys;
154 struct mspack_file *fh;
155 int error;
156
157 if (!base) return NULL;
158 sys = self->system;
159
160 if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) {
161 if ((chm = (struct mschmd_header *) sys->alloc(sys, sizeof(struct mschmd_header)))) {
162 chm->filename = filename;
163 error = chmd_read_headers(sys, fh, chm, entire);
164 if (error) {
165 /* if the error is DATAFORMAT, and there are some results, return
166 * partial results with a warning, rather than nothing */
167 if (error == MSPACK_ERR_DATAFORMAT && (chm->files || chm->sysfiles)) {
168 sys->message(fh, "WARNING; contents are corrupt");
169 error = MSPACK_ERR_OK;
170 }
171 else {
172 chmd_close(base, chm);
173 chm = NULL;
174 }
175 }
176 self->error = error;
177 }
178 else {
179 self->error = MSPACK_ERR_NOMEMORY;
180 }
181 sys->close(fh);
182 }
183 else {
184 self->error = MSPACK_ERR_OPEN;
185 }
186 return chm;
187}
188
189/***************************************
190 * CHMD_CLOSE
191 ***************************************
192 * frees all memory associated with a given mschmd_header
193 */
194static void chmd_close(struct mschm_decompressor *base,
195 struct mschmd_header *chm)
196{
197 struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base;
198 struct mschmd_file *fi, *nfi;
199 struct mspack_system *sys;
200 unsigned int i;
201
202 if (!base) return;
203 sys = self->system;
204
205 self->error = MSPACK_ERR_OK;
206
207 /* free files */
208 for (fi = chm->files; fi; fi = nfi) {
209 nfi = fi->next;
210 sys->free(fi);
211 }
212 for (fi = chm->sysfiles; fi; fi = nfi) {
213 nfi = fi->next;
214 sys->free(fi);
215 }
216
217 /* if this CHM was being decompressed, free decompression state */
218 if (self->d && (self->d->chm == chm)) {
219 if (self->d->infh) sys->close(self->d->infh);
220 if (self->d->state) lzxd_free(self->d->state);
221 sys->free(self->d);
222 self->d = NULL;
223 }
224
225 /* if this CHM had a chunk cache, free it and contents */
226 if (chm->chunk_cache) {
227 for (i = 0; i < chm->num_chunks; i++) sys->free(chm->chunk_cache[i]);
228 sys->free(chm->chunk_cache);
229 }
230
231 sys->free(chm);
232}
233
234/***************************************
235 * CHMD_READ_HEADERS
236 ***************************************
237 * reads the basic CHM file headers. If the "entire" parameter is
238 * non-zero, all file entries will also be read. fills out a pre-existing
239 * mschmd_header structure, allocates memory for files as necessary
240 */
241
242/* The GUIDs found in CHM headers */
243static const unsigned char guids[32] = {
244 /* {7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC} */
245 0x10, 0xFD, 0x01, 0x7C, 0xAA, 0x7B, 0xD0, 0x11,
246 0x9E, 0x0C, 0x00, 0xA0, 0xC9, 0x22, 0xE6, 0xEC,
247 /* {7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC} */
248 0x11, 0xFD, 0x01, 0x7C, 0xAA, 0x7B, 0xD0, 0x11,
249 0x9E, 0x0C, 0x00, 0xA0, 0xC9, 0x22, 0xE6, 0xEC
250};
251
252/* reads an encoded integer into a variable; 7 bits of data per byte,
253 * the high bit is used to indicate that there is another byte */
254#define READ_ENCINT(var) do { \
255 (var) = 0; \
256 do { \
257 if (p >= end) goto chunk_end; \
258 (var) = ((var) << 7) | (*p & 0x7F); \
259 } while (*p++ & 0x80); \
260} while (0)
261
262static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh,
263 struct mschmd_header *chm, int entire)
264{
265 unsigned int section, name_len, x, errors, num_chunks;
266 unsigned char buf[0x54], *chunk = NULL, *name, *p, *end;
267 struct mschmd_file *fi, *link = NULL;
268 off_t offset, length;
269 int num_entries;
270
271 /* initialise pointers */
272 chm->files = NULL;
273 chm->sysfiles = NULL;
274 chm->chunk_cache = NULL;
275 chm->sec0.base.chm = chm;
276 chm->sec0.base.id = 0;
277 chm->sec1.base.chm = chm;
278 chm->sec1.base.id = 1;
279 chm->sec1.content = NULL;
280 chm->sec1.control = NULL;
281 chm->sec1.spaninfo = NULL;
282 chm->sec1.rtable = NULL;
283
284 /* read the first header */
285 if (sys->read(fh, &buf[0], chmhead_SIZEOF) != chmhead_SIZEOF) {
286 return MSPACK_ERR_READ;
287 }
288
289 /* check ITSF signature */
290 if (EndGetI32(&buf[chmhead_Signature]) != 0x46535449) {
291 return MSPACK_ERR_SIGNATURE;
292 }
293
294 /* check both header GUIDs */
295 if (memcmp(&buf[chmhead_GUID1], &guids[0], 32L) != 0) {
296 D(("incorrect GUIDs"))
297 return MSPACK_ERR_SIGNATURE;
298 }
299
300 chm->version = EndGetI32(&buf[chmhead_Version]);
301 chm->timestamp = EndGetM32(&buf[chmhead_Timestamp]);
302 chm->language = EndGetI32(&buf[chmhead_LanguageID]);
303 if (chm->version > 3) {
304 sys->message(fh, "WARNING; CHM version > 3");
305 }
306
307 /* read the header section table */
308 if (sys->read(fh, &buf[0], chmhst3_SIZEOF) != chmhst3_SIZEOF) {
309 return MSPACK_ERR_READ;
310 }
311
312 /* chmhst3_OffsetCS0 does not exist in version 1 or 2 CHM files.
313 * The offset will be corrected later, once HS1 is read.
314 */
315 if (read_off64(&offset, &buf[chmhst_OffsetHS0], sys, fh) ||
316 read_off64(&chm->dir_offset, &buf[chmhst_OffsetHS1], sys, fh) ||
317 read_off64(&chm->sec0.offset, &buf[chmhst3_OffsetCS0], sys, fh))
318 {
319 return MSPACK_ERR_DATAFORMAT;
320 }
321
322 /* seek to header section 0 */
323 if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) {
324 return MSPACK_ERR_SEEK;
325 }
326
327 /* read header section 0 */
328 if (sys->read(fh, &buf[0], chmhs0_SIZEOF) != chmhs0_SIZEOF) {
329 return MSPACK_ERR_READ;
330 }
331 if (read_off64(&chm->length, &buf[chmhs0_FileLen], sys, fh)) {
332 return MSPACK_ERR_DATAFORMAT;
333 }
334
335 /* seek to header section 1 */
336 if (sys->seek(fh, chm->dir_offset, MSPACK_SYS_SEEK_START)) {
337 return MSPACK_ERR_SEEK;
338 }
339
340 /* read header section 1 */
341 if (sys->read(fh, &buf[0], chmhs1_SIZEOF) != chmhs1_SIZEOF) {
342 return MSPACK_ERR_READ;
343 }
344
345 chm->dir_offset = sys->tell(fh);
346 chm->chunk_size = EndGetI32(&buf[chmhs1_ChunkSize]);
347 chm->density = EndGetI32(&buf[chmhs1_Density]);
348 chm->depth = EndGetI32(&buf[chmhs1_Depth]);
349 chm->index_root = EndGetI32(&buf[chmhs1_IndexRoot]);
350 chm->num_chunks = EndGetI32(&buf[chmhs1_NumChunks]);
351 chm->first_pmgl = EndGetI32(&buf[chmhs1_FirstPMGL]);
352 chm->last_pmgl = EndGetI32(&buf[chmhs1_LastPMGL]);
353
354 if (chm->version < 3) {
355 /* versions before 3 don't have chmhst3_OffsetCS0 */
356 chm->sec0.offset = chm->dir_offset + (chm->chunk_size * chm->num_chunks);
357 }
358
359 /* check if content offset or file size is wrong */
360 if (chm->sec0.offset > chm->length) {
361 D(("content section begins after file has ended"))
362 return MSPACK_ERR_DATAFORMAT;
363 }
364
365 /* ensure there are chunks and that chunk size is
366 * large enough for signature and num_entries */
367 if (chm->chunk_size < (pmgl_Entries + 2)) {
368 D(("chunk size not large enough"))
369 return MSPACK_ERR_DATAFORMAT;
370 }
371 if (chm->num_chunks == 0) {
372 D(("no chunks"))
373 return MSPACK_ERR_DATAFORMAT;
374 }
375
376 /* The chunk_cache data structure is not great; large values for num_chunks
377 * or num_chunks*chunk_size can exhaust all memory. Until a better chunk
378 * cache is implemented, put arbitrary limits on num_chunks and chunk size.
379 */
380 if (chm->num_chunks > 100000) {
381 D(("more than 100,000 chunks"))
382 return MSPACK_ERR_DATAFORMAT;
383 }
384 if (chm->chunk_size > 8192) {
385 D(("chunk size over 8192 (get in touch if this is valid)"))
386 return MSPACK_ERR_DATAFORMAT;
387 }
388 if ((off_t)chm->chunk_size * (off_t)chm->num_chunks > chm->length) {
389 D(("chunks larger than entire file"))
390 return MSPACK_ERR_DATAFORMAT;
391 }
392
393 /* common sense checks on header section 1 fields */
394 if (chm->chunk_size != 4096) {
395 sys->message(fh, "WARNING; chunk size is not 4096");
396 }
397 if (chm->first_pmgl != 0) {
398 sys->message(fh, "WARNING; first PMGL chunk is not zero");
399 }
400 if (chm->first_pmgl > chm->last_pmgl) {
401 D(("first pmgl chunk is after last pmgl chunk"))
402 return MSPACK_ERR_DATAFORMAT;
403 }
404 if (chm->index_root != 0xFFFFFFFF && chm->index_root >= chm->num_chunks) {
405 D(("index_root outside valid range"))
406 return MSPACK_ERR_DATAFORMAT;
407 }
408
409 /* if we are doing a quick read, stop here! */
410 if (!entire) {
411 return MSPACK_ERR_OK;
412 }
413
414 /* seek to the first PMGL chunk, and reduce the number of chunks to read */
415 if ((x = chm->first_pmgl) != 0) {
416 if (sys->seek(fh,(off_t) (x * chm->chunk_size), MSPACK_SYS_SEEK_CUR)) {
417 return MSPACK_ERR_SEEK;
418 }
419 }
420 num_chunks = chm->last_pmgl - x + 1;
421
422 if (!(chunk = (unsigned char *) sys->alloc(sys, (size_t)chm->chunk_size))) {
423 return MSPACK_ERR_NOMEMORY;
424 }
425
426 /* read and process all chunks from FirstPMGL to LastPMGL */
427 errors = 0;
428 while (num_chunks--) {
429 /* read next chunk */
430 if (sys->read(fh, chunk, (int)chm->chunk_size) != (int)chm->chunk_size) {
431 sys->free(chunk);
432 return MSPACK_ERR_READ;
433 }
434
435 /* process only directory (PMGL) chunks */
436 if (EndGetI32(&chunk[pmgl_Signature]) != 0x4C474D50) continue;
437
438 if (EndGetI32(&chunk[pmgl_QuickRefSize]) < 2) {
439 sys->message(fh, "WARNING; PMGL quickref area is too small");
440 }
441 if (EndGetI32(&chunk[pmgl_QuickRefSize]) >
442 (chm->chunk_size - pmgl_Entries))
443 {
444 sys->message(fh, "WARNING; PMGL quickref area is too large");
445 }
446
447 p = &chunk[pmgl_Entries];
448 end = &chunk[chm->chunk_size - 2];
449 num_entries = EndGetI16(end);
450
451 while (num_entries--) {
452 READ_ENCINT(name_len);
453 if (name_len > (unsigned int) (end - p)) goto chunk_end;
454 name = p; p += name_len;
455 READ_ENCINT(section);
456 READ_ENCINT(offset);
457 READ_ENCINT(length);
458
459 /* ignore blank or one-char (e.g. "/") filenames we'd return as blank */
460 if (name_len < 2 || !name[0] || !name[1]) continue;
461
462 /* empty files and directory names are stored as a file entry at
463 * offset 0 with length 0. We want to keep empty files, but not
464 * directory names, which end with a "/" */
465 if ((offset == 0) && (length == 0)) {
466 if ((name_len > 0) && (name[name_len-1] == '/')) continue;
467 }
468
469 if (section > 1) {
470 sys->message(fh, "invalid section number '%u'.", section);
471 continue;
472 }
473
474 if (!(fi = (struct mschmd_file *) sys->alloc(sys, sizeof(struct mschmd_file) + name_len + 1))) {
475 sys->free(chunk);
476 return MSPACK_ERR_NOMEMORY;
477 }
478
479 fi->next = NULL;
480 fi->filename = (char *) &fi[1];
481 fi->section = ((section == 0) ? (struct mschmd_section *) (&chm->sec0)
482 : (struct mschmd_section *) (&chm->sec1));
483 fi->offset = offset;
484 fi->length = length;
485 sys->copy(name, fi->filename, (size_t) name_len);
486 fi->filename[name_len] = '\0';
487
488 if (name[0] == ':' && name[1] == ':') {
489 /* system file */
490 if (name_len == 40 && memcmp(name, content_name, 40) == 0) {
491 chm->sec1.content = fi;
492 }
493 else if (name_len == 44 && memcmp(name, control_name, 44) == 0) {
494 chm->sec1.control = fi;
495 }
496 else if (name_len == 41 && memcmp(name, spaninfo_name, 41) == 0) {
497 chm->sec1.spaninfo = fi;
498 }
499 else if (name_len == 105 && memcmp(name, rtable_name, 105) == 0) {
500 chm->sec1.rtable = fi;
501 }
502 fi->next = chm->sysfiles;
503 chm->sysfiles = fi;
504 }
505 else {
506 /* normal file */
507 if (link) link->next = fi; else chm->files = fi;
508 link = fi;
509 }
510 }
511
512 /* this is reached either when num_entries runs out, or if
513 * reading data from the chunk reached a premature end of chunk */
514 chunk_end:
515 if (num_entries >= 0) {
516 D(("chunk ended before all entries could be read"))
517 errors++;
518 }
519
520 }
521 sys->free(chunk);
522 return (errors > 0) ? MSPACK_ERR_DATAFORMAT : MSPACK_ERR_OK;
523}
524
525/***************************************
526 * CHMD_FAST_FIND
527 ***************************************
528 * uses PMGI index chunks and quickref data to quickly locate a file
529 * directly from the on-disk index.
530 *
531 * TODO: protect against infinite loops in chunks (where pgml_NextChunk
532 * or a PMGI index entry point to an already visited chunk)
533 */
534static int chmd_fast_find(struct mschm_decompressor *base,
535 struct mschmd_header *chm, const char *filename,
536 struct mschmd_file *f_ptr, int f_size)
537{
538 struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base;
539 struct mspack_system *sys;
540 struct mspack_file *fh;
541 /* p and end are initialised to prevent MSVC warning about "potentially"
542 * uninitialised usage. This is provably untrue, but MS won't fix:
543 * https://developercommunity.visualstudio.com/content/problem/363489/c4701-false-positive-warning.html */
544 const unsigned char *chunk, *p = NULL, *end = NULL;
545 int err = MSPACK_ERR_OK, result = -1;
546 unsigned int n, sec;
547
548 if (!self || !chm || !f_ptr || (f_size != sizeof(struct mschmd_file))) {
549 return MSPACK_ERR_ARGS;
550 }
551 sys = self->system;
552
553 /* clear the results structure */
554 memset(f_ptr, 0, f_size);
555
556 if (!(fh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ))) {
557 return MSPACK_ERR_OPEN;
558 }
559
560 /* go through PMGI chunk hierarchy to reach PMGL chunk */
561 if (chm->index_root < chm->num_chunks) {
562 n = chm->index_root;
563 for (;;) {
564 if (!(chunk = read_chunk(self, chm, fh, n))) {
565 sys->close(fh);
566 return self->error;
567 }
568
569 /* search PMGI/PMGL chunk. exit early if no entry found */
570 if ((result = search_chunk(chm, chunk, filename, &p, &end)) <= 0) {
571 break;
572 }
573
574 /* found result. loop around for next chunk if this is PMGI */
575 if (chunk[3] == 0x4C) break; else READ_ENCINT(n);
576 }
577 }
578 else {
579 /* PMGL chunks only, search from first_pmgl to last_pmgl */
580 for (n = chm->first_pmgl; n <= chm->last_pmgl;
581 n = EndGetI32(&chunk[pmgl_NextChunk]))
582 {
583 if (!(chunk = read_chunk(self, chm, fh, n))) {
584 err = self->error;
585 break;
586 }
587
588 /* search PMGL chunk. exit if file found */
589 if ((result = search_chunk(chm, chunk, filename, &p, &end)) > 0) {
590 break;
591 }
592
593 /* stop simple infinite loops: can't visit the same chunk twice */
594 if (n == EndGetI32(&chunk[pmgl_NextChunk])) {
595 break;
596 }
597 }
598 }
599
600 /* if we found a file, read it */
601 if (result > 0) {
602 READ_ENCINT(sec);
603 f_ptr->section = (sec == 0) ? (struct mschmd_section *) &chm->sec0
604 : (struct mschmd_section *) &chm->sec1;
605 READ_ENCINT(f_ptr->offset);
606 READ_ENCINT(f_ptr->length);
607 }
608 else if (result < 0) {
609 err = MSPACK_ERR_DATAFORMAT;
610 }
611
612 sys->close(fh);
613 return self->error = err;
614
615 chunk_end:
616 D(("read beyond end of chunk entries"))
617 sys->close(fh);
618 return self->error = MSPACK_ERR_DATAFORMAT;
619}
620
621/* reads the given chunk into memory, storing it in a chunk cache
622 * so it doesn't need to be read from disk more than once
623 */
624static unsigned char *read_chunk(struct mschm_decompressor_p *self,
625 struct mschmd_header *chm,
626 struct mspack_file *fh,
627 unsigned int chunk_num)
628{
629 struct mspack_system *sys = self->system;
630 unsigned char *buf;
631
632 /* check arguments - most are already checked by chmd_fast_find */
633 if (chunk_num >= chm->num_chunks) return NULL;
634
635 /* ensure chunk cache is available */
636 if (!chm->chunk_cache) {
637 size_t size = sizeof(unsigned char *) * chm->num_chunks;
638 if (!(chm->chunk_cache = (unsigned char **) sys->alloc(sys, size))) {
639 self->error = MSPACK_ERR_NOMEMORY;
640 return NULL;
641 }
642 memset(chm->chunk_cache, 0, size);
643 }
644
645 /* try to answer out of chunk cache */
646 if (chm->chunk_cache[chunk_num]) return chm->chunk_cache[chunk_num];
647
648 /* need to read chunk - allocate memory for it */
649 if (!(buf = (unsigned char *) sys->alloc(sys, chm->chunk_size))) {
650 self->error = MSPACK_ERR_NOMEMORY;
651 return NULL;
652 }
653
654 /* seek to block and read it */
655 if (sys->seek(fh, (off_t) (chm->dir_offset + (chunk_num * chm->chunk_size)),
656 MSPACK_SYS_SEEK_START))
657 {
658 self->error = MSPACK_ERR_SEEK;
659 sys->free(buf);
660 return NULL;
661 }
662 if (sys->read(fh, buf, (int)chm->chunk_size) != (int)chm->chunk_size) {
663 self->error = MSPACK_ERR_READ;
664 sys->free(buf);
665 return NULL;
666 }
667
668 /* check the signature. Is is PMGL or PMGI? */
669 if (!((buf[0] == 0x50) && (buf[1] == 0x4D) && (buf[2] == 0x47) &&
670 ((buf[3] == 0x4C) || (buf[3] == 0x49))))
671 {
672 self->error = MSPACK_ERR_SEEK;
673 sys->free(buf);
674 return NULL;
675 }
676
677 /* all OK. Store chunk in cache and return it */
678 return chm->chunk_cache[chunk_num] = buf;
679}
680
681/* searches a PMGI/PMGL chunk for a given filename entry. Returns -1 on
682 * data format error, 0 if entry definitely not found, 1 if entry
683 * found. In the latter case, *result and *result_end are set pointing
684 * to that entry's data (either the "next chunk" ENCINT for a PMGI or
685 * the section, offset and length ENCINTs for a PMGL).
686 *
687 * In the case of PMGL chunks, the entry has definitely been
688 * found. In the case of PMGI chunks, the entry which points to the
689 * chunk that may eventually contain that entry has been found.
690 */
691static int search_chunk(struct mschmd_header *chm,
692 const unsigned char *chunk,
693 const char *filename,
694 const unsigned char **result,
695 const unsigned char **result_end)
696{
697 const unsigned char *start, *end, *p;
698 unsigned int qr_size, num_entries, qr_entries, qr_density, name_len;
699 unsigned int L, R, M, fname_len, entries_off, is_pmgl;
700 int cmp;
701
702 fname_len = strlen(filename);
703
704 /* PMGL chunk or PMGI chunk? (note: read_chunk() has already
705 * checked the rest of the characters in the chunk signature) */
706 if (chunk[3] == 0x4C) {
707 is_pmgl = 1;
708 entries_off = pmgl_Entries;
709 }
710 else {
711 is_pmgl = 0;
712 entries_off = pmgi_Entries;
713 }
714
715 /* Step 1: binary search first filename of each QR entry
716 * - target filename == entry
717 * found file
718 * - target filename < all entries
719 * file not found
720 * - target filename > all entries
721 * proceed to step 2 using final entry
722 * - target filename between two searched entries
723 * proceed to step 2
724 */
725 qr_size = EndGetI32(&chunk[pmgl_QuickRefSize]);
726 start = &chunk[chm->chunk_size - 2];
727 end = &chunk[chm->chunk_size - qr_size];
728 num_entries = EndGetI16(start);
729 qr_density = 1 + (1 << chm->density);
730 qr_entries = (num_entries + qr_density-1) / qr_density;
731
732 if (num_entries == 0) {
733 D(("chunk has no entries"))
734 return -1;
735 }
736
737 if (qr_size > chm->chunk_size) {
738 D(("quickref size > chunk size"))
739 return -1;
740 }
741
742 *result_end = end;
743
744 if (((int)qr_entries * 2) > (start - end)) {
745 D(("WARNING; more quickrefs than quickref space"))
746 qr_entries = 0; /* but we can live with it */
747 }
748
749 if (qr_entries > 0) {
750 L = 0;
751 R = qr_entries - 1;
752 do {
753 /* pick new midpoint */
754 M = (L + R) >> 1;
755
756 /* compare filename with entry QR points to */
757 p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)];
758 READ_ENCINT(name_len);
759 if (name_len > (unsigned int) (end - p)) goto chunk_end;
760 cmp = compare(filename, (char *)p, fname_len, name_len);
761
762 if (cmp == 0) break;
763 else if (cmp < 0) { if (M) R = M - 1; else return 0; }
764 else if (cmp > 0) L = M + 1;
765 } while (L <= R);
766 M = (L + R) >> 1;
767
768 if (cmp == 0) {
769 /* exact match! */
770 p += name_len;
771 *result = p;
772 return 1;
773 }
774
775 /* otherwise, read the group of entries for QR entry M */
776 p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)];
777 num_entries -= (M * qr_density);
778 if (num_entries > qr_density) num_entries = qr_density;
779 }
780 else {
781 p = &chunk[entries_off];
782 }
783
784 /* Step 2: linear search through the set of entries reached in step 1.
785 * - filename == any entry
786 * found entry
787 * - filename < all entries (PMGI) or any entry (PMGL)
788 * entry not found, stop now
789 * - filename > all entries
790 * entry not found (PMGL) / maybe found (PMGI)
791 * -
792 */
793 *result = NULL;
794 while (num_entries-- > 0) {
795 READ_ENCINT(name_len);
796 if (name_len > (unsigned int) (end - p)) goto chunk_end;
797 cmp = compare(filename, (char *)p, fname_len, name_len);
798 p += name_len;
799
800 if (cmp == 0) {
801 /* entry found */
802 *result = p;
803 return 1;
804 }
805
806 if (cmp < 0) {
807 /* entry not found (PMGL) / maybe found (PMGI) */
808 break;
809 }
810
811 /* read and ignore the rest of this entry */
812 if (is_pmgl) {
813 READ_ENCINT(R); /* skip section */
814 READ_ENCINT(R); /* skip offset */
815 READ_ENCINT(R); /* skip length */
816 }
817 else {
818 *result = p; /* store potential final result */
819 READ_ENCINT(R); /* skip chunk number */
820 }
821 }
822
823 /* PMGL? not found. PMGI? maybe found */
824 return (is_pmgl) ? 0 : (*result ? 1 : 0);
825
826 chunk_end:
827 D(("reached end of chunk data while searching"))
828 return -1;
829}
830
831#if HAVE_TOWLOWER
832# include <wctype.h>
833# define TOLOWER(x) towlower(x)
834#else
835# include <ctype.h>
836# define TOLOWER(x) tolower(x)
837#endif
838
839/* decodes a UTF-8 character from s[] into c. Will not read past e.
840 * doesn't test that extension bytes are %10xxxxxx.
841 * allows some overlong encodings.
842 */
843#define GET_UTF8_CHAR(s, e, c) do { \
844 unsigned char x = *s++; \
845 if (x < 0x80) c = x; \
846 else if (x >= 0xC2 && x < 0xE0 && s < e) { \
847 c = (x & 0x1F) << 6 | (*s++ & 0x3F); \
848 } \
849 else if (x >= 0xE0 && x < 0xF0 && s+1 < e) { \
850 c = (x & 0x0F) << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F); \
851 s += 2; \
852 } \
853 else if (x >= 0xF0 && x <= 0xF5 && s+2 < e) { \
854 c = (x & 0x07) << 18 | (s[0] & 0x3F) << 12 | \
855 (s[1] & 0x3F) << 6 | (s[2] & 0x3F); \
856 if (c > 0x10FFFF) c = 0xFFFD; \
857 s += 3; \
858 } \
859 else c = 0xFFFD; \
860} while (0)
861
862/* case-insensitively compares two UTF8 encoded strings. String length for
863 * both strings must be provided, null bytes are not terminators */
864static inline int compare(const char *s1, const char *s2, int l1, int l2) {
865 register const unsigned char *p1 = (const unsigned char *) s1;
866 register const unsigned char *p2 = (const unsigned char *) s2;
867 register const unsigned char *e1 = p1 + l1, *e2 = p2 + l2;
868 int c1, c2;
869
870 while (p1 < e1 && p2 < e2) {
871 GET_UTF8_CHAR(p1, e1, c1);
872 GET_UTF8_CHAR(p2, e2, c2);
873 if (c1 == c2) continue;
874 c1 = TOLOWER(c1);
875 c2 = TOLOWER(c2);
876 if (c1 != c2) return c1 - c2;
877 }
878 return l1 - l2;
879}
880
881
882/***************************************
883 * CHMD_EXTRACT
884 ***************************************
885 * extracts a file from a CHM helpfile
886 */
887static int chmd_extract(struct mschm_decompressor *base,
888 struct mschmd_file *file, const char *filename)
889{
890 struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base;
891 struct mspack_system *sys;
892 struct mschmd_header *chm;
893 struct mspack_file *fh;
894 off_t bytes;
895
896 if (!self) return MSPACK_ERR_ARGS;
897 if (!file || !file->section) return self->error = MSPACK_ERR_ARGS;
898 sys = self->system;
899 chm = file->section->chm;
900
901 /* create decompression state if it doesn't exist */
902 if (!self->d) {
903 self->d = (struct mschmd_decompress_state *) sys->alloc(sys, sizeof(struct mschmd_decompress_state));
904 if (!self->d) return self->error = MSPACK_ERR_NOMEMORY;
905 self->d->chm = chm;
906 self->d->offset = 0;
907 self->d->state = NULL;
908 self->d->sys = *sys;
909 self->d->sys.write = &chmd_sys_write;
910 self->d->infh = NULL;
911 self->d->outfh = NULL;
912 }
913
914 /* open input chm file if not open, or the open one is a different chm */
915 if (!self->d->infh || (self->d->chm != chm)) {
916 if (self->d->infh) sys->close(self->d->infh);
917 if (self->d->state) lzxd_free(self->d->state);
918 self->d->chm = chm;
919 self->d->offset = 0;
920 self->d->state = NULL;
921 self->d->infh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ);
922 if (!self->d->infh) return self->error = MSPACK_ERR_OPEN;
923 }
924
925 /* open file for output */
926 if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) {
927 return self->error = MSPACK_ERR_OPEN;
928 }
929
930 /* if file is empty, simply creating it is enough */
931 if (!file->length) {
932 sys->close(fh);
933 return self->error = MSPACK_ERR_OK;
934 }
935
936 self->error = MSPACK_ERR_OK;
937
938 switch (file->section->id) {
939 case 0: /* Uncompressed section file */
940 /* simple seek + copy */
941 if (sys->seek(self->d->infh, file->section->chm->sec0.offset
942 + file->offset, MSPACK_SYS_SEEK_START))
943 {
944 self->error = MSPACK_ERR_SEEK;
945 }
946 else {
947 unsigned char buf[512];
948 off_t length = file->length;
949 while (length > 0) {
950 int run = sizeof(buf);
951 if ((off_t)run > length) run = (int)length;
952 if (sys->read(self->d->infh, &buf[0], run) != run) {
953 self->error = MSPACK_ERR_READ;
954 break;
955 }
956 if (sys->write(fh, &buf[0], run) != run) {
957 self->error = MSPACK_ERR_WRITE;
958 break;
959 }
960 length -= run;
961 }
962 }
963 break;
964
965 case 1: /* MSCompressed section file */
966 /* (re)initialise compression state if we it is not yet initialised,
967 * or we have advanced too far and have to backtrack
968 */
969 if (!self->d->state || (file->offset < self->d->offset)) {
970 if (self->d->state) {
971 lzxd_free(self->d->state);
972 self->d->state = NULL;
973 }
974 if (chmd_init_decomp(self, file)) break;
975 }
976
977 /* seek to input data */
978 if (sys->seek(self->d->infh, self->d->inoffset, MSPACK_SYS_SEEK_START)) {
979 self->error = MSPACK_ERR_SEEK;
980 break;
981 }
982
983 /* get to correct offset. */
984 self->d->outfh = NULL;
985 if ((bytes = file->offset - self->d->offset)) {
986 self->error = lzxd_decompress(self->d->state, bytes);
987 }
988
989 /* if getting to the correct offset was error free, unpack file */
990 if (!self->error) {
991 self->d->outfh = fh;
992 self->error = lzxd_decompress(self->d->state, file->length);
993 }
994
995 /* save offset in input source stream, in case there is a section 0
996 * file between now and the next section 1 file extracted */
997 self->d->inoffset = sys->tell(self->d->infh);
998
999 /* if an LZX error occured, the LZX decompressor is now useless */
1000 if (self->error) {
1001 if (self->d->state) lzxd_free(self->d->state);
1002 self->d->state = NULL;
1003 }
1004 break;
1005 }
1006
1007 sys->close(fh);
1008 return self->error;
1009}
1010
1011/***************************************
1012 * CHMD_SYS_WRITE
1013 ***************************************
1014 * chmd_sys_write is the internal writer function which the decompressor
1015 * uses. If either writes data to disk (self->d->outfh) with the real
1016 * sys->write() function, or does nothing with the data when
1017 * self->d->outfh == NULL. advances self->d->offset.
1018 */
1019static int chmd_sys_write(struct mspack_file *file, void *buffer, int bytes) {
1020 struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) file;
1021 self->d->offset += bytes;
1022 if (self->d->outfh) {
1023 return self->system->write(self->d->outfh, buffer, bytes);
1024 }
1025 return bytes;
1026}
1027
1028/***************************************
1029 * CHMD_INIT_DECOMP
1030 ***************************************
1031 * Initialises the LZX decompressor to decompress the compressed stream,
1032 * from the nearest reset offset and length that is needed for the given
1033 * file.
1034 */
1035static int chmd_init_decomp(struct mschm_decompressor_p *self,
1036 struct mschmd_file *file)
1037{
1038 int window_size, window_bits, reset_interval, entry, err;
1039 struct mspack_system *sys = self->system;
1040 struct mschmd_sec_mscompressed *sec;
1041 unsigned char *data;
1042 off_t length, offset;
1043
1044 sec = (struct mschmd_sec_mscompressed *) file->section;
1045
1046 /* ensure we have a mscompressed content section */
1047 err = find_sys_file(self, sec, &sec->content, content_name);
1048 if (err) return self->error = err;
1049
1050 /* ensure we have a ControlData file */
1051 err = find_sys_file(self, sec, &sec->control, control_name);
1052 if (err) return self->error = err;
1053
1054 /* read ControlData */
1055 if (sec->control->length < lzxcd_SIZEOF) {
1056 D(("ControlData file is too short"))
1057 return self->error = MSPACK_ERR_DATAFORMAT;
1058 }
1059 if (!(data = read_sys_file(self, sec->control))) {
1060 D(("can't read mscompressed control data file"))
1061 return self->error;
1062 }
1063
1064 /* check LZXC signature */
1065 if (EndGetI32(&data[lzxcd_Signature]) != 0x43585A4C) {
1066 sys->free(data);
1067 return self->error = MSPACK_ERR_SIGNATURE;
1068 }
1069
1070 /* read reset_interval and window_size and validate version number */
1071 switch (EndGetI32(&data[lzxcd_Version])) {
1072 case 1:
1073 reset_interval = EndGetI32(&data[lzxcd_ResetInterval]);
1074 window_size = EndGetI32(&data[lzxcd_WindowSize]);
1075 break;
1076 case 2:
1077 reset_interval = EndGetI32(&data[lzxcd_ResetInterval]) * LZX_FRAME_SIZE;
1078 window_size = EndGetI32(&data[lzxcd_WindowSize]) * LZX_FRAME_SIZE;
1079 break;
1080 default:
1081 D(("bad controldata version"))
1082 sys->free(data);
1083 return self->error = MSPACK_ERR_DATAFORMAT;
1084 }
1085
1086 /* free ControlData */
1087 sys->free(data);
1088
1089 /* find window_bits from window_size */
1090 switch (window_size) {
1091 case 0x008000: window_bits = 15; break;
1092 case 0x010000: window_bits = 16; break;
1093 case 0x020000: window_bits = 17; break;
1094 case 0x040000: window_bits = 18; break;
1095 case 0x080000: window_bits = 19; break;
1096 case 0x100000: window_bits = 20; break;
1097 case 0x200000: window_bits = 21; break;
1098 default:
1099 D(("bad controldata window size"))
1100 return self->error = MSPACK_ERR_DATAFORMAT;
1101 }
1102
1103 /* validate reset_interval */
1104 if (reset_interval == 0 || reset_interval % LZX_FRAME_SIZE) {
1105 D(("bad controldata reset interval"))
1106 return self->error = MSPACK_ERR_DATAFORMAT;
1107 }
1108
1109 /* which reset table entry would we like? */
1110 entry = file->offset / reset_interval;
1111 /* convert from reset interval multiple (usually 64k) to 32k frames */
1112 entry *= reset_interval / LZX_FRAME_SIZE;
1113
1114 /* read the reset table entry */
1115 if (read_reset_table(self, sec, entry, &length, &offset)) {
1116 /* the uncompressed length given in the reset table is dishonest.
1117 * the uncompressed data is always padded out from the given
1118 * uncompressed length up to the next reset interval */
1119 length += reset_interval - 1;
1120 length &= -reset_interval;
1121 }
1122 else {
1123 /* if we can't read the reset table entry, just start from
1124 * the beginning. Use spaninfo to get the uncompressed length */
1125 entry = 0;
1126 offset = 0;
1127 err = read_spaninfo(self, sec, &length);
1128 }
1129 if (err) return self->error = err;
1130
1131 /* get offset of compressed data stream:
1132 * = offset of uncompressed section from start of file
1133 * + offset of compressed stream from start of uncompressed section
1134 * + offset of chosen reset interval from start of compressed stream */
1135 self->d->inoffset = file->section->chm->sec0.offset + sec->content->offset + offset;
1136
1137 /* set start offset and overall remaining stream length */
1138 self->d->offset = entry * LZX_FRAME_SIZE;
1139 length -= self->d->offset;
1140
1141 /* initialise LZX stream */
1142 self->d->state = lzxd_init(&self->d->sys, self->d->infh,
1143 (struct mspack_file *) self, window_bits,
1144 reset_interval / LZX_FRAME_SIZE,
1145 4096, length, 0);
1146 if (!self->d->state) self->error = MSPACK_ERR_NOMEMORY;
1147 return self->error;
1148}
1149
1150/***************************************
1151 * READ_RESET_TABLE
1152 ***************************************
1153 * Reads one entry out of the reset table. Also reads the uncompressed
1154 * data length. Writes these to offset_ptr and length_ptr respectively.
1155 * Returns non-zero for success, zero for failure.
1156 */
1157static int read_reset_table(struct mschm_decompressor_p *self,
1158 struct mschmd_sec_mscompressed *sec,
1159 unsigned int entry,
1160 off_t *length_ptr, off_t *offset_ptr)
1161{
1162 struct mspack_system *sys = self->system;
1163 unsigned char *data;
1164 unsigned int pos, entrysize;
1165
1166 /* do we have a ResetTable file? */
1167 int err = find_sys_file(self, sec, &sec->rtable, rtable_name);
1168 if (err) return 0;
1169
1170 /* read ResetTable file */
1171 if (sec->rtable->length < lzxrt_headerSIZEOF) {
1172 D(("ResetTable file is too short"))
1173 return 0;
1174 }
1175 if (!(data = read_sys_file(self, sec->rtable))) {
1176 D(("can't read reset table"))
1177 return 0;
1178 }
1179
1180 /* check sanity of reset table */
1181 if (EndGetI32(&data[lzxrt_FrameLen]) != LZX_FRAME_SIZE) {
1182 D(("bad reset table frame length"))
1183 sys->free(data);
1184 return 0;
1185 }
1186
1187 /* get the uncompressed length of the LZX stream */
1188 if (read_off64(length_ptr, &data[lzxrt_UncompLen], sys, self->d->infh)) {
1189 sys->free(data);
1190 return 0;
1191 }
1192
1193 entrysize = EndGetI32(&data[lzxrt_EntrySize]);
1194 pos = EndGetI32(&data[lzxrt_TableOffset]) + (entry * entrysize);
1195
1196 /* ensure reset table entry for this offset exists */
1197 if (entry < EndGetI32(&data[lzxrt_NumEntries]) &&
1198 pos <= (sec->rtable->length - entrysize))
1199 {
1200 switch (entrysize) {
1201 case 4:
1202 *offset_ptr = EndGetI32(&data[pos]);
1203 err = 0;
1204 break;
1205 case 8:
1206 err = read_off64(offset_ptr, &data[pos], sys, self->d->infh);
1207 break;
1208 default:
1209 D(("reset table entry size neither 4 nor 8"))
1210 err = 1;
1211 break;
1212 }
1213 }
1214 else {
1215 D(("bad reset interval"))
1216 err = 1;
1217 }
1218
1219 /* free the reset table */
1220 sys->free(data);
1221
1222 /* return success */
1223 return (err == 0);
1224}
1225
1226/***************************************
1227 * READ_SPANINFO
1228 ***************************************
1229 * Reads the uncompressed data length from the spaninfo file.
1230 * Returns zero for success or a non-zero error code for failure.
1231 */
1232static int read_spaninfo(struct mschm_decompressor_p *self,
1233 struct mschmd_sec_mscompressed *sec,
1234 off_t *length_ptr)
1235{
1236 struct mspack_system *sys = self->system;
1237 unsigned char *data;
1238
1239 /* find SpanInfo file */
1240 int err = find_sys_file(self, sec, &sec->spaninfo, spaninfo_name);
1241 if (err) return MSPACK_ERR_DATAFORMAT;
1242
1243 /* check it's large enough */
1244 if (sec->spaninfo->length != 8) {
1245 D(("SpanInfo file is wrong size"))
1246 return MSPACK_ERR_DATAFORMAT;
1247 }
1248
1249 /* read the SpanInfo file */
1250 if (!(data = read_sys_file(self, sec->spaninfo))) {
1251 D(("can't read SpanInfo file"))
1252 return self->error;
1253 }
1254
1255 /* get the uncompressed length of the LZX stream */
1256 err = read_off64(length_ptr, data, sys, self->d->infh);
1257 sys->free(data);
1258 if (err) return MSPACK_ERR_DATAFORMAT;
1259
1260 if (*length_ptr <= 0) {
1261 D(("output length is invalid"))
1262 return MSPACK_ERR_DATAFORMAT;
1263 }
1264
1265 return MSPACK_ERR_OK;
1266}
1267
1268/***************************************
1269 * FIND_SYS_FILE
1270 ***************************************
1271 * Uses chmd_fast_find to locate a system file, and fills out that system
1272 * file's entry and links it into the list of system files. Returns zero
1273 * for success, non-zero for both failure and the file not existing.
1274 */
1275static int find_sys_file(struct mschm_decompressor_p *self,
1276 struct mschmd_sec_mscompressed *sec,
1277 struct mschmd_file **f_ptr, const char *name)
1278{
1279 struct mspack_system *sys = self->system;
1280 struct mschmd_file result;
1281
1282 /* already loaded */
1283 if (*f_ptr) return MSPACK_ERR_OK;
1284
1285 /* try using fast_find to find the file - return DATAFORMAT error if
1286 * it fails, or successfully doesn't find the file */
1287 if (chmd_fast_find((struct mschm_decompressor *) self, sec->base.chm,
1288 name, &result, (int)sizeof(result)) || !result.section)
1289 {
1290 return MSPACK_ERR_DATAFORMAT;
1291 }
1292
1293 if (!(*f_ptr = (struct mschmd_file *) sys->alloc(sys, sizeof(result)))) {
1294 return MSPACK_ERR_NOMEMORY;
1295 }
1296
1297 /* copy result */
1298 *(*f_ptr) = result;
1299 (*f_ptr)->filename = (char *) name;
1300
1301 /* link file into sysfiles list */
1302 (*f_ptr)->next = sec->base.chm->sysfiles;
1303 sec->base.chm->sysfiles = *f_ptr;
1304 return MSPACK_ERR_OK;
1305}
1306
1307/***************************************
1308 * READ_SYS_FILE
1309 ***************************************
1310 * Allocates memory for a section 0 (uncompressed) file and reads it into
1311 * memory.
1312 */
1313static unsigned char *read_sys_file(struct mschm_decompressor_p *self,
1314 struct mschmd_file *file)
1315{
1316 struct mspack_system *sys = self->system;
1317 unsigned char *data = NULL;
1318 int len;
1319
1320 if (!file || !file->section || (file->section->id != 0)) {
1321 self->error = MSPACK_ERR_DATAFORMAT;
1322 return NULL;
1323 }
1324
1325 len = (int) file->length;
1326
1327 if (!(data = (unsigned char *) sys->alloc(sys, (size_t) len))) {
1328 self->error = MSPACK_ERR_NOMEMORY;
1329 return NULL;
1330 }
1331 if (sys->seek(self->d->infh, file->section->chm->sec0.offset
1332 + file->offset, MSPACK_SYS_SEEK_START))
1333 {
1334 self->error = MSPACK_ERR_SEEK;
1335 sys->free(data);
1336 return NULL;
1337 }
1338 if (sys->read(self->d->infh, data, len) != len) {
1339 self->error = MSPACK_ERR_READ;
1340 sys->free(data);
1341 return NULL;
1342 }
1343 return data;
1344}
1345
1346/***************************************
1347 * CHMD_ERROR
1348 ***************************************
1349 * returns the last error that occurred
1350 */
1351static int chmd_error(struct mschm_decompressor *base) {
1352 struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base;
1353 return (self) ? self->error : MSPACK_ERR_ARGS;
1354}
1355
1356/***************************************
1357 * READ_OFF64
1358 ***************************************
1359 * Reads a 64-bit signed integer from memory in Intel byte order.
1360 * If running on a system with a 64-bit off_t, this is simply done.
1361 * If running on a system with a 32-bit off_t, offsets up to 0x7FFFFFFF
1362 * are accepted, offsets beyond that cause an error message.
1363 */
1364static int read_off64(off_t *var, unsigned char *mem,
1365 struct mspack_system *sys, struct mspack_file *fh)
1366{
1367#if LARGEFILE_SUPPORT
1368 *var = EndGetI64(mem);
1369#else
1370 *var = EndGetI32(mem);
1371 if ((*var & 0x80000000) || EndGetI32(mem+4)) {
1372 sys->message(fh, (char *)largefile_msg);
1373 return 1;
1374 }
1375#endif
1376 return 0;
1377}
diff --git a/utils/rbutilqt/mspack/des.h b/utils/rbutilqt/mspack/des.h
new file mode 100644
index 0000000000..64a1ed27e4
--- /dev/null
+++ b/utils/rbutilqt/mspack/des.h
@@ -0,0 +1,15 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifndef MSPACK_DES_H
11#define MSPACK_DES_H 1
12
13/* DES encryption / decryption definitions */
14
15#endif
diff --git a/utils/rbutilqt/mspack/hlp.h b/utils/rbutilqt/mspack/hlp.h
new file mode 100644
index 0000000000..a6e3abc7ff
--- /dev/null
+++ b/utils/rbutilqt/mspack/hlp.h
@@ -0,0 +1,33 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifndef MSPACK_HLP_H
11#define MSPACK_HLP_H 1
12
13#include "lzss.h"
14
15/* generic HLP definitions */
16
17/* HLP compression definitions */
18
19struct mshlp_compressor_p {
20 struct mshlp_compressor base;
21 struct mspack_system *system;
22 /* todo */
23};
24
25/* HLP decompression definitions */
26
27struct mshlp_decompressor_p {
28 struct mshlp_decompressor base;
29 struct mspack_system *system;
30 /* todo */
31};
32
33#endif
diff --git a/utils/rbutilqt/mspack/hlpc.c b/utils/rbutilqt/mspack/hlpc.c
new file mode 100644
index 0000000000..b33e499f65
--- /dev/null
+++ b/utils/rbutilqt/mspack/hlpc.c
@@ -0,0 +1,24 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10/* HLP compression implementation */
11
12#include "system-mspack.h"
13#include "hlp.h"
14
15struct mshlp_compressor *
16 mspack_create_hlp_compressor(struct mspack_system *sys)
17{
18 /* todo */
19 return NULL;
20}
21
22void mspack_destroy_hlp_compressor(struct mshlp_compressor *self) {
23 /* todo */
24}
diff --git a/utils/rbutilqt/mspack/hlpd.c b/utils/rbutilqt/mspack/hlpd.c
new file mode 100644
index 0000000000..ae1f2efba8
--- /dev/null
+++ b/utils/rbutilqt/mspack/hlpd.c
@@ -0,0 +1,24 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10/* HLP decompression implementation */
11
12#include "system-mspack.h"
13#include "hlp.h"
14
15struct mshlp_decompressor *
16 mspack_create_hlp_decompressor(struct mspack_system *sys)
17{
18 /* todo */
19 return NULL;
20}
21
22void mspack_destroy_hlp_decompressor(struct mshlp_decompressor *self) {
23 /* todo */
24}
diff --git a/utils/rbutilqt/mspack/kwaj.h b/utils/rbutilqt/mspack/kwaj.h
new file mode 100644
index 0000000000..75425d958a
--- /dev/null
+++ b/utils/rbutilqt/mspack/kwaj.h
@@ -0,0 +1,118 @@
1/* This file is part of libmspack.
2 * (C) 2003-2010 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifndef MSPACK_KWAJ_H
11#define MSPACK_KWAJ_H 1
12
13#include "lzss.h"
14
15/* generic KWAJ definitions */
16#define kwajh_Signature1 (0x00)
17#define kwajh_Signature2 (0x04)
18#define kwajh_CompMethod (0x08)
19#define kwajh_DataOffset (0x0a)
20#define kwajh_Flags (0x0c)
21#define kwajh_SIZEOF (0x0e)
22
23/* KWAJ compression definitions */
24
25struct mskwaj_compressor_p {
26 struct mskwaj_compressor base;
27 struct mspack_system *system;
28 /* todo */
29 int param[2]; /* !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!! */
30 int error;
31};
32
33/* KWAJ decompression definitions */
34
35struct mskwaj_decompressor_p {
36 struct mskwaj_decompressor base;
37 struct mspack_system *system;
38 int error;
39};
40
41struct mskwajd_header_p {
42 struct mskwajd_header base;
43 struct mspack_file *fh;
44};
45
46/* input buffer size during decompression - not worth parameterising IMHO */
47#define KWAJ_INPUT_SIZE (2048)
48
49/* huffman codes that are 9 bits or less are decoded immediately */
50#define KWAJ_TABLEBITS (9)
51
52/* number of codes in each huffman table */
53#define KWAJ_MATCHLEN1_SYMS (16)
54#define KWAJ_MATCHLEN2_SYMS (16)
55#define KWAJ_LITLEN_SYMS (32)
56#define KWAJ_OFFSET_SYMS (64)
57#define KWAJ_LITERAL_SYMS (256)
58
59/* define decoding table sizes */
60#define KWAJ_TABLESIZE (1 << KWAJ_TABLEBITS)
61#if KWAJ_TABLESIZE < (KWAJ_MATCHLEN1_SYMS * 2)
62# define KWAJ_MATCHLEN1_TBLSIZE (KWAJ_MATCHLEN1_SYMS * 4)
63#else
64# define KWAJ_MATCHLEN1_TBLSIZE (KWAJ_TABLESIZE + (KWAJ_MATCHLEN1_SYMS * 2))
65#endif
66#if KWAJ_TABLESIZE < (KWAJ_MATCHLEN2_SYMS * 2)
67# define KWAJ_MATCHLEN2_TBLSIZE (KWAJ_MATCHLEN2_SYMS * 4)
68#else
69# define KWAJ_MATCHLEN2_TBLSIZE (KWAJ_TABLESIZE + (KWAJ_MATCHLEN2_SYMS * 2))
70#endif
71#if KWAJ_TABLESIZE < (KWAJ_LITLEN_SYMS * 2)
72# define KWAJ_LITLEN_TBLSIZE (KWAJ_LITLEN_SYMS * 4)
73#else
74# define KWAJ_LITLEN_TBLSIZE (KWAJ_TABLESIZE + (KWAJ_LITLEN_SYMS * 2))
75#endif
76#if KWAJ_TABLESIZE < (KWAJ_OFFSET_SYMS * 2)
77# define KWAJ_OFFSET_TBLSIZE (KWAJ_OFFSET_SYMS * 4)
78#else
79# define KWAJ_OFFSET_TBLSIZE (KWAJ_TABLESIZE + (KWAJ_OFFSET_SYMS * 2))
80#endif
81#if KWAJ_TABLESIZE < (KWAJ_LITERAL_SYMS * 2)
82# define KWAJ_LITERAL_TBLSIZE (KWAJ_LITERAL_SYMS * 4)
83#else
84# define KWAJ_LITERAL_TBLSIZE (KWAJ_TABLESIZE + (KWAJ_LITERAL_SYMS * 2))
85#endif
86
87struct kwajd_stream {
88 /* I/O buffering */
89 struct mspack_system *sys;
90 struct mspack_file *input;
91 struct mspack_file *output;
92 unsigned char *i_ptr, *i_end;
93 unsigned int bit_buffer, bits_left;
94 int input_end;
95
96 /* huffman code lengths */
97 unsigned char MATCHLEN1_len [KWAJ_MATCHLEN1_SYMS];
98 unsigned char MATCHLEN2_len [KWAJ_MATCHLEN2_SYMS];
99 unsigned char LITLEN_len [KWAJ_LITLEN_SYMS];
100 unsigned char OFFSET_len [KWAJ_OFFSET_SYMS];
101 unsigned char LITERAL_len [KWAJ_LITERAL_SYMS];
102
103 /* huffman decoding tables */
104 unsigned short MATCHLEN1_table [KWAJ_MATCHLEN1_TBLSIZE];
105 unsigned short MATCHLEN2_table [KWAJ_MATCHLEN2_TBLSIZE];
106 unsigned short LITLEN_table [KWAJ_LITLEN_TBLSIZE];
107 unsigned short OFFSET_table [KWAJ_OFFSET_TBLSIZE];
108 unsigned short LITERAL_table [KWAJ_LITERAL_TBLSIZE];
109
110 /* input buffer */
111 unsigned char inbuf[KWAJ_INPUT_SIZE];
112
113 /* history window */
114 unsigned char window[LZSS_WINDOW_SIZE];
115};
116
117
118#endif
diff --git a/utils/rbutilqt/mspack/kwajc.c b/utils/rbutilqt/mspack/kwajc.c
new file mode 100644
index 0000000000..52ca29432a
--- /dev/null
+++ b/utils/rbutilqt/mspack/kwajc.c
@@ -0,0 +1,24 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10/* KWAJ compression implementation */
11
12#include "system-mspack.h"
13#include "kwaj.h"
14
15struct mskwaj_compressor *
16 mspack_create_kwaj_compressor(struct mspack_system *sys)
17{
18 /* todo */
19 return NULL;
20}
21
22void mspack_destroy_kwaj_compressor(struct mskwaj_compressor *self) {
23 /* todo */
24}
diff --git a/utils/rbutilqt/mspack/kwajd.c b/utils/rbutilqt/mspack/kwajd.c
new file mode 100644
index 0000000000..24e0b0613b
--- /dev/null
+++ b/utils/rbutilqt/mspack/kwajd.c
@@ -0,0 +1,566 @@
1/* This file is part of libmspack.
2 * (C) 2003-2011 Stuart Caie.
3 *
4 * KWAJ is a format very similar to SZDD. KWAJ method 3 (LZH) was
5 * written by Jeff Johnson.
6 *
7 * libmspack is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
9 *
10 * For further details, see the file COPYING.LIB distributed with libmspack
11 */
12
13/* KWAJ decompression implementation */
14
15#include "system-mspack.h"
16#include "kwaj.h"
17#include "mszip.h"
18
19/* prototypes */
20static struct mskwajd_header *kwajd_open(
21 struct mskwaj_decompressor *base, const char *filename);
22static void kwajd_close(
23 struct mskwaj_decompressor *base, struct mskwajd_header *hdr);
24static int kwajd_read_headers(
25 struct mspack_system *sys, struct mspack_file *fh,
26 struct mskwajd_header *hdr);
27static int kwajd_extract(
28 struct mskwaj_decompressor *base, struct mskwajd_header *hdr,
29 const char *filename);
30static int kwajd_decompress(
31 struct mskwaj_decompressor *base, const char *input, const char *output);
32static int kwajd_error(
33 struct mskwaj_decompressor *base);
34
35static struct kwajd_stream *lzh_init(
36 struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out);
37static int lzh_decompress(
38 struct kwajd_stream *kwaj);
39static void lzh_free(
40 struct kwajd_stream *kwaj);
41static int lzh_read_lens(
42 struct kwajd_stream *kwaj,
43 unsigned int type, unsigned int numsyms,
44 unsigned char *lens);
45static int lzh_read_input(
46 struct kwajd_stream *kwaj);
47
48
49/***************************************
50 * MSPACK_CREATE_KWAJ_DECOMPRESSOR
51 ***************************************
52 * constructor
53 */
54struct mskwaj_decompressor *
55 mspack_create_kwaj_decompressor(struct mspack_system *sys)
56{
57 struct mskwaj_decompressor_p *self = NULL;
58
59 if (!sys) sys = mspack_default_system;
60 if (!mspack_valid_system(sys)) return NULL;
61
62 if ((self = (struct mskwaj_decompressor_p *) sys->alloc(sys, sizeof(struct mskwaj_decompressor_p)))) {
63 self->base.open = &kwajd_open;
64 self->base.close = &kwajd_close;
65 self->base.extract = &kwajd_extract;
66 self->base.decompress = &kwajd_decompress;
67 self->base.last_error = &kwajd_error;
68 self->system = sys;
69 self->error = MSPACK_ERR_OK;
70 }
71 return (struct mskwaj_decompressor *) self;
72}
73
74/***************************************
75 * MSPACK_DESTROY_KWAJ_DECOMPRESSOR
76 ***************************************
77 * destructor
78 */
79void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *base)
80{
81 struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
82 if (self) {
83 struct mspack_system *sys = self->system;
84 sys->free(self);
85 }
86}
87
88/***************************************
89 * KWAJD_OPEN
90 ***************************************
91 * opens a KWAJ file without decompressing, reads header
92 */
93static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base,
94 const char *filename)
95{
96 struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
97 struct mskwajd_header *hdr;
98 struct mspack_system *sys;
99 struct mspack_file *fh;
100
101 if (!self) return NULL;
102 sys = self->system;
103
104 fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ);
105 hdr = (struct mskwajd_header *) sys->alloc(sys, sizeof(struct mskwajd_header_p));
106 if (fh && hdr) {
107 ((struct mskwajd_header_p *) hdr)->fh = fh;
108 self->error = kwajd_read_headers(sys, fh, hdr);
109 }
110 else {
111 if (!fh) self->error = MSPACK_ERR_OPEN;
112 if (!hdr) self->error = MSPACK_ERR_NOMEMORY;
113 }
114
115 if (self->error) {
116 if (fh) sys->close(fh);
117 sys->free(hdr);
118 hdr = NULL;
119 }
120
121 return hdr;
122}
123
124/***************************************
125 * KWAJD_CLOSE
126 ***************************************
127 * closes a KWAJ file
128 */
129static void kwajd_close(struct mskwaj_decompressor *base,
130 struct mskwajd_header *hdr)
131{
132 struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
133 struct mskwajd_header_p *hdr_p = (struct mskwajd_header_p *) hdr;
134
135 if (!self || !self->system) return;
136
137 /* close the file handle associated */
138 self->system->close(hdr_p->fh);
139
140 /* free the memory associated */
141 self->system->free(hdr);
142
143 self->error = MSPACK_ERR_OK;
144}
145
146/***************************************
147 * KWAJD_READ_HEADERS
148 ***************************************
149 * reads the headers of a KWAJ format file
150 */
151static int kwajd_read_headers(struct mspack_system *sys,
152 struct mspack_file *fh,
153 struct mskwajd_header *hdr)
154{
155 unsigned char buf[16];
156 int i;
157
158 /* read in the header */
159 if (sys->read(fh, &buf[0], kwajh_SIZEOF) != kwajh_SIZEOF) {
160 return MSPACK_ERR_READ;
161 }
162
163 /* check for "KWAJ" signature */
164 if (((unsigned int) EndGetI32(&buf[kwajh_Signature1]) != 0x4A41574B) ||
165 ((unsigned int) EndGetI32(&buf[kwajh_Signature2]) != 0xD127F088))
166 {
167 return MSPACK_ERR_SIGNATURE;
168 }
169
170 /* basic header fields */
171 hdr->comp_type = EndGetI16(&buf[kwajh_CompMethod]);
172 hdr->data_offset = EndGetI16(&buf[kwajh_DataOffset]);
173 hdr->headers = EndGetI16(&buf[kwajh_Flags]);
174 hdr->length = 0;
175 hdr->filename = NULL;
176 hdr->extra = NULL;
177 hdr->extra_length = 0;
178
179 /* optional headers */
180
181 /* 4 bytes: length of unpacked file */
182 if (hdr->headers & MSKWAJ_HDR_HASLENGTH) {
183 if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ;
184 hdr->length = EndGetI32(&buf[0]);
185 }
186
187 /* 2 bytes: unknown purpose */
188 if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN1) {
189 if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ;
190 }
191
192 /* 2 bytes: length of section, then [length] bytes: unknown purpose */
193 if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN2) {
194 if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ;
195 i = EndGetI16(&buf[0]);
196 if (sys->seek(fh, (off_t)i, MSPACK_SYS_SEEK_CUR)) return MSPACK_ERR_SEEK;
197 }
198
199 /* filename and extension */
200 if (hdr->headers & (MSKWAJ_HDR_HASFILENAME | MSKWAJ_HDR_HASFILEEXT)) {
201 int len;
202 /* allocate memory for maximum length filename */
203 char *fn = (char *) sys->alloc(sys, (size_t) 13);
204 if (!(hdr->filename = fn)) return MSPACK_ERR_NOMEMORY;
205
206 /* copy filename if present */
207 if (hdr->headers & MSKWAJ_HDR_HASFILENAME) {
208 /* read and copy up to 9 bytes of a null terminated string */
209 if ((len = sys->read(fh, &buf[0], 9)) < 2) return MSPACK_ERR_READ;
210 for (i = 0; i < len; i++) if (!(*fn++ = buf[i])) break;
211 /* if string was 9 bytes with no null terminator, reject it */
212 if (i == 9 && buf[8] != '\0') return MSPACK_ERR_DATAFORMAT;
213 /* seek to byte after string ended in file */
214 if (sys->seek(fh, (off_t)(i + 1 - len), MSPACK_SYS_SEEK_CUR))
215 return MSPACK_ERR_SEEK;
216 fn--; /* remove the null terminator */
217 }
218
219 /* copy extension if present */
220 if (hdr->headers & MSKWAJ_HDR_HASFILEEXT) {
221 *fn++ = '.';
222 /* read and copy up to 4 bytes of a null terminated string */
223 if ((len = sys->read(fh, &buf[0], 4)) < 2) return MSPACK_ERR_READ;
224 for (i = 0; i < len; i++) if (!(*fn++ = buf[i])) break;
225 /* if string was 4 bytes with no null terminator, reject it */
226 if (i == 4 && buf[3] != '\0') return MSPACK_ERR_DATAFORMAT;
227 /* seek to byte after string ended in file */
228 if (sys->seek(fh, (off_t)(i + 1 - len), MSPACK_SYS_SEEK_CUR))
229 return MSPACK_ERR_SEEK;
230 fn--; /* remove the null terminator */
231 }
232 *fn = '\0';
233 }
234
235 /* 2 bytes: extra text length then [length] bytes of extra text data */
236 if (hdr->headers & MSKWAJ_HDR_HASEXTRATEXT) {
237 if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ;
238 i = EndGetI16(&buf[0]);
239 hdr->extra = (char *) sys->alloc(sys, (size_t)i+1);
240 if (! hdr->extra) return MSPACK_ERR_NOMEMORY;
241 if (sys->read(fh, hdr->extra, i) != i) return MSPACK_ERR_READ;
242 hdr->extra[i] = '\0';
243 hdr->extra_length = i;
244 }
245 return MSPACK_ERR_OK;
246}
247
248/***************************************
249 * KWAJD_EXTRACT
250 ***************************************
251 * decompresses a KWAJ file
252 */
253static int kwajd_extract(struct mskwaj_decompressor *base,
254 struct mskwajd_header *hdr, const char *filename)
255{
256 struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
257 struct mspack_system *sys;
258 struct mspack_file *fh, *outfh;
259
260 if (!self) return MSPACK_ERR_ARGS;
261 if (!hdr) return self->error = MSPACK_ERR_ARGS;
262
263 sys = self->system;
264 fh = ((struct mskwajd_header_p *) hdr)->fh;
265
266 /* seek to the compressed data */
267 if (sys->seek(fh, hdr->data_offset, MSPACK_SYS_SEEK_START)) {
268 return self->error = MSPACK_ERR_SEEK;
269 }
270
271 /* open file for output */
272 if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) {
273 return self->error = MSPACK_ERR_OPEN;
274 }
275
276 self->error = MSPACK_ERR_OK;
277
278 /* decompress based on format */
279 if (hdr->comp_type == MSKWAJ_COMP_NONE ||
280 hdr->comp_type == MSKWAJ_COMP_XOR)
281 {
282 /* NONE is a straight copy. XOR is a copy xored with 0xFF */
283 unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) KWAJ_INPUT_SIZE);
284 if (buf) {
285 int read, i;
286 while ((read = sys->read(fh, buf, KWAJ_INPUT_SIZE)) > 0) {
287 if (hdr->comp_type == MSKWAJ_COMP_XOR) {
288 for (i = 0; i < read; i++) buf[i] ^= 0xFF;
289 }
290 if (sys->write(outfh, buf, read) != read) {
291 self->error = MSPACK_ERR_WRITE;
292 break;
293 }
294 }
295 if (read < 0) self->error = MSPACK_ERR_READ;
296 sys->free(buf);
297 }
298 else {
299 self->error = MSPACK_ERR_NOMEMORY;
300 }
301 }
302 else if (hdr->comp_type == MSKWAJ_COMP_SZDD) {
303 self->error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE,
304 LZSS_MODE_EXPAND);
305 }
306 else if (hdr->comp_type == MSKWAJ_COMP_LZH) {
307 struct kwajd_stream *lzh = lzh_init(sys, fh, outfh);
308 self->error = (lzh) ? lzh_decompress(lzh) : MSPACK_ERR_NOMEMORY;
309 lzh_free(lzh);
310 }
311 else if (hdr->comp_type == MSKWAJ_COMP_MSZIP) {
312 struct mszipd_stream *zip = mszipd_init(sys,fh,outfh,KWAJ_INPUT_SIZE,0);
313 self->error = (zip) ? mszipd_decompress_kwaj(zip) : MSPACK_ERR_NOMEMORY;
314 mszipd_free(zip);
315 }
316 else {
317 self->error = MSPACK_ERR_DATAFORMAT;
318 }
319
320 /* close output file */
321 sys->close(outfh);
322
323 return self->error;
324}
325
326/***************************************
327 * KWAJD_DECOMPRESS
328 ***************************************
329 * unpacks directly from input to output
330 */
331static int kwajd_decompress(struct mskwaj_decompressor *base,
332 const char *input, const char *output)
333{
334 struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
335 struct mskwajd_header *hdr;
336 int error;
337
338 if (!self) return MSPACK_ERR_ARGS;
339
340 if (!(hdr = kwajd_open(base, input))) return self->error;
341 error = kwajd_extract(base, hdr, output);
342 kwajd_close(base, hdr);
343 return self->error = error;
344}
345
346/***************************************
347 * KWAJD_ERROR
348 ***************************************
349 * returns the last error that occurred
350 */
351static int kwajd_error(struct mskwaj_decompressor *base)
352{
353 struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
354 return (self) ? self->error : MSPACK_ERR_ARGS;
355}
356
357/***************************************
358 * LZH_INIT, LZH_DECOMPRESS, LZH_FREE
359 ***************************************
360 * unpacks KWAJ method 3 files
361 */
362
363/* import bit-reading macros and code */
364#define BITS_TYPE struct kwajd_stream
365#define BITS_VAR lzh
366#define BITS_ORDER_MSB
367#define BITS_NO_READ_INPUT
368#define READ_BYTES do { \
369 if (i_ptr >= i_end) { \
370 if ((err = lzh_read_input(lzh))) return err; \
371 i_ptr = lzh->i_ptr; \
372 i_end = lzh->i_end; \
373 } \
374 INJECT_BITS(*i_ptr++, 8); \
375} while (0)
376#include <readbits.h>
377
378/* import huffman-reading macros and code */
379#define TABLEBITS(tbl) KWAJ_TABLEBITS
380#define MAXSYMBOLS(tbl) KWAJ_##tbl##_SYMS
381#define HUFF_TABLE(tbl,idx) lzh->tbl##_table[idx]
382#define HUFF_LEN(tbl,idx) lzh->tbl##_len[idx]
383#define HUFF_ERROR return MSPACK_ERR_DATAFORMAT
384#include <readhuff.h>
385
386/* In the KWAJ LZH format, there is no special 'eof' marker, it just
387 * ends. Depending on how many bits are left in the final byte when
388 * the stream ends, that might be enough to start another literal or
389 * match. The only easy way to detect that we've come to an end is to
390 * guard all bit-reading. We allow fake bits to be read once we reach
391 * the end of the stream, but we check if we then consumed any of
392 * those fake bits, after doing the READ_BITS / READ_HUFFSYM. This
393 * isn't how the default readbits.h read_input() works (it simply lets
394 * 2 fake bytes in then stops), so we implement our own.
395 */
396#define READ_BITS_SAFE(val, n) do { \
397 READ_BITS(val, n); \
398 if (lzh->input_end && bits_left < lzh->input_end) \
399 return MSPACK_ERR_OK; \
400} while (0)
401
402#define READ_HUFFSYM_SAFE(tbl, val) do { \
403 READ_HUFFSYM(tbl, val); \
404 if (lzh->input_end && bits_left < lzh->input_end) \
405 return MSPACK_ERR_OK; \
406} while (0)
407
408#define BUILD_TREE(tbl, type) \
409 STORE_BITS; \
410 err = lzh_read_lens(lzh, type, MAXSYMBOLS(tbl), &HUFF_LEN(tbl,0)); \
411 if (err) return err; \
412 RESTORE_BITS; \
413 if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \
414 &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \
415 return MSPACK_ERR_DATAFORMAT;
416
417#define WRITE_BYTE do { \
418 if (lzh->sys->write(lzh->output, &lzh->window[pos], 1) != 1) \
419 return MSPACK_ERR_WRITE; \
420} while (0)
421
422static struct kwajd_stream *lzh_init(struct mspack_system *sys,
423 struct mspack_file *in, struct mspack_file *out)
424{
425 struct kwajd_stream *lzh;
426
427 if (!sys || !in || !out) return NULL;
428 if (!(lzh = (struct kwajd_stream *) sys->alloc(sys, sizeof(struct kwajd_stream)))) return NULL;
429
430 lzh->sys = sys;
431 lzh->input = in;
432 lzh->output = out;
433 return lzh;
434}
435
436static int lzh_decompress(struct kwajd_stream *lzh)
437{
438 register unsigned int bit_buffer;
439 register int bits_left, i;
440 register unsigned short sym;
441 unsigned char *i_ptr, *i_end, lit_run = 0;
442 int j, pos = 0, len, offset, err;
443 unsigned int types[6];
444
445 /* reset global state */
446 INIT_BITS;
447 RESTORE_BITS;
448 memset(&lzh->window[0], LZSS_WINDOW_FILL, (size_t) LZSS_WINDOW_SIZE);
449
450 /* read 6 encoding types (for byte alignment) but only 5 are needed */
451 for (i = 0; i < 6; i++) READ_BITS_SAFE(types[i], 4);
452
453 /* read huffman table symbol lengths and build huffman trees */
454 BUILD_TREE(MATCHLEN1, types[0]);
455 BUILD_TREE(MATCHLEN2, types[1]);
456 BUILD_TREE(LITLEN, types[2]);
457 BUILD_TREE(OFFSET, types[3]);
458 BUILD_TREE(LITERAL, types[4]);
459
460 while (!lzh->input_end) {
461 if (lit_run) READ_HUFFSYM_SAFE(MATCHLEN2, len);
462 else READ_HUFFSYM_SAFE(MATCHLEN1, len);
463
464 if (len > 0) {
465 len += 2;
466 lit_run = 0; /* not the end of a literal run */
467 READ_HUFFSYM_SAFE(OFFSET, j); offset = j << 6;
468 READ_BITS_SAFE(j, 6); offset |= j;
469
470 /* copy match as output and into the ring buffer */
471 while (len-- > 0) {
472 lzh->window[pos] = lzh->window[(pos+4096-offset) & 4095];
473 WRITE_BYTE;
474 pos++; pos &= 4095;
475 }
476 }
477 else {
478 READ_HUFFSYM_SAFE(LITLEN, len); len++;
479 lit_run = (len == 32) ? 0 : 1; /* end of a literal run? */
480 while (len-- > 0) {
481 READ_HUFFSYM_SAFE(LITERAL, j);
482 /* copy as output and into the ring buffer */
483 lzh->window[pos] = j;
484 WRITE_BYTE;
485 pos++; pos &= 4095;
486 }
487 }
488 }
489 return MSPACK_ERR_OK;
490}
491
492static void lzh_free(struct kwajd_stream *lzh)
493{
494 struct mspack_system *sys;
495 if (!lzh || !lzh->sys) return;
496 sys = lzh->sys;
497 sys->free(lzh);
498}
499
500static int lzh_read_lens(struct kwajd_stream *lzh,
501 unsigned int type, unsigned int numsyms,
502 unsigned char *lens)
503{
504 register unsigned int bit_buffer;
505 register int bits_left;
506 unsigned char *i_ptr, *i_end;
507 unsigned int i, c, sel;
508 int err;
509
510 RESTORE_BITS;
511 switch (type) {
512 case 0:
513 i = numsyms; c = (i==16)?4: (i==32)?5: (i==64)?6: (i==256)?8 :0;
514 for (i = 0; i < numsyms; i++) lens[i] = c;
515 break;
516
517 case 1:
518 READ_BITS_SAFE(c, 4); lens[0] = c;
519 for (i = 1; i < numsyms; i++) {
520 READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = c;
521 else { READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = ++c;
522 else { READ_BITS_SAFE(c, 4); lens[i] = c; }}
523 }
524 break;
525
526 case 2:
527 READ_BITS_SAFE(c, 4); lens[0] = c;
528 for (i = 1; i < numsyms; i++) {
529 READ_BITS_SAFE(sel, 2);
530 if (sel == 3) READ_BITS_SAFE(c, 4); else c += (char) sel-1;
531 lens[i] = c;
532 }
533 break;
534
535 case 3:
536 for (i = 0; i < numsyms; i++) {
537 READ_BITS_SAFE(c, 4); lens[i] = c;
538 }
539 break;
540 }
541 STORE_BITS;
542 return MSPACK_ERR_OK;
543}
544
545static int lzh_read_input(struct kwajd_stream *lzh) {
546 int read;
547 if (lzh->input_end) {
548 lzh->input_end += 8;
549 lzh->inbuf[0] = 0;
550 read = 1;
551 }
552 else {
553 read = lzh->sys->read(lzh->input, &lzh->inbuf[0], KWAJ_INPUT_SIZE);
554 if (read < 0) return MSPACK_ERR_READ;
555 if (read == 0) {
556 lzh->input_end = 8;
557 lzh->inbuf[0] = 0;
558 read = 1;
559 }
560 }
561
562 /* update i_ptr and i_end */
563 lzh->i_ptr = &lzh->inbuf[0];
564 lzh->i_end = &lzh->inbuf[read];
565 return MSPACK_ERR_OK;
566}
diff --git a/utils/rbutilqt/mspack/lit.h b/utils/rbutilqt/mspack/lit.h
new file mode 100644
index 0000000000..2ccc7dd7f3
--- /dev/null
+++ b/utils/rbutilqt/mspack/lit.h
@@ -0,0 +1,35 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifndef MSPACK_LIT_H
11#define MSPACK_LIT_H 1
12
13#include "lzx.h"
14#include "des.h"
15#include "sha.h"
16
17/* generic LIT definitions */
18
19/* LIT compression definitions */
20
21struct mslit_compressor_p {
22 struct mslit_compressor base;
23 struct mspack_system *system;
24 /* todo */
25};
26
27/* LIT decompression definitions */
28
29struct mslit_decompressor_p {
30 struct mslit_decompressor base;
31 struct mspack_system *system;
32 /* todo */
33};
34
35#endif
diff --git a/utils/rbutilqt/mspack/litc.c b/utils/rbutilqt/mspack/litc.c
new file mode 100644
index 0000000000..4959a81d36
--- /dev/null
+++ b/utils/rbutilqt/mspack/litc.c
@@ -0,0 +1,24 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10/* LIT compression implementation */
11
12#include "system-mspack.h"
13#include "lit.h"
14
15struct mslit_compressor *
16 mspack_create_lit_compressor(struct mspack_system *sys)
17{
18 /* todo */
19 return NULL;
20}
21
22void mspack_destroy_lit_compressor(struct mslit_compressor *self) {
23 /* todo */
24}
diff --git a/utils/rbutilqt/mspack/litd.c b/utils/rbutilqt/mspack/litd.c
new file mode 100644
index 0000000000..93cbf1765a
--- /dev/null
+++ b/utils/rbutilqt/mspack/litd.c
@@ -0,0 +1,24 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10/* LIT decompression implementation */
11
12#include "system-mspack.h"
13#include "lit.h"
14
15struct mslit_decompressor *
16 mspack_create_lit_decompressor(struct mspack_system *sys)
17{
18 /* todo */
19 return NULL;
20}
21
22void mspack_destroy_lit_decompressor(struct mslit_decompressor *self) {
23 /* todo */
24}
diff --git a/utils/rbutilqt/mspack/lzss.h b/utils/rbutilqt/mspack/lzss.h
new file mode 100644
index 0000000000..aa946e52ae
--- /dev/null
+++ b/utils/rbutilqt/mspack/lzss.h
@@ -0,0 +1,66 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifndef MSPACK_LZSS_H
11#define MSPACK_LZSS_H 1
12
13#ifdef __cplusplus
14extern "C" {
15#endif
16
17/* LZSS compression / decompression definitions */
18
19#define LZSS_WINDOW_SIZE (4096)
20#define LZSS_WINDOW_FILL (0x20)
21
22#define LZSS_MODE_EXPAND (0)
23#define LZSS_MODE_MSHELP (1)
24#define LZSS_MODE_QBASIC (2)
25
26/**
27 * Decompresses an LZSS stream.
28 *
29 * Input bytes will be read in as necessary using the system->read()
30 * function with the input file handle given. This will continue until
31 * system->read() returns 0 bytes, or an error. Errors will be passed
32 * out of the function as MSPACK_ERR_READ errors. Input streams should
33 * convey an "end of input stream" by refusing to supply all the bytes
34 * that LZSS asks for when they reach the end of the stream, rather
35 * than return an error code.
36 *
37 * Output bytes will be passed to the system->write() function, using
38 * the output file handle given. More than one call may be made to
39 * system->write().
40 *
41 * As EXPAND.EXE (SZDD/KWAJ), Microsoft Help and QBasic have slightly
42 * different encodings for the control byte and matches, a "mode"
43 * parameter is allowed, to choose the encoding.
44 *
45 * @param system an mspack_system structure used to read from
46 * the input stream and write to the output
47 * stream, also to allocate and free memory.
48 * @param input an input stream with the LZSS data.
49 * @param output an output stream to write the decoded data to.
50 * @param input_buffer_size the number of bytes to use as an input
51 * bitstream buffer.
52 * @param mode one of #LZSS_MODE_EXPAND, #LZSS_MODE_MSHELP or
53 * #LZSS_MODE_QBASIC
54 * @return an error code, or MSPACK_ERR_OK if successful
55 */
56extern int lzss_decompress(struct mspack_system *system,
57 struct mspack_file *input,
58 struct mspack_file *output,
59 int input_buffer_size,
60 int mode);
61
62#ifdef __cplusplus
63}
64#endif
65
66#endif
diff --git a/utils/rbutilqt/mspack/lzssd.c b/utils/rbutilqt/mspack/lzssd.c
new file mode 100644
index 0000000000..63716d414a
--- /dev/null
+++ b/utils/rbutilqt/mspack/lzssd.c
@@ -0,0 +1,93 @@
1/* This file is part of libmspack.
2 * (C) 2003-2010 Stuart Caie.
3 *
4 * LZSS is a derivative of LZ77 and was created by James Storer and
5 * Thomas Szymanski in 1982. Haruhiko Okumura wrote a very popular C
6 * implementation.
7 *
8 * libmspack is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
10 *
11 * For further details, see the file COPYING.LIB distributed with libmspack
12 */
13
14#include "system-mspack.h"
15#include "lzss.h"
16
17#define ENSURE_BYTES do { \
18 if (i_ptr >= i_end) { \
19 read = system->read(input, &inbuf[0], \
20 input_buffer_size); \
21 if (read <= 0) { \
22 system->free(window); \
23 return (read < 0) ? MSPACK_ERR_READ \
24 : MSPACK_ERR_OK; \
25 } \
26 i_ptr = &inbuf[0]; i_end = &inbuf[read]; \
27 } \
28} while (0)
29
30#define WRITE_BYTE do { \
31 if (system->write(output, &window[pos], 1) != 1) { \
32 system->free(window); \
33 return MSPACK_ERR_WRITE; \
34 } \
35} while (0)
36
37int lzss_decompress(struct mspack_system *system,
38 struct mspack_file *input,
39 struct mspack_file *output,
40 int input_buffer_size,
41 int mode)
42{
43 unsigned char *window, *inbuf, *i_ptr, *i_end;
44 unsigned int pos, i, c, invert, mpos, len;
45 int read;
46
47 /* check parameters */
48 if (!system || input_buffer_size < 1 || (mode != LZSS_MODE_EXPAND &&
49 mode != LZSS_MODE_MSHELP && mode != LZSS_MODE_QBASIC))
50 {
51 return MSPACK_ERR_ARGS;
52 }
53
54 /* allocate memory */
55 window = (unsigned char *) system->alloc(system, LZSS_WINDOW_SIZE + input_buffer_size);
56 if (!window) return MSPACK_ERR_NOMEMORY;
57
58 /* initialise decompression */
59 inbuf = &window[LZSS_WINDOW_SIZE];
60 memset(window, LZSS_WINDOW_FILL, (size_t) LZSS_WINDOW_SIZE);
61 pos = LZSS_WINDOW_SIZE - ((mode == LZSS_MODE_QBASIC) ? 18 : 16);
62 invert = (mode == LZSS_MODE_MSHELP) ? ~0 : 0;
63 i_ptr = i_end = &inbuf[0];
64
65 /* loop forever; exit condition is in ENSURE_BYTES macro */
66 for (;;) {
67 ENSURE_BYTES; c = *i_ptr++ ^ invert;
68 for (i = 0x01; i & 0xFF; i <<= 1) {
69 if (c & i) {
70 /* literal */
71 ENSURE_BYTES; window[pos] = *i_ptr++;
72 WRITE_BYTE;
73 pos++; pos &= LZSS_WINDOW_SIZE - 1;
74 }
75 else {
76 /* match */
77 ENSURE_BYTES; mpos = *i_ptr++;
78 ENSURE_BYTES; mpos |= (*i_ptr & 0xF0) << 4;
79 len = (*i_ptr++ & 0x0F) + 3;
80 while (len--) {
81 window[pos] = window[mpos];
82 WRITE_BYTE;
83 pos++; pos &= LZSS_WINDOW_SIZE - 1;
84 mpos++; mpos &= LZSS_WINDOW_SIZE - 1;
85 }
86 }
87 }
88 }
89
90 /* not reached */
91 system->free(window);
92 return MSPACK_ERR_OK;
93}
diff --git a/utils/rbutilqt/mspack/lzx.h b/utils/rbutilqt/mspack/lzx.h
new file mode 100644
index 0000000000..a6152f622b
--- /dev/null
+++ b/utils/rbutilqt/mspack/lzx.h
@@ -0,0 +1,221 @@
1/* This file is part of libmspack.
2 * (C) 2003-2013 Stuart Caie.
3 *
4 * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted
5 * by Microsoft Corporation.
6 *
7 * libmspack is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
9 *
10 * For further details, see the file COPYING.LIB distributed with libmspack
11 */
12
13#ifndef MSPACK_LZX_H
14#define MSPACK_LZX_H 1
15
16#ifdef __cplusplus
17extern "C" {
18#endif
19
20/* LZX compression / decompression definitions */
21
22/* some constants defined by the LZX specification */
23#define LZX_MIN_MATCH (2)
24#define LZX_MAX_MATCH (257)
25#define LZX_NUM_CHARS (256)
26#define LZX_BLOCKTYPE_INVALID (0) /* also blocktypes 4-7 invalid */
27#define LZX_BLOCKTYPE_VERBATIM (1)
28#define LZX_BLOCKTYPE_ALIGNED (2)
29#define LZX_BLOCKTYPE_UNCOMPRESSED (3)
30#define LZX_PRETREE_NUM_ELEMENTS (20)
31#define LZX_ALIGNED_NUM_ELEMENTS (8) /* aligned offset tree #elements */
32#define LZX_NUM_PRIMARY_LENGTHS (7) /* this one missing from spec! */
33#define LZX_NUM_SECONDARY_LENGTHS (249) /* length tree #elements */
34
35/* LZX huffman defines: tweak tablebits as desired */
36#define LZX_PRETREE_MAXSYMBOLS (LZX_PRETREE_NUM_ELEMENTS)
37#define LZX_PRETREE_TABLEBITS (6)
38#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 290*8)
39#define LZX_MAINTREE_TABLEBITS (12)
40#define LZX_LENGTH_MAXSYMBOLS (LZX_NUM_SECONDARY_LENGTHS+1)
41#define LZX_LENGTH_TABLEBITS (12)
42#define LZX_ALIGNED_MAXSYMBOLS (LZX_ALIGNED_NUM_ELEMENTS)
43#define LZX_ALIGNED_TABLEBITS (7)
44#define LZX_LENTABLE_SAFETY (64) /* table decoding overruns are allowed */
45
46#define LZX_FRAME_SIZE (32768) /* the size of a frame in LZX */
47
48struct lzxd_stream {
49 struct mspack_system *sys; /* I/O routines */
50 struct mspack_file *input; /* input file handle */
51 struct mspack_file *output; /* output file handle */
52
53 off_t offset; /* number of bytes actually output */
54 off_t length; /* overall decompressed length of stream */
55
56 unsigned char *window; /* decoding window */
57 unsigned int window_size; /* window size */
58 unsigned int ref_data_size; /* LZX DELTA reference data size */
59 unsigned int num_offsets; /* number of match_offset entries in table */
60 unsigned int window_posn; /* decompression offset within window */
61 unsigned int frame_posn; /* current frame offset within in window */
62 unsigned int frame; /* the number of 32kb frames processed */
63 unsigned int reset_interval; /* which frame do we reset the compressor? */
64
65 unsigned int R0, R1, R2; /* for the LRU offset system */
66 unsigned int block_length; /* uncompressed length of this LZX block */
67 unsigned int block_remaining; /* uncompressed bytes still left to decode */
68
69 signed int intel_filesize; /* magic header value used for transform */
70 signed int intel_curpos; /* current offset in transform space */
71
72 unsigned char intel_started; /* has intel E8 decoding started? */
73 unsigned char block_type; /* type of the current block */
74 unsigned char header_read; /* have we started decoding at all yet? */
75 unsigned char input_end; /* have we reached the end of input? */
76 unsigned char is_delta; /* does stream follow LZX DELTA spec? */
77
78 int error;
79
80 /* I/O buffering */
81 unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end;
82 unsigned int bit_buffer, bits_left, inbuf_size;
83
84 /* huffman code lengths */
85 unsigned char PRETREE_len [LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
86 unsigned char MAINTREE_len [LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
87 unsigned char LENGTH_len [LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
88 unsigned char ALIGNED_len [LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
89
90 /* huffman decoding tables */
91 unsigned short PRETREE_table [(1 << LZX_PRETREE_TABLEBITS) +
92 (LZX_PRETREE_MAXSYMBOLS * 2)];
93 unsigned short MAINTREE_table[(1 << LZX_MAINTREE_TABLEBITS) +
94 (LZX_MAINTREE_MAXSYMBOLS * 2)];
95 unsigned short LENGTH_table [(1 << LZX_LENGTH_TABLEBITS) +
96 (LZX_LENGTH_MAXSYMBOLS * 2)];
97 unsigned short ALIGNED_table [(1 << LZX_ALIGNED_TABLEBITS) +
98 (LZX_ALIGNED_MAXSYMBOLS * 2)];
99 unsigned char LENGTH_empty;
100
101 /* this is used purely for doing the intel E8 transform */
102 unsigned char e8_buf[LZX_FRAME_SIZE];
103};
104
105/**
106 * Allocates and initialises LZX decompression state for decoding an LZX
107 * stream.
108 *
109 * This routine uses system->alloc() to allocate memory. If memory
110 * allocation fails, or the parameters to this function are invalid,
111 * NULL is returned.
112 *
113 * @param system an mspack_system structure used to read from
114 * the input stream and write to the output
115 * stream, also to allocate and free memory.
116 * @param input an input stream with the LZX data.
117 * @param output an output stream to write the decoded data to.
118 * @param window_bits the size of the decoding window, which must be
119 * between 15 and 21 inclusive for regular LZX
120 * data, or between 17 and 25 inclusive for
121 * LZX DELTA data.
122 * @param reset_interval the interval at which the LZX bitstream is
123 * reset, in multiples of LZX frames (32678
124 * bytes), e.g. a value of 2 indicates the input
125 * stream resets after every 65536 output bytes.
126 * A value of 0 indicates that the bitstream never
127 * resets, such as in CAB LZX streams.
128 * @param input_buffer_size the number of bytes to use as an input
129 * bitstream buffer.
130 * @param output_length the length in bytes of the entirely
131 * decompressed output stream, if known in
132 * advance. It is used to correctly perform the
133 * Intel E8 transformation, which must stop 6
134 * bytes before the very end of the
135 * decompressed stream. It is not otherwise used
136 * or adhered to. If the full decompressed
137 * length is known in advance, set it here.
138 * If it is NOT known, use the value 0, and call
139 * lzxd_set_output_length() once it is
140 * known. If never set, 4 of the final 6 bytes
141 * of the output stream may be incorrect.
142 * @param is_delta should be zero for all regular LZX data,
143 * non-zero for LZX DELTA encoded data.
144 * @return a pointer to an initialised lzxd_stream structure, or NULL if
145 * there was not enough memory or parameters to the function were wrong.
146 */
147extern struct lzxd_stream *lzxd_init(struct mspack_system *system,
148 struct mspack_file *input,
149 struct mspack_file *output,
150 int window_bits,
151 int reset_interval,
152 int input_buffer_size,
153 off_t output_length,
154 char is_delta);
155
156/* see description of output_length in lzxd_init() */
157extern void lzxd_set_output_length(struct lzxd_stream *lzx,
158 off_t output_length);
159
160/**
161 * Reads LZX DELTA reference data into the window and allows
162 * lzxd_decompress() to reference it.
163 *
164 * Call this before the first call to lzxd_decompress().
165
166 * @param lzx the LZX stream to apply this reference data to
167 * @param system an mspack_system implementation to use with the
168 * input param. Only read() will be called.
169 * @param input an input file handle to read reference data using
170 * system->read().
171 * @param length the length of the reference data. Cannot be longer
172 * than the LZX window size.
173 * @return an error code, or MSPACK_ERR_OK if successful
174 */
175extern int lzxd_set_reference_data(struct lzxd_stream *lzx,
176 struct mspack_system *system,
177 struct mspack_file *input,
178 unsigned int length);
179
180/**
181 * Decompresses entire or partial LZX streams.
182 *
183 * The number of bytes of data that should be decompressed is given as the
184 * out_bytes parameter. If more bytes are decoded than are needed, they
185 * will be kept over for a later invocation.
186 *
187 * The output bytes will be passed to the system->write() function given in
188 * lzxd_init(), using the output file handle given in lzxd_init(). More than
189 * one call may be made to system->write().
190
191 * Input bytes will be read in as necessary using the system->read()
192 * function given in lzxd_init(), using the input file handle given in
193 * lzxd_init(). This will continue until system->read() returns 0 bytes,
194 * or an error. Errors will be passed out of the function as
195 * MSPACK_ERR_READ errors. Input streams should convey an "end of input
196 * stream" by refusing to supply all the bytes that LZX asks for when they
197 * reach the end of the stream, rather than return an error code.
198 *
199 * If any error code other than MSPACK_ERR_OK is returned, the stream
200 * should be considered unusable and lzxd_decompress() should not be
201 * called again on this stream.
202 *
203 * @param lzx LZX decompression state, as allocated by lzxd_init().
204 * @param out_bytes the number of bytes of data to decompress.
205 * @return an error code, or MSPACK_ERR_OK if successful
206 */
207extern int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes);
208
209/**
210 * Frees all state associated with an LZX data stream. This will call
211 * system->free() using the system pointer given in lzxd_init().
212 *
213 * @param lzx LZX decompression state to free.
214 */
215void lzxd_free(struct lzxd_stream *lzx);
216
217#ifdef __cplusplus
218}
219#endif
220
221#endif
diff --git a/utils/rbutilqt/mspack/lzxc.c b/utils/rbutilqt/mspack/lzxc.c
new file mode 100644
index 0000000000..89d8c610ea
--- /dev/null
+++ b/utils/rbutilqt/mspack/lzxc.c
@@ -0,0 +1,18 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted
5 * by Microsoft Corporation.
6 *
7 * libmspack is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
9 *
10 * For further details, see the file COPYING.LIB distributed with libmspack
11 */
12
13/* LZX compression implementation */
14
15#include "system-mspack.h"
16#include "lzx.h"
17
18/* todo */
diff --git a/utils/rbutilqt/mspack/lzxd.c b/utils/rbutilqt/mspack/lzxd.c
new file mode 100644
index 0000000000..88cfd90c2a
--- /dev/null
+++ b/utils/rbutilqt/mspack/lzxd.c
@@ -0,0 +1,905 @@
1/* This file is part of libmspack.
2 * (C) 2003-2013 Stuart Caie.
3 *
4 * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted
5 * by Microsoft Corporation.
6 *
7 * libmspack is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
9 *
10 * For further details, see the file COPYING.LIB distributed with libmspack
11 */
12
13/* LZX decompression implementation */
14
15#include "system-mspack.h"
16#include "lzx.h"
17
18/* Microsoft's LZX document (in cab-sdk.exe) and their implementation
19 * of the com.ms.util.cab Java package do not concur.
20 *
21 * In the LZX document, there is a table showing the correlation between
22 * window size and the number of position slots. It states that the 1MB
23 * window = 40 slots and the 2MB window = 42 slots. In the implementation,
24 * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the
25 * first slot whose position base is equal to or more than the required
26 * window size'. This would explain why other tables in the document refer
27 * to 50 slots rather than 42.
28 *
29 * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode
30 * is not defined in the specification.
31 *
32 * The LZX document does not state the uncompressed block has an
33 * uncompressed length field. Where does this length field come from, so
34 * we can know how large the block is? The implementation has it as the 24
35 * bits following after the 3 blocktype bits, before the alignment
36 * padding.
37 *
38 * The LZX document states that aligned offset blocks have their aligned
39 * offset huffman tree AFTER the main and length trees. The implementation
40 * suggests that the aligned offset tree is BEFORE the main and length
41 * trees.
42 *
43 * The LZX document decoding algorithm states that, in an aligned offset
44 * block, if an extra_bits value is 1, 2 or 3, then that number of bits
45 * should be read and the result added to the match offset. This is
46 * correct for 1 and 2, but not 3, where just a huffman symbol (using the
47 * aligned tree) should be read.
48 *
49 * Regarding the E8 preprocessing, the LZX document states 'No translation
50 * may be performed on the last 6 bytes of the input block'. This is
51 * correct. However, the pseudocode provided checks for the *E8 leader*
52 * up to the last 6 bytes. If the leader appears between -10 and -7 bytes
53 * from the end, this would cause the next four bytes to be modified, at
54 * least one of which would be in the last 6 bytes, which is not allowed
55 * according to the spec.
56 *
57 * The specification states that the huffman trees must always contain at
58 * least one element. However, many CAB files contain blocks where the
59 * length tree is completely empty (because there are no matches), and
60 * this is expected to succeed.
61 *
62 * The errors in LZX documentation appear have been corrected in the
63 * new documentation for the LZX DELTA format.
64 *
65 * http://msdn.microsoft.com/en-us/library/cc483133.aspx
66 *
67 * However, this is a different format, an extension of regular LZX.
68 * I have noticed the following differences, there may be more:
69 *
70 * The maximum window size has increased from 2MB to 32MB. This also
71 * increases the maximum number of position slots, etc.
72 *
73 * If the match length is 257 (the maximum possible), this signals
74 * a further length decoding step, that allows for matches up to
75 * 33024 bytes long.
76 *
77 * The format now allows for "reference data", supplied by the caller.
78 * If match offsets go further back than the number of bytes
79 * decompressed so far, that is them accessing the reference data.
80 */
81
82/* import bit-reading macros and code */
83#define BITS_TYPE struct lzxd_stream
84#define BITS_VAR lzx
85#define BITS_ORDER_MSB
86#define READ_BYTES do { \
87 unsigned char b0, b1; \
88 READ_IF_NEEDED; b0 = *i_ptr++; \
89 READ_IF_NEEDED; b1 = *i_ptr++; \
90 INJECT_BITS((b1 << 8) | b0, 16); \
91} while (0)
92#include "readbits.h"
93
94/* import huffman-reading macros and code */
95#define TABLEBITS(tbl) LZX_##tbl##_TABLEBITS
96#define MAXSYMBOLS(tbl) LZX_##tbl##_MAXSYMBOLS
97#define HUFF_TABLE(tbl,idx) lzx->tbl##_table[idx]
98#define HUFF_LEN(tbl,idx) lzx->tbl##_len[idx]
99#define HUFF_ERROR return lzx->error = MSPACK_ERR_DECRUNCH
100#include "readhuff.h"
101
102/* BUILD_TABLE(tbl) builds a huffman lookup table from code lengths */
103#define BUILD_TABLE(tbl) \
104 if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \
105 &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \
106 { \
107 D(("failed to build %s table", #tbl)) \
108 return lzx->error = MSPACK_ERR_DECRUNCH; \
109 }
110
111#define BUILD_TABLE_MAYBE_EMPTY(tbl) do { \
112 lzx->tbl##_empty = 0; \
113 if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \
114 &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \
115 { \
116 for (i = 0; i < MAXSYMBOLS(tbl); i++) { \
117 if (HUFF_LEN(tbl, i) > 0) { \
118 D(("failed to build %s table", #tbl)) \
119 return lzx->error = MSPACK_ERR_DECRUNCH; \
120 } \
121 } \
122 /* empty tree - allow it, but don't decode symbols with it */ \
123 lzx->tbl##_empty = 1; \
124 } \
125} while (0)
126
127/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols
128 * first to last in the given table. The code lengths are stored in their
129 * own special LZX way.
130 */
131#define READ_LENGTHS(tbl, first, last) do { \
132 STORE_BITS; \
133 if (lzxd_read_lens(lzx, &HUFF_LEN(tbl, 0), (first), \
134 (unsigned int)(last))) return lzx->error; \
135 RESTORE_BITS; \
136} while (0)
137
138static int lzxd_read_lens(struct lzxd_stream *lzx, unsigned char *lens,
139 unsigned int first, unsigned int last)
140{
141 /* bit buffer and huffman symbol decode variables */
142 register unsigned int bit_buffer;
143 register int bits_left, i;
144 register unsigned short sym;
145 unsigned char *i_ptr, *i_end;
146
147 unsigned int x, y;
148 int z;
149
150 RESTORE_BITS;
151
152 /* read lengths for pretree (20 symbols, lengths stored in fixed 4 bits) */
153 for (x = 0; x < 20; x++) {
154 READ_BITS(y, 4);
155 lzx->PRETREE_len[x] = y;
156 }
157 BUILD_TABLE(PRETREE);
158
159 for (x = first; x < last; ) {
160 READ_HUFFSYM(PRETREE, z);
161 if (z == 17) {
162 /* code = 17, run of ([read 4 bits]+4) zeros */
163 READ_BITS(y, 4); y += 4;
164 while (y--) lens[x++] = 0;
165 }
166 else if (z == 18) {
167 /* code = 18, run of ([read 5 bits]+20) zeros */
168 READ_BITS(y, 5); y += 20;
169 while (y--) lens[x++] = 0;
170 }
171 else if (z == 19) {
172 /* code = 19, run of ([read 1 bit]+4) [read huffman symbol] */
173 READ_BITS(y, 1); y += 4;
174 READ_HUFFSYM(PRETREE, z);
175 z = lens[x] - z; if (z < 0) z += 17;
176 while (y--) lens[x++] = z;
177 }
178 else {
179 /* code = 0 to 16, delta current length entry */
180 z = lens[x] - z; if (z < 0) z += 17;
181 lens[x++] = z;
182 }
183 }
184
185 STORE_BITS;
186
187 return MSPACK_ERR_OK;
188}
189
190/* LZX static data tables:
191 *
192 * LZX uses 'position slots' to represent match offsets. For every match,
193 * a small 'position slot' number and a small offset from that slot are
194 * encoded instead of one large offset.
195 *
196 * The number of slots is decided by how many are needed to encode the
197 * largest offset for a given window size. This is easy when the gap between
198 * slots is less than 128Kb, it's a linear relationship. But when extra_bits
199 * reaches its limit of 17 (because LZX can only ensure reading 17 bits of
200 * data at a time), we can only jump 128Kb at a time and have to start
201 * using more and more position slots as each window size doubles.
202 *
203 * position_base[] is an index to the position slot bases
204 *
205 * extra_bits[] states how many bits of offset-from-base data is needed.
206 *
207 * They are calculated as follows:
208 * extra_bits[i] = 0 where i < 4
209 * extra_bits[i] = floor(i/2)-1 where i >= 4 && i < 36
210 * extra_bits[i] = 17 where i >= 36
211 * position_base[0] = 0
212 * position_base[i] = position_base[i-1] + (1 << extra_bits[i-1])
213 */
214static const unsigned int position_slots[11] = {
215 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290
216};
217static const unsigned char extra_bits[36] = {
218 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
219 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16
220};
221static const unsigned int position_base[290] = {
222 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512,
223 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768,
224 49152, 65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360,
225 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936,
226 1835008, 1966080, 2097152, 2228224, 2359296, 2490368, 2621440, 2752512,
227 2883584, 3014656, 3145728, 3276800, 3407872, 3538944, 3670016, 3801088,
228 3932160, 4063232, 4194304, 4325376, 4456448, 4587520, 4718592, 4849664,
229 4980736, 5111808, 5242880, 5373952, 5505024, 5636096, 5767168, 5898240,
230 6029312, 6160384, 6291456, 6422528, 6553600, 6684672, 6815744, 6946816,
231 7077888, 7208960, 7340032, 7471104, 7602176, 7733248, 7864320, 7995392,
232 8126464, 8257536, 8388608, 8519680, 8650752, 8781824, 8912896, 9043968,
233 9175040, 9306112, 9437184, 9568256, 9699328, 9830400, 9961472, 10092544,
234 10223616, 10354688, 10485760, 10616832, 10747904, 10878976, 11010048,
235 11141120, 11272192, 11403264, 11534336, 11665408, 11796480, 11927552,
236 12058624, 12189696, 12320768, 12451840, 12582912, 12713984, 12845056,
237 12976128, 13107200, 13238272, 13369344, 13500416, 13631488, 13762560,
238 13893632, 14024704, 14155776, 14286848, 14417920, 14548992, 14680064,
239 14811136, 14942208, 15073280, 15204352, 15335424, 15466496, 15597568,
240 15728640, 15859712, 15990784, 16121856, 16252928, 16384000, 16515072,
241 16646144, 16777216, 16908288, 17039360, 17170432, 17301504, 17432576,
242 17563648, 17694720, 17825792, 17956864, 18087936, 18219008, 18350080,
243 18481152, 18612224, 18743296, 18874368, 19005440, 19136512, 19267584,
244 19398656, 19529728, 19660800, 19791872, 19922944, 20054016, 20185088,
245 20316160, 20447232, 20578304, 20709376, 20840448, 20971520, 21102592,
246 21233664, 21364736, 21495808, 21626880, 21757952, 21889024, 22020096,
247 22151168, 22282240, 22413312, 22544384, 22675456, 22806528, 22937600,
248 23068672, 23199744, 23330816, 23461888, 23592960, 23724032, 23855104,
249 23986176, 24117248, 24248320, 24379392, 24510464, 24641536, 24772608,
250 24903680, 25034752, 25165824, 25296896, 25427968, 25559040, 25690112,
251 25821184, 25952256, 26083328, 26214400, 26345472, 26476544, 26607616,
252 26738688, 26869760, 27000832, 27131904, 27262976, 27394048, 27525120,
253 27656192, 27787264, 27918336, 28049408, 28180480, 28311552, 28442624,
254 28573696, 28704768, 28835840, 28966912, 29097984, 29229056, 29360128,
255 29491200, 29622272, 29753344, 29884416, 30015488, 30146560, 30277632,
256 30408704, 30539776, 30670848, 30801920, 30932992, 31064064, 31195136,
257 31326208, 31457280, 31588352, 31719424, 31850496, 31981568, 32112640,
258 32243712, 32374784, 32505856, 32636928, 32768000, 32899072, 33030144,
259 33161216, 33292288, 33423360
260};
261
262static void lzxd_reset_state(struct lzxd_stream *lzx) {
263 int i;
264
265 lzx->R0 = 1;
266 lzx->R1 = 1;
267 lzx->R2 = 1;
268 lzx->header_read = 0;
269 lzx->block_remaining = 0;
270 lzx->block_type = LZX_BLOCKTYPE_INVALID;
271
272 /* initialise tables to 0 (because deltas will be applied to them) */
273 for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) lzx->MAINTREE_len[i] = 0;
274 for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) lzx->LENGTH_len[i] = 0;
275}
276
277/*-------- main LZX code --------*/
278
279struct lzxd_stream *lzxd_init(struct mspack_system *system,
280 struct mspack_file *input,
281 struct mspack_file *output,
282 int window_bits,
283 int reset_interval,
284 int input_buffer_size,
285 off_t output_length,
286 char is_delta)
287{
288 unsigned int window_size = 1 << window_bits;
289 struct lzxd_stream *lzx;
290
291 if (!system) return NULL;
292
293 /* LZX DELTA window sizes are between 2^17 (128KiB) and 2^25 (32MiB),
294 * regular LZX windows are between 2^15 (32KiB) and 2^21 (2MiB)
295 */
296 if (is_delta) {
297 if (window_bits < 17 || window_bits > 25) return NULL;
298 }
299 else {
300 if (window_bits < 15 || window_bits > 21) return NULL;
301 }
302
303 if (reset_interval < 0 || output_length < 0) {
304 D(("reset interval or output length < 0"))
305 return NULL;
306 }
307
308 /* round up input buffer size to multiple of two */
309 input_buffer_size = (input_buffer_size + 1) & -2;
310 if (input_buffer_size < 2) return NULL;
311
312 /* allocate decompression state */
313 if (!(lzx = (struct lzxd_stream *) system->alloc(system, sizeof(struct lzxd_stream)))) {
314 return NULL;
315 }
316
317 /* allocate decompression window and input buffer */
318 lzx->window = (unsigned char *) system->alloc(system, (size_t) window_size);
319 lzx->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size);
320 if (!lzx->window || !lzx->inbuf) {
321 system->free(lzx->window);
322 system->free(lzx->inbuf);
323 system->free(lzx);
324 return NULL;
325 }
326
327 /* initialise decompression state */
328 lzx->sys = system;
329 lzx->input = input;
330 lzx->output = output;
331 lzx->offset = 0;
332 lzx->length = output_length;
333
334 lzx->inbuf_size = input_buffer_size;
335 lzx->window_size = 1 << window_bits;
336 lzx->ref_data_size = 0;
337 lzx->window_posn = 0;
338 lzx->frame_posn = 0;
339 lzx->frame = 0;
340 lzx->reset_interval = reset_interval;
341 lzx->intel_filesize = 0;
342 lzx->intel_curpos = 0;
343 lzx->intel_started = 0;
344 lzx->error = MSPACK_ERR_OK;
345 lzx->num_offsets = position_slots[window_bits - 15] << 3;
346 lzx->is_delta = is_delta;
347
348 lzx->o_ptr = lzx->o_end = &lzx->e8_buf[0];
349 lzxd_reset_state(lzx);
350 INIT_BITS;
351 return lzx;
352}
353
354int lzxd_set_reference_data(struct lzxd_stream *lzx,
355 struct mspack_system *system,
356 struct mspack_file *input,
357 unsigned int length)
358{
359 if (!lzx) return MSPACK_ERR_ARGS;
360
361 if (!lzx->is_delta) {
362 D(("only LZX DELTA streams support reference data"))
363 return MSPACK_ERR_ARGS;
364 }
365 if (lzx->offset) {
366 D(("too late to set reference data after decoding starts"))
367 return MSPACK_ERR_ARGS;
368 }
369 if (length > lzx->window_size) {
370 D(("reference length (%u) is longer than the window", length))
371 return MSPACK_ERR_ARGS;
372 }
373 if (length > 0 && (!system || !input)) {
374 D(("length > 0 but no system or input"))
375 return MSPACK_ERR_ARGS;
376 }
377
378 lzx->ref_data_size = length;
379 if (length > 0) {
380 /* copy reference data */
381 unsigned char *pos = &lzx->window[lzx->window_size - length];
382 int bytes = system->read(input, pos, length);
383 /* length can't be more than 2^25, so no signedness problem */
384 if (bytes < (int)length) return MSPACK_ERR_READ;
385 }
386 lzx->ref_data_size = length;
387 return MSPACK_ERR_OK;
388}
389
390void lzxd_set_output_length(struct lzxd_stream *lzx, off_t out_bytes) {
391 if (lzx && out_bytes > 0) lzx->length = out_bytes;
392}
393
394int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) {
395 /* bitstream and huffman reading variables */
396 register unsigned int bit_buffer;
397 register int bits_left, i=0;
398 unsigned char *i_ptr, *i_end;
399 register unsigned short sym;
400
401 int match_length, length_footer, extra, verbatim_bits, bytes_todo;
402 int this_run, main_element, aligned_bits, j, warned = 0;
403 unsigned char *window, *runsrc, *rundest, buf[12];
404 unsigned int frame_size=0, end_frame, match_offset, window_posn;
405 unsigned int R0, R1, R2;
406
407 /* easy answers */
408 if (!lzx || (out_bytes < 0)) return MSPACK_ERR_ARGS;
409 if (lzx->error) return lzx->error;
410
411 /* flush out any stored-up bytes before we begin */
412 i = lzx->o_end - lzx->o_ptr;
413 if ((off_t) i > out_bytes) i = (int) out_bytes;
414 if (i) {
415 if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) {
416 return lzx->error = MSPACK_ERR_WRITE;
417 }
418 lzx->o_ptr += i;
419 lzx->offset += i;
420 out_bytes -= i;
421 }
422 if (out_bytes == 0) return MSPACK_ERR_OK;
423
424 /* restore local state */
425 RESTORE_BITS;
426 window = lzx->window;
427 window_posn = lzx->window_posn;
428 R0 = lzx->R0;
429 R1 = lzx->R1;
430 R2 = lzx->R2;
431
432 end_frame = (unsigned int)((lzx->offset + out_bytes) / LZX_FRAME_SIZE) + 1;
433
434 while (lzx->frame < end_frame) {
435 /* have we reached the reset interval? (if there is one?) */
436 if (lzx->reset_interval && ((lzx->frame % lzx->reset_interval) == 0)) {
437 if (lzx->block_remaining) {
438 /* this is a file format error, we can make a best effort to extract what we can */
439 D(("%d bytes remaining at reset interval", lzx->block_remaining))
440 if (!warned) {
441 lzx->sys->message(NULL, "WARNING; invalid reset interval detected during LZX decompression");
442 warned++;
443 }
444 }
445
446 /* re-read the intel header and reset the huffman lengths */
447 lzxd_reset_state(lzx);
448 R0 = lzx->R0;
449 R1 = lzx->R1;
450 R2 = lzx->R2;
451 }
452
453 /* LZX DELTA format has chunk_size, not present in LZX format */
454 if (lzx->is_delta) {
455 ENSURE_BITS(16);
456 REMOVE_BITS(16);
457 }
458
459 /* read header if necessary */
460 if (!lzx->header_read) {
461 /* read 1 bit. if bit=0, intel filesize = 0.
462 * if bit=1, read intel filesize (32 bits) */
463 j = 0; READ_BITS(i, 1); if (i) { READ_BITS(i, 16); READ_BITS(j, 16); }
464 lzx->intel_filesize = (i << 16) | j;
465 lzx->header_read = 1;
466 }
467
468 /* calculate size of frame: all frames are 32k except the final frame
469 * which is 32kb or less. this can only be calculated when lzx->length
470 * has been filled in. */
471 frame_size = LZX_FRAME_SIZE;
472 if (lzx->length && (lzx->length - lzx->offset) < (off_t)frame_size) {
473 frame_size = lzx->length - lzx->offset;
474 }
475
476 /* decode until one more frame is available */
477 bytes_todo = lzx->frame_posn + frame_size - window_posn;
478 while (bytes_todo > 0) {
479 /* initialise new block, if one is needed */
480 if (lzx->block_remaining == 0) {
481 /* realign if previous block was an odd-sized UNCOMPRESSED block */
482 if ((lzx->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) &&
483 (lzx->block_length & 1))
484 {
485 READ_IF_NEEDED;
486 i_ptr++;
487 }
488
489 /* read block type (3 bits) and block length (24 bits) */
490 READ_BITS(lzx->block_type, 3);
491 READ_BITS(i, 16); READ_BITS(j, 8);
492 lzx->block_remaining = lzx->block_length = (i << 8) | j;
493 /*D(("new block t%d len %u", lzx->block_type, lzx->block_length))*/
494
495 /* read individual block headers */
496 switch (lzx->block_type) {
497 case LZX_BLOCKTYPE_ALIGNED:
498 /* read lengths of and build aligned huffman decoding tree */
499 for (i = 0; i < 8; i++) { READ_BITS(j, 3); lzx->ALIGNED_len[i] = j; }
500 BUILD_TABLE(ALIGNED);
501 /* rest of aligned header is same as verbatim */ /*@fallthrough@*/
502 case LZX_BLOCKTYPE_VERBATIM:
503 /* read lengths of and build main huffman decoding tree */
504 READ_LENGTHS(MAINTREE, 0, 256);
505 READ_LENGTHS(MAINTREE, 256, LZX_NUM_CHARS + lzx->num_offsets);
506 BUILD_TABLE(MAINTREE);
507 /* if the literal 0xE8 is anywhere in the block... */
508 if (lzx->MAINTREE_len[0xE8] != 0) lzx->intel_started = 1;
509 /* read lengths of and build lengths huffman decoding tree */
510 READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS);
511 BUILD_TABLE_MAYBE_EMPTY(LENGTH);
512 break;
513
514 case LZX_BLOCKTYPE_UNCOMPRESSED:
515 /* because we can't assume otherwise */
516 lzx->intel_started = 1;
517
518 /* read 1-16 (not 0-15) bits to align to bytes */
519 if (bits_left == 0) ENSURE_BITS(16);
520 bits_left = 0; bit_buffer = 0;
521
522 /* read 12 bytes of stored R0 / R1 / R2 values */
523 for (rundest = &buf[0], i = 0; i < 12; i++) {
524 READ_IF_NEEDED;
525 *rundest++ = *i_ptr++;
526 }
527 R0 = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
528 R1 = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24);
529 R2 = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24);
530 break;
531
532 default:
533 D(("bad block type"))
534 return lzx->error = MSPACK_ERR_DECRUNCH;
535 }
536 }
537
538 /* decode more of the block:
539 * run = min(what's available, what's needed) */
540 this_run = lzx->block_remaining;
541 if (this_run > bytes_todo) this_run = bytes_todo;
542
543 /* assume we decode exactly this_run bytes, for now */
544 bytes_todo -= this_run;
545 lzx->block_remaining -= this_run;
546
547 /* decode at least this_run bytes */
548 switch (lzx->block_type) {
549 case LZX_BLOCKTYPE_VERBATIM:
550 while (this_run > 0) {
551 READ_HUFFSYM(MAINTREE, main_element);
552 if (main_element < LZX_NUM_CHARS) {
553 /* literal: 0 to LZX_NUM_CHARS-1 */
554 window[window_posn++] = main_element;
555 this_run--;
556 }
557 else {
558 /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
559 main_element -= LZX_NUM_CHARS;
560
561 /* get match length */
562 match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
563 if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
564 if (lzx->LENGTH_empty) {
565 D(("LENGTH symbol needed but tree is empty"))
566 return lzx->error = MSPACK_ERR_DECRUNCH;
567 }
568 READ_HUFFSYM(LENGTH, length_footer);
569 match_length += length_footer;
570 }
571 match_length += LZX_MIN_MATCH;
572
573 /* get match offset */
574 switch ((match_offset = (main_element >> 3))) {
575 case 0: match_offset = R0; break;
576 case 1: match_offset = R1; R1=R0; R0 = match_offset; break;
577 case 2: match_offset = R2; R2=R0; R0 = match_offset; break;
578 case 3: match_offset = 1; R2=R1; R1=R0; R0 = match_offset; break;
579 default:
580 extra = (match_offset >= 36) ? 17 : extra_bits[match_offset];
581 READ_BITS(verbatim_bits, extra);
582 match_offset = position_base[match_offset] - 2 + verbatim_bits;
583 R2 = R1; R1 = R0; R0 = match_offset;
584 }
585
586 /* LZX DELTA uses max match length to signal even longer match */
587 if (match_length == LZX_MAX_MATCH && lzx->is_delta) {
588 int extra_len = 0;
589 ENSURE_BITS(3); /* 4 entry huffman tree */
590 if (PEEK_BITS(1) == 0) {
591 REMOVE_BITS(1); /* '0' -> 8 extra length bits */
592 READ_BITS(extra_len, 8);
593 }
594 else if (PEEK_BITS(2) == 2) {
595 REMOVE_BITS(2); /* '10' -> 10 extra length bits + 0x100 */
596 READ_BITS(extra_len, 10);
597 extra_len += 0x100;
598 }
599 else if (PEEK_BITS(3) == 6) {
600 REMOVE_BITS(3); /* '110' -> 12 extra length bits + 0x500 */
601 READ_BITS(extra_len, 12);
602 extra_len += 0x500;
603 }
604 else {
605 REMOVE_BITS(3); /* '111' -> 15 extra length bits */
606 READ_BITS(extra_len, 15);
607 }
608 match_length += extra_len;
609 }
610
611 if ((window_posn + match_length) > lzx->window_size) {
612 D(("match ran over window wrap"))
613 return lzx->error = MSPACK_ERR_DECRUNCH;
614 }
615
616 /* copy match */
617 rundest = &window[window_posn];
618 i = match_length;
619 /* does match offset wrap the window? */
620 if (match_offset > window_posn) {
621 if (match_offset > lzx->offset &&
622 (match_offset - window_posn) > lzx->ref_data_size)
623 {
624 D(("match offset beyond LZX stream"))
625 return lzx->error = MSPACK_ERR_DECRUNCH;
626 }
627 /* j = length from match offset to end of window */
628 j = match_offset - window_posn;
629 if (j > (int) lzx->window_size) {
630 D(("match offset beyond window boundaries"))
631 return lzx->error = MSPACK_ERR_DECRUNCH;
632 }
633 runsrc = &window[lzx->window_size - j];
634 if (j < i) {
635 /* if match goes over the window edge, do two copy runs */
636 i -= j; while (j-- > 0) *rundest++ = *runsrc++;
637 runsrc = window;
638 }
639 while (i-- > 0) *rundest++ = *runsrc++;
640 }
641 else {
642 runsrc = rundest - match_offset;
643 while (i-- > 0) *rundest++ = *runsrc++;
644 }
645
646 this_run -= match_length;
647 window_posn += match_length;
648 }
649 } /* while (this_run > 0) */
650 break;
651
652 case LZX_BLOCKTYPE_ALIGNED:
653 while (this_run > 0) {
654 READ_HUFFSYM(MAINTREE, main_element);
655 if (main_element < LZX_NUM_CHARS) {
656 /* literal: 0 to LZX_NUM_CHARS-1 */
657 window[window_posn++] = main_element;
658 this_run--;
659 }
660 else {
661 /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
662 main_element -= LZX_NUM_CHARS;
663
664 /* get match length */
665 match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
666 if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
667 if (lzx->LENGTH_empty) {
668 D(("LENGTH symbol needed but tree is empty"))
669 return lzx->error = MSPACK_ERR_DECRUNCH;
670 }
671 READ_HUFFSYM(LENGTH, length_footer);
672 match_length += length_footer;
673 }
674 match_length += LZX_MIN_MATCH;
675
676 /* get match offset */
677 switch ((match_offset = (main_element >> 3))) {
678 case 0: match_offset = R0; break;
679 case 1: match_offset = R1; R1 = R0; R0 = match_offset; break;
680 case 2: match_offset = R2; R2 = R0; R0 = match_offset; break;
681 default:
682 extra = (match_offset >= 36) ? 17 : extra_bits[match_offset];
683 match_offset = position_base[match_offset] - 2;
684 if (extra > 3) {
685 /* verbatim and aligned bits */
686 extra -= 3;
687 READ_BITS(verbatim_bits, extra);
688 match_offset += (verbatim_bits << 3);
689 READ_HUFFSYM(ALIGNED, aligned_bits);
690 match_offset += aligned_bits;
691 }
692 else if (extra == 3) {
693 /* aligned bits only */
694 READ_HUFFSYM(ALIGNED, aligned_bits);
695 match_offset += aligned_bits;
696 }
697 else if (extra > 0) { /* extra==1, extra==2 */
698 /* verbatim bits only */
699 READ_BITS(verbatim_bits, extra);
700 match_offset += verbatim_bits;
701 }
702 else /* extra == 0 */ {
703 /* ??? not defined in LZX specification! */
704 match_offset = 1;
705 }
706 /* update repeated offset LRU queue */
707 R2 = R1; R1 = R0; R0 = match_offset;
708 }
709
710 /* LZX DELTA uses max match length to signal even longer match */
711 if (match_length == LZX_MAX_MATCH && lzx->is_delta) {
712 int extra_len = 0;
713 ENSURE_BITS(3); /* 4 entry huffman tree */
714 if (PEEK_BITS(1) == 0) {
715 REMOVE_BITS(1); /* '0' -> 8 extra length bits */
716 READ_BITS(extra_len, 8);
717 }
718 else if (PEEK_BITS(2) == 2) {
719 REMOVE_BITS(2); /* '10' -> 10 extra length bits + 0x100 */
720 READ_BITS(extra_len, 10);
721 extra_len += 0x100;
722 }
723 else if (PEEK_BITS(3) == 6) {
724 REMOVE_BITS(3); /* '110' -> 12 extra length bits + 0x500 */
725 READ_BITS(extra_len, 12);
726 extra_len += 0x500;
727 }
728 else {
729 REMOVE_BITS(3); /* '111' -> 15 extra length bits */
730 READ_BITS(extra_len, 15);
731 }
732 match_length += extra_len;
733 }
734
735 if ((window_posn + match_length) > lzx->window_size) {
736 D(("match ran over window wrap"))
737 return lzx->error = MSPACK_ERR_DECRUNCH;
738 }
739
740 /* copy match */
741 rundest = &window[window_posn];
742 i = match_length;
743 /* does match offset wrap the window? */
744 if (match_offset > window_posn) {
745 if (match_offset > lzx->offset &&
746 (match_offset - window_posn) > lzx->ref_data_size)
747 {
748 D(("match offset beyond LZX stream"))
749 return lzx->error = MSPACK_ERR_DECRUNCH;
750 }
751 /* j = length from match offset to end of window */
752 j = match_offset - window_posn;
753 if (j > (int) lzx->window_size) {
754 D(("match offset beyond window boundaries"))
755 return lzx->error = MSPACK_ERR_DECRUNCH;
756 }
757 runsrc = &window[lzx->window_size - j];
758 if (j < i) {
759 /* if match goes over the window edge, do two copy runs */
760 i -= j; while (j-- > 0) *rundest++ = *runsrc++;
761 runsrc = window;
762 }
763 while (i-- > 0) *rundest++ = *runsrc++;
764 }
765 else {
766 runsrc = rundest - match_offset;
767 while (i-- > 0) *rundest++ = *runsrc++;
768 }
769
770 this_run -= match_length;
771 window_posn += match_length;
772 }
773 } /* while (this_run > 0) */
774 break;
775
776 case LZX_BLOCKTYPE_UNCOMPRESSED:
777 /* as this_run is limited not to wrap a frame, this also means it
778 * won't wrap the window (as the window is a multiple of 32k) */
779 rundest = &window[window_posn];
780 window_posn += this_run;
781 while (this_run > 0) {
782 if ((i = i_end - i_ptr) == 0) {
783 READ_IF_NEEDED;
784 }
785 else {
786 if (i > this_run) i = this_run;
787 lzx->sys->copy(i_ptr, rundest, (size_t) i);
788 rundest += i;
789 i_ptr += i;
790 this_run -= i;
791 }
792 }
793 break;
794
795 default:
796 return lzx->error = MSPACK_ERR_DECRUNCH; /* might as well */
797 }
798
799 /* did the final match overrun our desired this_run length? */
800 if (this_run < 0) {
801 if ((unsigned int)(-this_run) > lzx->block_remaining) {
802 D(("overrun went past end of block by %d (%d remaining)",
803 -this_run, lzx->block_remaining ))
804 return lzx->error = MSPACK_ERR_DECRUNCH;
805 }
806 lzx->block_remaining -= -this_run;
807 }
808 } /* while (bytes_todo > 0) */
809
810 /* streams don't extend over frame boundaries */
811 if ((window_posn - lzx->frame_posn) != frame_size) {
812 D(("decode beyond output frame limits! %d != %d",
813 window_posn - lzx->frame_posn, frame_size))
814 return lzx->error = MSPACK_ERR_DECRUNCH;
815 }
816
817 /* re-align input bitstream */
818 if (bits_left > 0) ENSURE_BITS(16);
819 if (bits_left & 15) REMOVE_BITS(bits_left & 15);
820
821 /* check that we've used all of the previous frame first */
822 if (lzx->o_ptr != lzx->o_end) {
823 D(("%ld avail bytes, new %d frame",
824 (long)(lzx->o_end - lzx->o_ptr), frame_size))
825 return lzx->error = MSPACK_ERR_DECRUNCH;
826 }
827
828 /* does this intel block _really_ need decoding? */
829 if (lzx->intel_started && lzx->intel_filesize &&
830 (lzx->frame <= 32768) && (frame_size > 10))
831 {
832 unsigned char *data = &lzx->e8_buf[0];
833 unsigned char *dataend = &lzx->e8_buf[frame_size - 10];
834 signed int curpos = lzx->intel_curpos;
835 signed int filesize = lzx->intel_filesize;
836 signed int abs_off, rel_off;
837
838 /* copy e8 block to the e8 buffer and tweak if needed */
839 lzx->o_ptr = data;
840 lzx->sys->copy(&lzx->window[lzx->frame_posn], data, frame_size);
841
842 while (data < dataend) {
843 if (*data++ != 0xE8) { curpos++; continue; }
844 abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
845 if ((abs_off >= -curpos) && (abs_off < filesize)) {
846 rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
847 data[0] = (unsigned char) rel_off;
848 data[1] = (unsigned char) (rel_off >> 8);
849 data[2] = (unsigned char) (rel_off >> 16);
850 data[3] = (unsigned char) (rel_off >> 24);
851 }
852 data += 4;
853 curpos += 5;
854 }
855 lzx->intel_curpos += frame_size;
856 }
857 else {
858 lzx->o_ptr = &lzx->window[lzx->frame_posn];
859 if (lzx->intel_filesize) lzx->intel_curpos += frame_size;
860 }
861 lzx->o_end = &lzx->o_ptr[frame_size];
862
863 /* write a frame */
864 i = (out_bytes < (off_t)frame_size) ? (unsigned int)out_bytes : frame_size;
865 if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) {
866 return lzx->error = MSPACK_ERR_WRITE;
867 }
868 lzx->o_ptr += i;
869 lzx->offset += i;
870 out_bytes -= i;
871
872 /* advance frame start position */
873 lzx->frame_posn += frame_size;
874 lzx->frame++;
875
876 /* wrap window / frame position pointers */
877 if (window_posn == lzx->window_size) window_posn = 0;
878 if (lzx->frame_posn == lzx->window_size) lzx->frame_posn = 0;
879
880 } /* while (lzx->frame < end_frame) */
881
882 if (out_bytes) {
883 D(("bytes left to output"))
884 return lzx->error = MSPACK_ERR_DECRUNCH;
885 }
886
887 /* store local state */
888 STORE_BITS;
889 lzx->window_posn = window_posn;
890 lzx->R0 = R0;
891 lzx->R1 = R1;
892 lzx->R2 = R2;
893
894 return MSPACK_ERR_OK;
895}
896
897void lzxd_free(struct lzxd_stream *lzx) {
898 struct mspack_system *sys;
899 if (lzx) {
900 sys = lzx->sys;
901 sys->free(lzx->inbuf);
902 sys->free(lzx->window);
903 sys->free(lzx);
904 }
905}
diff --git a/utils/rbutilqt/mspack/mspack.h b/utils/rbutilqt/mspack/mspack.h
new file mode 100644
index 0000000000..3e99624463
--- /dev/null
+++ b/utils/rbutilqt/mspack/mspack.h
@@ -0,0 +1,2385 @@
1/* libmspack -- a library for working with Microsoft compression formats.
2 * (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/** \mainpage
18 *
19 * \section intro Introduction
20 *
21 * libmspack is a library which provides compressors and decompressors,
22 * archivers and dearchivers for Microsoft compression formats.
23 *
24 * \section formats Formats supported
25 *
26 * The following file formats are supported:
27 * - SZDD files, which use LZSS compression
28 * - KWAJ files, which use LZSS, LZSS+Huffman or deflate compression
29 * - .HLP (MS Help) files, which use LZSS compression
30 * - .CAB (MS Cabinet) files, which use deflate, LZX or Quantum compression
31 * - .CHM (HTML Help) files, which use LZX compression
32 * - .LIT (MS EBook) files, which use LZX compression and DES encryption
33 * - .LZX (Exchange Offline Addressbook) files, which use LZX compression
34 *
35 * To determine the capabilities of the library, and the binary
36 * compatibility version of any particular compressor or decompressor, use
37 * the mspack_version() function. The UNIX library interface version is
38 * defined as the highest-versioned library component.
39 *
40 * \section starting Getting started
41 *
42 * The macro MSPACK_SYS_SELFTEST() should be used to ensure the library can
43 * be used. In particular, it checks if the caller is using 32-bit file I/O
44 * when the library is compiled for 64-bit file I/O and vice versa.
45 *
46 * If compiled normally, the library includes basic file I/O and memory
47 * management functionality using the standard C library. This can be
48 * customised and replaced entirely by creating a mspack_system structure.
49 *
50 * A compressor or decompressor for the required format must be
51 * instantiated before it can be used. Each construction function takes
52 * one parameter, which is either a pointer to a custom mspack_system
53 * structure, or NULL to use the default. The instantiation returned, if
54 * not NULL, contains function pointers (methods) to work with the given
55 * file format.
56 *
57 * For compression:
58 * - mspack_create_cab_compressor() creates a mscab_compressor
59 * - mspack_create_chm_compressor() creates a mschm_compressor
60 * - mspack_create_lit_compressor() creates a mslit_compressor
61 * - mspack_create_hlp_compressor() creates a mshlp_compressor
62 * - mspack_create_szdd_compressor() creates a msszdd_compressor
63 * - mspack_create_kwaj_compressor() creates a mskwaj_compressor
64 * - mspack_create_oab_compressor() creates a msoab_compressor
65 *
66 * For decompression:
67 * - mspack_create_cab_decompressor() creates a mscab_decompressor
68 * - mspack_create_chm_decompressor() creates a mschm_decompressor
69 * - mspack_create_lit_decompressor() creates a mslit_decompressor
70 * - mspack_create_hlp_decompressor() creates a mshlp_decompressor
71 * - mspack_create_szdd_decompressor() creates a msszdd_decompressor
72 * - mspack_create_kwaj_decompressor() creates a mskwaj_decompressor
73 * - mspack_create_oab_decompressor() creates a msoab_decompressor
74 *
75 * Once finished working with a format, each kind of
76 * compressor/decompressor has its own specific destructor:
77 * - mspack_destroy_cab_compressor()
78 * - mspack_destroy_cab_decompressor()
79 * - mspack_destroy_chm_compressor()
80 * - mspack_destroy_chm_decompressor()
81 * - mspack_destroy_lit_compressor()
82 * - mspack_destroy_lit_decompressor()
83 * - mspack_destroy_hlp_compressor()
84 * - mspack_destroy_hlp_decompressor()
85 * - mspack_destroy_szdd_compressor()
86 * - mspack_destroy_szdd_decompressor()
87 * - mspack_destroy_kwaj_compressor()
88 * - mspack_destroy_kwaj_decompressor()
89 * - mspack_destroy_oab_compressor()
90 * - mspack_destroy_oab_decompressor()
91 *
92 * Destroying a compressor or decompressor does not destroy any objects,
93 * structures or handles that have been created using that compressor or
94 * decompressor. Ensure that everything created or opened is destroyed or
95 * closed before compressor/decompressor is itself destroyed.
96 *
97 * \section errors Error codes
98 *
99 * All compressors and decompressors use the same set of error codes. Most
100 * methods return an error code directly. For methods which do not
101 * return error codes directly, the error code can be obtained with the
102 * last_error() method.
103 *
104 * - #MSPACK_ERR_OK is used to indicate success. This error code is defined
105 * as zero, all other code are non-zero.
106 * - #MSPACK_ERR_ARGS indicates that a method was called with inappropriate
107 * arguments.
108 * - #MSPACK_ERR_OPEN indicates that mspack_system::open() failed.
109 * - #MSPACK_ERR_READ indicates that mspack_system::read() failed.
110 * - #MSPACK_ERR_WRITE indicates that mspack_system::write() failed.
111 * - #MSPACK_ERR_SEEK indicates that mspack_system::seek() failed.
112 * - #MSPACK_ERR_NOMEMORY indicates that mspack_system::alloc() failed.
113 * - #MSPACK_ERR_SIGNATURE indicates that the file being read does not
114 * have the correct "signature". It is probably not a valid file for
115 * whatever format is being read.
116 * - #MSPACK_ERR_DATAFORMAT indicates that the file being used or read
117 * is corrupt.
118 * - #MSPACK_ERR_CHECKSUM indicates that a data checksum has failed.
119 * - #MSPACK_ERR_CRUNCH indicates an error occured during compression.
120 * - #MSPACK_ERR_DECRUNCH indicates an error occured during decompression.
121 *
122 * \section threading Multi-threading
123 *
124 * libmspack methods are reentrant and multithreading-safe when each
125 * thread has its own compressor or decompressor.
126
127 * You should not call multiple methods simultaneously on a single
128 * compressor or decompressor instance.
129 *
130 * If this may happen, you can either use one compressor or
131 * decompressor per thread, or you can use your preferred lock,
132 * semaphore or mutex library to ensure no more than one method on a
133 * compressor/decompressor is called simultaneously. libmspack will
134 * not do this locking for you.
135 *
136 * Example of incorrect behaviour:
137 * - thread 1 calls mspack_create_cab_decompressor()
138 * - thread 1 calls open()
139 * - thread 1 calls extract() for one file
140 * - thread 2 simultaneously calls extract() for another file
141 *
142 * Correct behaviour:
143 * - thread 1 calls mspack_create_cab_decompressor()
144 * - thread 2 calls mspack_create_cab_decompressor()
145 * - thread 1 calls its own open() / extract()
146 * - thread 2 simultaneously calls its own open() / extract()
147 *
148 * Also correct behaviour:
149 * - thread 1 calls mspack_create_cab_decompressor()
150 * - thread 1 locks a mutex for with the decompressor before
151 * calling any methods on it, and unlocks the mutex after each
152 * method returns.
153 * - thread 1 can share the results of open() with thread 2, and both
154 * can call extract(), provided they both guard against simultaneous
155 * use of extract(), and any other methods, with the mutex
156 */
157
158#ifndef LIB_MSPACK_H
159#define LIB_MSPACK_H 1
160
161#ifdef __cplusplus
162extern "C" {
163#endif
164
165#include <sys/types.h>
166#include <stdlib.h>
167
168/**
169 * System self-test function, to ensure both library and calling program
170 * can use one another.
171 *
172 * A result of MSPACK_ERR_OK means the library and caller are
173 * compatible. Any other result indicates that the library and caller are
174 * not compatible and should not be used. In particular, a value of
175 * MSPACK_ERR_SEEK means the library and caller use different off_t
176 * datatypes.
177 *
178 * It should be used like so:
179 *
180 * @code
181 * int selftest_result;
182 * MSPACK_SYS_SELFTEST(selftest_result);
183 * if (selftest_result != MSPACK_ERR_OK) {
184 * fprintf(stderr, "incompatible with this build of libmspack\n");
185 * exit(0);
186 * }
187 * @endcode
188 *
189 * @param result an int variable to store the result of the self-test
190 */
191#define MSPACK_SYS_SELFTEST(result) do { \
192 (result) = mspack_sys_selftest_internal(sizeof(off_t)); \
193} while (0)
194
195/** Part of the MSPACK_SYS_SELFTEST() macro, must not be used directly. */
196extern int mspack_sys_selftest_internal(int);
197
198/**
199 * Enquire about the binary compatibility version of a specific interface in
200 * the library. Currently, the following interfaces are defined:
201 *
202 * - #MSPACK_VER_LIBRARY: the overall library
203 * - #MSPACK_VER_SYSTEM: the mspack_system interface
204 * - #MSPACK_VER_MSCABD: the mscab_decompressor interface
205 * - #MSPACK_VER_MSCABC: the mscab_compressor interface
206 * - #MSPACK_VER_MSCHMD: the mschm_decompressor interface
207 * - #MSPACK_VER_MSCHMC: the mschm_compressor interface
208 * - #MSPACK_VER_MSLITD: the mslit_decompressor interface
209 * - #MSPACK_VER_MSLITC: the mslit_compressor interface
210 * - #MSPACK_VER_MSHLPD: the mshlp_decompressor interface
211 * - #MSPACK_VER_MSHLPC: the mshlp_compressor interface
212 * - #MSPACK_VER_MSSZDDD: the msszdd_decompressor interface
213 * - #MSPACK_VER_MSSZDDC: the msszdd_compressor interface
214 * - #MSPACK_VER_MSKWAJD: the mskwaj_decompressor interface
215 * - #MSPACK_VER_MSKWAJC: the mskwaj_compressor interface
216 * - #MSPACK_VER_MSOABD: the msoab_decompressor interface
217 * - #MSPACK_VER_MSOABC: the msoab_compressor interface
218 *
219 * The result of the function should be interpreted as follows:
220 * - -1: this interface is completely unknown to the library
221 * - 0: this interface is known, but non-functioning
222 * - 1: this interface has all basic functionality
223 * - 2, 3, ...: this interface has additional functionality, clearly marked
224 * in the documentation as "version 2", "version 3" and so on.
225 *
226 * @param entity the interface to request current version of
227 * @return the version of the requested interface
228 */
229extern int mspack_version(int entity);
230
231/** Pass to mspack_version() to get the overall library version */
232#define MSPACK_VER_LIBRARY (0)
233/** Pass to mspack_version() to get the mspack_system version */
234#define MSPACK_VER_SYSTEM (1)
235/** Pass to mspack_version() to get the mscab_decompressor version */
236#define MSPACK_VER_MSCABD (2)
237/** Pass to mspack_version() to get the mscab_compressor version */
238#define MSPACK_VER_MSCABC (3)
239/** Pass to mspack_version() to get the mschm_decompressor version */
240#define MSPACK_VER_MSCHMD (4)
241/** Pass to mspack_version() to get the mschm_compressor version */
242#define MSPACK_VER_MSCHMC (5)
243/** Pass to mspack_version() to get the mslit_decompressor version */
244#define MSPACK_VER_MSLITD (6)
245/** Pass to mspack_version() to get the mslit_compressor version */
246#define MSPACK_VER_MSLITC (7)
247/** Pass to mspack_version() to get the mshlp_decompressor version */
248#define MSPACK_VER_MSHLPD (8)
249/** Pass to mspack_version() to get the mshlp_compressor version */
250#define MSPACK_VER_MSHLPC (9)
251/** Pass to mspack_version() to get the msszdd_decompressor version */
252#define MSPACK_VER_MSSZDDD (10)
253/** Pass to mspack_version() to get the msszdd_compressor version */
254#define MSPACK_VER_MSSZDDC (11)
255/** Pass to mspack_version() to get the mskwaj_decompressor version */
256#define MSPACK_VER_MSKWAJD (12)
257/** Pass to mspack_version() to get the mskwaj_compressor version */
258#define MSPACK_VER_MSKWAJC (13)
259/** Pass to mspack_version() to get the msoab_decompressor version */
260#define MSPACK_VER_MSOABD (14)
261/** Pass to mspack_version() to get the msoab_compressor version */
262#define MSPACK_VER_MSOABC (15)
263
264/* --- file I/O abstraction ------------------------------------------------ */
265
266/**
267 * A structure which abstracts file I/O and memory management.
268 *
269 * The library always uses the mspack_system structure for interaction
270 * with the file system and to allocate, free and copy all memory. It also
271 * uses it to send literal messages to the library user.
272 *
273 * When the library is compiled normally, passing NULL to a compressor or
274 * decompressor constructor will result in a default mspack_system being
275 * used, where all methods are implemented with the standard C library.
276 * However, all constructors support being given a custom created
277 * mspack_system structure, with the library user's own methods. This
278 * allows for more abstract interaction, such as reading and writing files
279 * directly to memory, or from a network socket or pipe.
280 *
281 * Implementors of an mspack_system structure should read all
282 * documentation entries for every structure member, and write methods
283 * which conform to those standards.
284 */
285struct mspack_system {
286 /**
287 * Opens a file for reading, writing, appending or updating.
288 *
289 * @param self a self-referential pointer to the mspack_system
290 * structure whose open() method is being called. If
291 * this pointer is required by close(), read(), write(),
292 * seek() or tell(), it should be stored in the result
293 * structure at this time.
294 * @param filename the file to be opened. It is passed directly from the
295 * library caller without being modified, so it is up to
296 * the caller what this parameter actually represents.
297 * @param mode one of #MSPACK_SYS_OPEN_READ (open an existing file
298 * for reading), #MSPACK_SYS_OPEN_WRITE (open a new file
299 * for writing), #MSPACK_SYS_OPEN_UPDATE (open an existing
300 * file for reading/writing from the start of the file) or
301 * #MSPACK_SYS_OPEN_APPEND (open an existing file for
302 * reading/writing from the end of the file)
303 * @return a pointer to a mspack_file structure. This structure officially
304 * contains no members, its true contents are up to the
305 * mspack_system implementor. It should contain whatever is needed
306 * for other mspack_system methods to operate. Returning the NULL
307 * pointer indicates an error condition.
308 * @see close(), read(), write(), seek(), tell(), message()
309 */
310 struct mspack_file * (*open)(struct mspack_system *self,
311 const char *filename,
312 int mode);
313
314 /**
315 * Closes a previously opened file. If any memory was allocated for this
316 * particular file handle, it should be freed at this time.
317 *
318 * @param file the file to close
319 * @see open()
320 */
321 void (*close)(struct mspack_file *file);
322
323 /**
324 * Reads a given number of bytes from an open file.
325 *
326 * @param file the file to read from
327 * @param buffer the location where the read bytes should be stored
328 * @param bytes the number of bytes to read from the file.
329 * @return the number of bytes successfully read (this can be less than
330 * the number requested), zero to mark the end of file, or less
331 * than zero to indicate an error. The library does not "retry"
332 * reads and assumes short reads are due to EOF, so you should
333 * avoid returning short reads because of transient errors.
334 * @see open(), write()
335 */
336 int (*read)(struct mspack_file *file,
337 void *buffer,
338 int bytes);
339
340 /**
341 * Writes a given number of bytes to an open file.
342 *
343 * @param file the file to write to
344 * @param buffer the location where the written bytes should be read from
345 * @param bytes the number of bytes to write to the file.
346 * @return the number of bytes successfully written, this can be less
347 * than the number requested. Zero or less can indicate an error
348 * where no bytes at all could be written. All cases where less
349 * bytes were written than requested are considered by the library
350 * to be an error.
351 * @see open(), read()
352 */
353 int (*write)(struct mspack_file *file,
354 void *buffer,
355 int bytes);
356
357 /**
358 * Seeks to a specific file offset within an open file.
359 *
360 * Sometimes the library needs to know the length of a file. It does
361 * this by seeking to the end of the file with seek(file, 0,
362 * MSPACK_SYS_SEEK_END), then calling tell(). Implementations may want
363 * to make a special case for this.
364 *
365 * Due to the potentially varying 32/64 bit datatype off_t on some
366 * architectures, the #MSPACK_SYS_SELFTEST macro MUST be used before
367 * using the library. If not, the error caused by the library passing an
368 * inappropriate stackframe to seek() is subtle and hard to trace.
369 *
370 * @param file the file to be seeked
371 * @param offset an offset to seek, measured in bytes
372 * @param mode one of #MSPACK_SYS_SEEK_START (the offset should be
373 * measured from the start of the file), #MSPACK_SYS_SEEK_CUR
374 * (the offset should be measured from the current file offset)
375 * or #MSPACK_SYS_SEEK_END (the offset should be measured from
376 * the end of the file)
377 * @return zero for success, non-zero for an error
378 * @see open(), tell()
379 */
380 int (*seek)(struct mspack_file *file,
381 off_t offset,
382 int mode);
383
384 /**
385 * Returns the current file position (in bytes) of the given file.
386 *
387 * @param file the file whose file position is wanted
388 * @return the current file position of the file
389 * @see open(), seek()
390 */
391 off_t (*tell)(struct mspack_file *file);
392
393 /**
394 * Used to send messages from the library to the user.
395 *
396 * Occasionally, the library generates warnings or other messages in
397 * plain english to inform the human user. These are informational only
398 * and can be ignored if not wanted.
399 *
400 * @param file may be a file handle returned from open() if this message
401 * pertains to a specific open file, or NULL if not related to
402 * a specific file.
403 * @param format a printf() style format string. It does NOT include a
404 * trailing newline.
405 * @see open()
406 */
407 void (*message)(struct mspack_file *file,
408 const char *format,
409 ...);
410
411 /**
412 * Allocates memory.
413 *
414 * @param self a self-referential pointer to the mspack_system
415 * structure whose alloc() method is being called.
416 * @param bytes the number of bytes to allocate
417 * @result a pointer to the requested number of bytes, or NULL if
418 * not enough memory is available
419 * @see free()
420 */
421 void * (*alloc)(struct mspack_system *self,
422 size_t bytes);
423
424 /**
425 * Frees memory.
426 *
427 * @param ptr the memory to be freed. NULL is accepted and ignored.
428 * @see alloc()
429 */
430 void (*free)(void *ptr);
431
432 /**
433 * Copies from one region of memory to another.
434 *
435 * The regions of memory are guaranteed not to overlap, are usually less
436 * than 256 bytes, and may not be aligned. Please note that the source
437 * parameter comes before the destination parameter, unlike the standard
438 * C function memcpy().
439 *
440 * @param src the region of memory to copy from
441 * @param dest the region of memory to copy to
442 * @param bytes the size of the memory region, in bytes
443 */
444 void (*copy)(void *src,
445 void *dest,
446 size_t bytes);
447
448 /**
449 * A null pointer to mark the end of mspack_system. It must equal NULL.
450 *
451 * Should the mspack_system structure extend in the future, this NULL
452 * will be seen, rather than have an invalid method pointer called.
453 */
454 void *null_ptr;
455};
456
457/** mspack_system::open() mode: open existing file for reading. */
458#define MSPACK_SYS_OPEN_READ (0)
459/** mspack_system::open() mode: open new file for writing */
460#define MSPACK_SYS_OPEN_WRITE (1)
461/** mspack_system::open() mode: open existing file for writing */
462#define MSPACK_SYS_OPEN_UPDATE (2)
463/** mspack_system::open() mode: open existing file for writing */
464#define MSPACK_SYS_OPEN_APPEND (3)
465
466/** mspack_system::seek() mode: seek relative to start of file */
467#define MSPACK_SYS_SEEK_START (0)
468/** mspack_system::seek() mode: seek relative to current offset */
469#define MSPACK_SYS_SEEK_CUR (1)
470/** mspack_system::seek() mode: seek relative to end of file */
471#define MSPACK_SYS_SEEK_END (2)
472
473/**
474 * A structure which represents an open file handle. The contents of this
475 * structure are determined by the implementation of the
476 * mspack_system::open() method.
477 */
478struct mspack_file {
479 int dummy;
480};
481
482/* --- error codes --------------------------------------------------------- */
483
484/** Error code: no error */
485#define MSPACK_ERR_OK (0)
486/** Error code: bad arguments to method */
487#define MSPACK_ERR_ARGS (1)
488/** Error code: error opening file */
489#define MSPACK_ERR_OPEN (2)
490/** Error code: error reading file */
491#define MSPACK_ERR_READ (3)
492/** Error code: error writing file */
493#define MSPACK_ERR_WRITE (4)
494/** Error code: seek error */
495#define MSPACK_ERR_SEEK (5)
496/** Error code: out of memory */
497#define MSPACK_ERR_NOMEMORY (6)
498/** Error code: bad "magic id" in file */
499#define MSPACK_ERR_SIGNATURE (7)
500/** Error code: bad or corrupt file format */
501#define MSPACK_ERR_DATAFORMAT (8)
502/** Error code: bad checksum or CRC */
503#define MSPACK_ERR_CHECKSUM (9)
504/** Error code: error during compression */
505#define MSPACK_ERR_CRUNCH (10)
506/** Error code: error during decompression */
507#define MSPACK_ERR_DECRUNCH (11)
508
509/* --- functions available in library -------------------------------------- */
510
511/** Creates a new CAB compressor.
512 * @param sys a custom mspack_system structure, or NULL to use the default
513 * @return a #mscab_compressor or NULL
514 */
515extern struct mscab_compressor *
516 mspack_create_cab_compressor(struct mspack_system *sys);
517
518/** Creates a new CAB decompressor.
519 * @param sys a custom mspack_system structure, or NULL to use the default
520 * @return a #mscab_decompressor or NULL
521 */
522extern struct mscab_decompressor *
523 mspack_create_cab_decompressor(struct mspack_system *sys);
524
525/** Destroys an existing CAB compressor.
526 * @param self the #mscab_compressor to destroy
527 */
528extern void mspack_destroy_cab_compressor(struct mscab_compressor *self);
529
530/** Destroys an existing CAB decompressor.
531 * @param self the #mscab_decompressor to destroy
532 */
533extern void mspack_destroy_cab_decompressor(struct mscab_decompressor *self);
534
535
536/** Creates a new CHM compressor.
537 * @param sys a custom mspack_system structure, or NULL to use the default
538 * @return a #mschm_compressor or NULL
539 */
540extern struct mschm_compressor *
541 mspack_create_chm_compressor(struct mspack_system *sys);
542
543/** Creates a new CHM decompressor.
544 * @param sys a custom mspack_system structure, or NULL to use the default
545 * @return a #mschm_decompressor or NULL
546 */
547extern struct mschm_decompressor *
548 mspack_create_chm_decompressor(struct mspack_system *sys);
549
550/** Destroys an existing CHM compressor.
551 * @param self the #mschm_compressor to destroy
552 */
553extern void mspack_destroy_chm_compressor(struct mschm_compressor *self);
554
555/** Destroys an existing CHM decompressor.
556 * @param self the #mschm_decompressor to destroy
557 */
558extern void mspack_destroy_chm_decompressor(struct mschm_decompressor *self);
559
560
561/** Creates a new LIT compressor.
562 * @param sys a custom mspack_system structure, or NULL to use the default
563 * @return a #mslit_compressor or NULL
564 */
565extern struct mslit_compressor *
566 mspack_create_lit_compressor(struct mspack_system *sys);
567
568/** Creates a new LIT decompressor.
569 * @param sys a custom mspack_system structure, or NULL to use the default
570 * @return a #mslit_decompressor or NULL
571 */
572extern struct mslit_decompressor *
573 mspack_create_lit_decompressor(struct mspack_system *sys);
574
575/** Destroys an existing LIT compressor.
576 * @param self the #mslit_compressor to destroy
577 */
578extern void mspack_destroy_lit_compressor(struct mslit_compressor *self);
579
580/** Destroys an existing LIT decompressor.
581 * @param self the #mslit_decompressor to destroy
582 */
583extern void mspack_destroy_lit_decompressor(struct mslit_decompressor *self);
584
585
586/** Creates a new HLP compressor.
587 * @param sys a custom mspack_system structure, or NULL to use the default
588 * @return a #mshlp_compressor or NULL
589 */
590extern struct mshlp_compressor *
591 mspack_create_hlp_compressor(struct mspack_system *sys);
592
593/** Creates a new HLP decompressor.
594 * @param sys a custom mspack_system structure, or NULL to use the default
595 * @return a #mshlp_decompressor or NULL
596 */
597extern struct mshlp_decompressor *
598 mspack_create_hlp_decompressor(struct mspack_system *sys);
599
600/** Destroys an existing hlp compressor.
601 * @param self the #mshlp_compressor to destroy
602 */
603extern void mspack_destroy_hlp_compressor(struct mshlp_compressor *self);
604
605/** Destroys an existing hlp decompressor.
606 * @param self the #mshlp_decompressor to destroy
607 */
608extern void mspack_destroy_hlp_decompressor(struct mshlp_decompressor *self);
609
610
611/** Creates a new SZDD compressor.
612 * @param sys a custom mspack_system structure, or NULL to use the default
613 * @return a #msszdd_compressor or NULL
614 */
615extern struct msszdd_compressor *
616 mspack_create_szdd_compressor(struct mspack_system *sys);
617
618/** Creates a new SZDD decompressor.
619 * @param sys a custom mspack_system structure, or NULL to use the default
620 * @return a #msszdd_decompressor or NULL
621 */
622extern struct msszdd_decompressor *
623 mspack_create_szdd_decompressor(struct mspack_system *sys);
624
625/** Destroys an existing SZDD compressor.
626 * @param self the #msszdd_compressor to destroy
627 */
628extern void mspack_destroy_szdd_compressor(struct msszdd_compressor *self);
629
630/** Destroys an existing SZDD decompressor.
631 * @param self the #msszdd_decompressor to destroy
632 */
633extern void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *self);
634
635
636/** Creates a new KWAJ compressor.
637 * @param sys a custom mspack_system structure, or NULL to use the default
638 * @return a #mskwaj_compressor or NULL
639 */
640extern struct mskwaj_compressor *
641 mspack_create_kwaj_compressor(struct mspack_system *sys);
642
643/** Creates a new KWAJ decompressor.
644 * @param sys a custom mspack_system structure, or NULL to use the default
645 * @return a #mskwaj_decompressor or NULL
646 */
647extern struct mskwaj_decompressor *
648 mspack_create_kwaj_decompressor(struct mspack_system *sys);
649
650/** Destroys an existing KWAJ compressor.
651 * @param self the #mskwaj_compressor to destroy
652 */
653extern void mspack_destroy_kwaj_compressor(struct mskwaj_compressor *self);
654
655/** Destroys an existing KWAJ decompressor.
656 * @param self the #mskwaj_decompressor to destroy
657 */
658extern void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *self);
659
660
661/** Creates a new OAB compressor.
662 * @param sys a custom mspack_system structure, or NULL to use the default
663 * @return a #msoab_compressor or NULL
664 */
665extern struct msoab_compressor *
666 mspack_create_oab_compressor(struct mspack_system *sys);
667
668/** Creates a new OAB decompressor.
669 * @param sys a custom mspack_system structure, or NULL to use the default
670 * @return a #msoab_decompressor or NULL
671 */
672extern struct msoab_decompressor *
673 mspack_create_oab_decompressor(struct mspack_system *sys);
674
675/** Destroys an existing OAB compressor.
676 * @param self the #msoab_compressor to destroy
677 */
678extern void mspack_destroy_oab_compressor(struct msoab_compressor *self);
679
680/** Destroys an existing OAB decompressor.
681 * @param self the #msoab_decompressor to destroy
682 */
683extern void mspack_destroy_oab_decompressor(struct msoab_decompressor *self);
684
685
686/* --- support for .CAB (MS Cabinet) file format --------------------------- */
687
688/**
689 * A structure which represents a single cabinet file.
690 *
691 * All fields are READ ONLY.
692 *
693 * If this cabinet is part of a merged cabinet set, the #files and #folders
694 * fields are common to all cabinets in the set, and will be identical.
695 *
696 * @see mscab_decompressor::open(), mscab_decompressor::close(),
697 * mscab_decompressor::search()
698 */
699struct mscabd_cabinet {
700 /**
701 * The next cabinet in a chained list, if this cabinet was opened with
702 * mscab_decompressor::search(). May be NULL to mark the end of the
703 * list.
704 */
705 struct mscabd_cabinet *next;
706
707 /**
708 * The filename of the cabinet. More correctly, the filename of the
709 * physical file that the cabinet resides in. This is given by the
710 * library user and may be in any format.
711 */
712 const char *filename;
713
714 /** The file offset of cabinet within the physical file it resides in. */
715 off_t base_offset;
716
717 /** The length of the cabinet file in bytes. */
718 unsigned int length;
719
720 /** The previous cabinet in a cabinet set, or NULL. */
721 struct mscabd_cabinet *prevcab;
722
723 /** The next cabinet in a cabinet set, or NULL. */
724 struct mscabd_cabinet *nextcab;
725
726 /** The filename of the previous cabinet in a cabinet set, or NULL. */
727 char *prevname;
728
729 /** The filename of the next cabinet in a cabinet set, or NULL. */
730 char *nextname;
731
732 /** The name of the disk containing the previous cabinet in a cabinet
733 * set, or NULL.
734 */
735 char *previnfo;
736
737 /** The name of the disk containing the next cabinet in a cabinet set,
738 * or NULL.
739 */
740 char *nextinfo;
741
742 /** A list of all files in the cabinet or cabinet set. */
743 struct mscabd_file *files;
744
745 /** A list of all folders in the cabinet or cabinet set. */
746 struct mscabd_folder *folders;
747
748 /**
749 * The set ID of the cabinet. All cabinets in the same set should have
750 * the same set ID.
751 */
752 unsigned short set_id;
753
754 /**
755 * The index number of the cabinet within the set. Numbering should
756 * start from 0 for the first cabinet in the set, and increment by 1 for
757 * each following cabinet.
758 */
759 unsigned short set_index;
760
761 /**
762 * The number of bytes reserved in the header area of the cabinet.
763 *
764 * If this is non-zero and flags has MSCAB_HDR_RESV set, this data can
765 * be read by the calling application. It is of the given length,
766 * located at offset (base_offset + MSCAB_HDR_RESV_OFFSET) in the
767 * cabinet file.
768 *
769 * @see flags
770 */
771 unsigned short header_resv;
772
773 /**
774 * Header flags.
775 *
776 * - MSCAB_HDR_PREVCAB indicates the cabinet is part of a cabinet set, and
777 * has a predecessor cabinet.
778 * - MSCAB_HDR_NEXTCAB indicates the cabinet is part of a cabinet set, and
779 * has a successor cabinet.
780 * - MSCAB_HDR_RESV indicates the cabinet has reserved header space.
781 *
782 * @see prevname, previnfo, nextname, nextinfo, header_resv
783 */
784 int flags;
785};
786
787/** Offset from start of cabinet to the reserved header data (if present). */
788#define MSCAB_HDR_RESV_OFFSET (0x28)
789
790/** Cabinet header flag: cabinet has a predecessor */
791#define MSCAB_HDR_PREVCAB (0x01)
792/** Cabinet header flag: cabinet has a successor */
793#define MSCAB_HDR_NEXTCAB (0x02)
794/** Cabinet header flag: cabinet has reserved header space */
795#define MSCAB_HDR_RESV (0x04)
796
797/**
798 * A structure which represents a single folder in a cabinet or cabinet set.
799 *
800 * All fields are READ ONLY.
801 *
802 * A folder is a single compressed stream of data. When uncompressed, it
803 * holds the data of one or more files. A folder may be split across more
804 * than one cabinet.
805 */
806struct mscabd_folder {
807 /**
808 * A pointer to the next folder in this cabinet or cabinet set, or NULL
809 * if this is the final folder.
810 */
811 struct mscabd_folder *next;
812
813 /**
814 * The compression format used by this folder.
815 *
816 * The macro MSCABD_COMP_METHOD() should be used on this field to get
817 * the algorithm used. The macro MSCABD_COMP_LEVEL() should be used to get
818 * the "compression level".
819 *
820 * @see MSCABD_COMP_METHOD(), MSCABD_COMP_LEVEL()
821 */
822 int comp_type;
823
824 /**
825 * The total number of data blocks used by this folder. This includes
826 * data blocks present in other files, if this folder spans more than
827 * one cabinet.
828 */
829 unsigned int num_blocks;
830};
831
832/**
833 * Returns the compression method used by a folder.
834 *
835 * @param comp_type a mscabd_folder::comp_type value
836 * @return one of #MSCAB_COMP_NONE, #MSCAB_COMP_MSZIP, #MSCAB_COMP_QUANTUM
837 * or #MSCAB_COMP_LZX
838 */
839#define MSCABD_COMP_METHOD(comp_type) ((comp_type) & 0x0F)
840/**
841 * Returns the compression level used by a folder.
842 *
843 * @param comp_type a mscabd_folder::comp_type value
844 * @return the compression level. This is only defined by LZX and Quantum
845 * compression
846 */
847#define MSCABD_COMP_LEVEL(comp_type) (((comp_type) >> 8) & 0x1F)
848
849/** Compression mode: no compression. */
850#define MSCAB_COMP_NONE (0)
851/** Compression mode: MSZIP (deflate) compression. */
852#define MSCAB_COMP_MSZIP (1)
853/** Compression mode: Quantum compression */
854#define MSCAB_COMP_QUANTUM (2)
855/** Compression mode: LZX compression */
856#define MSCAB_COMP_LZX (3)
857
858/**
859 * A structure which represents a single file in a cabinet or cabinet set.
860 *
861 * All fields are READ ONLY.
862 */
863struct mscabd_file {
864 /**
865 * The next file in the cabinet or cabinet set, or NULL if this is the
866 * final file.
867 */
868 struct mscabd_file *next;
869
870 /**
871 * The filename of the file.
872 *
873 * A null terminated string of up to 255 bytes in length, it may be in
874 * either ISO-8859-1 or UTF8 format, depending on the file attributes.
875 *
876 * @see attribs
877 */
878 char *filename;
879
880 /** The uncompressed length of the file, in bytes. */
881 unsigned int length;
882
883 /**
884 * File attributes.
885 *
886 * The following attributes are defined:
887 * - #MSCAB_ATTRIB_RDONLY indicates the file is write protected.
888 * - #MSCAB_ATTRIB_HIDDEN indicates the file is hidden.
889 * - #MSCAB_ATTRIB_SYSTEM indicates the file is a operating system file.
890 * - #MSCAB_ATTRIB_ARCH indicates the file is "archived".
891 * - #MSCAB_ATTRIB_EXEC indicates the file is an executable program.
892 * - #MSCAB_ATTRIB_UTF_NAME indicates the filename is in UTF8 format rather
893 * than ISO-8859-1.
894 */
895 int attribs;
896
897 /** File's last modified time, hour field. */
898 char time_h;
899 /** File's last modified time, minute field. */
900 char time_m;
901 /** File's last modified time, second field. */
902 char time_s;
903
904 /** File's last modified date, day field. */
905 char date_d;
906 /** File's last modified date, month field. */
907 char date_m;
908 /** File's last modified date, year field. */
909 int date_y;
910
911 /** A pointer to the folder that contains this file. */
912 struct mscabd_folder *folder;
913
914 /** The uncompressed offset of this file in its folder. */
915 unsigned int offset;
916};
917
918/** mscabd_file::attribs attribute: file is read-only. */
919#define MSCAB_ATTRIB_RDONLY (0x01)
920/** mscabd_file::attribs attribute: file is hidden. */
921#define MSCAB_ATTRIB_HIDDEN (0x02)
922/** mscabd_file::attribs attribute: file is an operating system file. */
923#define MSCAB_ATTRIB_SYSTEM (0x04)
924/** mscabd_file::attribs attribute: file is "archived". */
925#define MSCAB_ATTRIB_ARCH (0x20)
926/** mscabd_file::attribs attribute: file is an executable program. */
927#define MSCAB_ATTRIB_EXEC (0x40)
928/** mscabd_file::attribs attribute: filename is UTF8, not ISO-8859-1. */
929#define MSCAB_ATTRIB_UTF_NAME (0x80)
930
931/** mscab_decompressor::set_param() parameter: search buffer size. */
932#define MSCABD_PARAM_SEARCHBUF (0)
933/** mscab_decompressor::set_param() parameter: repair MS-ZIP streams? */
934#define MSCABD_PARAM_FIXMSZIP (1)
935/** mscab_decompressor::set_param() parameter: size of decompression buffer */
936#define MSCABD_PARAM_DECOMPBUF (2)
937/** mscab_decompressor::set_param() parameter: salvage data from bad cabinets?
938 * If enabled, open() will skip file with bad folder indices or filenames
939 * rather than reject the whole cabinet, and extract() will limit rather than
940 * reject files with invalid offsets and lengths, and bad data block checksums
941 * will be ignored. Available only in CAB decoder version 2 and above.
942 */
943#define MSCABD_PARAM_SALVAGE (3)
944
945/** TODO */
946struct mscab_compressor {
947 int dummy;
948};
949
950/**
951 * A decompressor for .CAB (Microsoft Cabinet) files
952 *
953 * All fields are READ ONLY.
954 *
955 * @see mspack_create_cab_decompressor(), mspack_destroy_cab_decompressor()
956 */
957struct mscab_decompressor {
958 /**
959 * Opens a cabinet file and reads its contents.
960 *
961 * If the file opened is a valid cabinet file, all headers will be read
962 * and a mscabd_cabinet structure will be returned, with a full list of
963 * folders and files.
964 *
965 * In the case of an error occuring, NULL is returned and the error code
966 * is available from last_error().
967 *
968 * The filename pointer should be considered "in use" until close() is
969 * called on the cabinet.
970 *
971 * @param self a self-referential pointer to the mscab_decompressor
972 * instance being called
973 * @param filename the filename of the cabinet file. This is passed
974 * directly to mspack_system::open().
975 * @return a pointer to a mscabd_cabinet structure, or NULL on failure
976 * @see close(), search(), last_error()
977 */
978 struct mscabd_cabinet * (*open) (struct mscab_decompressor *self,
979 const char *filename);
980
981 /**
982 * Closes a previously opened cabinet or cabinet set.
983 *
984 * This closes a cabinet, all cabinets associated with it via the
985 * mscabd_cabinet::next, mscabd_cabinet::prevcab and
986 * mscabd_cabinet::nextcab pointers, and all folders and files. All
987 * memory used by these entities is freed.
988 *
989 * The cabinet pointer is now invalid and cannot be used again. All
990 * mscabd_folder and mscabd_file pointers from that cabinet or cabinet
991 * set are also now invalid, and cannot be used again.
992 *
993 * If the cabinet pointer given was created using search(), it MUST be
994 * the cabinet pointer returned by search() and not one of the later
995 * cabinet pointers further along the mscabd_cabinet::next chain.
996
997 * If extra cabinets have been added using append() or prepend(), these
998 * will all be freed, even if the cabinet pointer given is not the first
999 * cabinet in the set. Do NOT close() more than one cabinet in the set.
1000 *
1001 * The mscabd_cabinet::filename is not freed by the library, as it is
1002 * not allocated by the library. The caller should free this itself if
1003 * necessary, before it is lost forever.
1004 *
1005 * @param self a self-referential pointer to the mscab_decompressor
1006 * instance being called
1007 * @param cab the cabinet to close
1008 * @see open(), search(), append(), prepend()
1009 */
1010 void (*close)(struct mscab_decompressor *self,
1011 struct mscabd_cabinet *cab);
1012
1013 /**
1014 * Searches a regular file for embedded cabinets.
1015 *
1016 * This opens a normal file with the given filename and will search the
1017 * entire file for embedded cabinet files
1018 *
1019 * If any cabinets are found, the equivalent of open() is called on each
1020 * potential cabinet file at the offset it was found. All successfully
1021 * open()ed cabinets are kept in a list.
1022 *
1023 * The first cabinet found will be returned directly as the result of
1024 * this method. Any further cabinets found will be chained in a list
1025 * using the mscabd_cabinet::next field.
1026 *
1027 * In the case of an error occuring anywhere other than the simulated
1028 * open(), NULL is returned and the error code is available from
1029 * last_error().
1030 *
1031 * If no error occurs, but no cabinets can be found in the file, NULL is
1032 * returned and last_error() returns MSPACK_ERR_OK.
1033 *
1034 * The filename pointer should be considered in use until close() is
1035 * called on the cabinet.
1036 *
1037 * close() should only be called on the result of search(), not on any
1038 * subsequent cabinets in the mscabd_cabinet::next chain.
1039 *
1040 * @param self a self-referential pointer to the mscab_decompressor
1041 * instance being called
1042 * @param filename the filename of the file to search for cabinets. This
1043 * is passed directly to mspack_system::open().
1044 * @return a pointer to a mscabd_cabinet structure, or NULL
1045 * @see close(), open(), last_error()
1046 */
1047 struct mscabd_cabinet * (*search) (struct mscab_decompressor *self,
1048 const char *filename);
1049
1050 /**
1051 * Appends one mscabd_cabinet to another, forming or extending a cabinet
1052 * set.
1053 *
1054 * This will attempt to append one cabinet to another such that
1055 * <tt>(cab->nextcab == nextcab) && (nextcab->prevcab == cab)</tt> and
1056 * any folders split between the two cabinets are merged.
1057 *
1058 * The cabinets MUST be part of a cabinet set -- a cabinet set is a
1059 * cabinet that spans more than one physical cabinet file on disk -- and
1060 * must be appropriately matched.
1061 *
1062 * It can be determined if a cabinet has further parts to load by
1063 * examining the mscabd_cabinet::flags field:
1064 *
1065 * - if <tt>(flags & MSCAB_HDR_PREVCAB)</tt> is non-zero, there is a
1066 * predecessor cabinet to open() and prepend(). Its MS-DOS
1067 * case-insensitive filename is mscabd_cabinet::prevname
1068 * - if <tt>(flags & MSCAB_HDR_NEXTCAB)</tt> is non-zero, there is a
1069 * successor cabinet to open() and append(). Its MS-DOS case-insensitive
1070 * filename is mscabd_cabinet::nextname
1071 *
1072 * If the cabinets do not match, an error code will be returned. Neither
1073 * cabinet has been altered, and both should be closed seperately.
1074 *
1075 * Files and folders in a cabinet set are a single entity. All cabinets
1076 * in a set use the same file list, which is updated as cabinets in the
1077 * set are added. All pointers to mscabd_folder and mscabd_file
1078 * structures in either cabinet must be discarded and re-obtained after
1079 * merging.
1080 *
1081 * @param self a self-referential pointer to the mscab_decompressor
1082 * instance being called
1083 * @param cab the cabinet which will be appended to,
1084 * predecessor of nextcab
1085 * @param nextcab the cabinet which will be appended,
1086 * successor of cab
1087 * @return an error code, or MSPACK_ERR_OK if successful
1088 * @see prepend(), open(), close()
1089 */
1090 int (*append) (struct mscab_decompressor *self,
1091 struct mscabd_cabinet *cab,
1092 struct mscabd_cabinet *nextcab);
1093
1094 /**
1095 * Prepends one mscabd_cabinet to another, forming or extending a
1096 * cabinet set.
1097 *
1098 * This will attempt to prepend one cabinet to another, such that
1099 * <tt>(cab->prevcab == prevcab) && (prevcab->nextcab == cab)</tt>. In
1100 * all other respects, it is identical to append(). See append() for the
1101 * full documentation.
1102 *
1103 * @param self a self-referential pointer to the mscab_decompressor
1104 * instance being called
1105 * @param cab the cabinet which will be prepended to,
1106 * successor of prevcab
1107 * @param prevcab the cabinet which will be prepended,
1108 * predecessor of cab
1109 * @return an error code, or MSPACK_ERR_OK if successful
1110 * @see append(), open(), close()
1111 */
1112 int (*prepend) (struct mscab_decompressor *self,
1113 struct mscabd_cabinet *cab,
1114 struct mscabd_cabinet *prevcab);
1115
1116 /**
1117 * Extracts a file from a cabinet or cabinet set.
1118 *
1119 * This extracts a compressed file in a cabinet and writes it to the given
1120 * filename.
1121 *
1122 * The MS-DOS filename of the file, mscabd_file::filename, is NOT USED
1123 * by extract(). The caller must examine this MS-DOS filename, copy and
1124 * change it as necessary, create directories as necessary, and provide
1125 * the correct filename as a parameter, which will be passed unchanged
1126 * to the decompressor's mspack_system::open()
1127 *
1128 * If the file belongs to a split folder in a multi-part cabinet set,
1129 * and not enough parts of the cabinet set have been loaded and appended
1130 * or prepended, an error will be returned immediately.
1131 *
1132 * @param self a self-referential pointer to the mscab_decompressor
1133 * instance being called
1134 * @param file the file to be decompressed
1135 * @param filename the filename of the file being written to
1136 * @return an error code, or MSPACK_ERR_OK if successful
1137 */
1138 int (*extract)(struct mscab_decompressor *self,
1139 struct mscabd_file *file,
1140 const char *filename);
1141
1142 /**
1143 * Sets a CAB decompression engine parameter.
1144 *
1145 * The following parameters are defined:
1146 * - #MSCABD_PARAM_SEARCHBUF: How many bytes should be allocated as a
1147 * buffer when using search()? The minimum value is 4. The default
1148 * value is 32768.
1149 * - #MSCABD_PARAM_FIXMSZIP: If non-zero, extract() will ignore bad
1150 * checksums and recover from decompression errors in MS-ZIP
1151 * compressed folders. The default value is 0 (don't recover).
1152 * - #MSCABD_PARAM_DECOMPBUF: How many bytes should be used as an input
1153 * bit buffer by decompressors? The minimum value is 4. The default
1154 * value is 4096.
1155 *
1156 * @param self a self-referential pointer to the mscab_decompressor
1157 * instance being called
1158 * @param param the parameter to set
1159 * @param value the value to set the parameter to
1160 * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there
1161 * is a problem with either parameter or value.
1162 * @see search(), extract()
1163 */
1164 int (*set_param)(struct mscab_decompressor *self,
1165 int param,
1166 int value);
1167
1168 /**
1169 * Returns the error code set by the most recently called method.
1170 *
1171 * This is useful for open() and search(), which do not return an error
1172 * code directly.
1173 *
1174 * @param self a self-referential pointer to the mscab_decompressor
1175 * instance being called
1176 * @return the most recent error code
1177 * @see open(), search()
1178 */
1179 int (*last_error)(struct mscab_decompressor *self);
1180};
1181
1182/* --- support for .CHM (HTMLHelp) file format ----------------------------- */
1183
1184/**
1185 * A structure which represents a file to be placed in a CHM helpfile.
1186 *
1187 * A contiguous array of these structures should be passed to
1188 * mschm_compressor::generate(). The array list is terminated with an
1189 * entry whose mschmc_file::section field is set to #MSCHMC_ENDLIST, the
1190 * other fields in this entry are ignored.
1191 */
1192struct mschmc_file {
1193 /** One of #MSCHMC_ENDLIST, #MSCHMC_UNCOMP or #MSCHMC_MSCOMP. */
1194 int section;
1195
1196 /** The filename of the source file that will be added to the CHM. This
1197 * is passed directly to mspack_system::open(). */
1198 const char *filename;
1199
1200 /** The full path and filename of the file within the CHM helpfile, a
1201 * UTF-1 encoded null-terminated string. */
1202 char *chm_filename;
1203
1204 /** The length of the file, in bytes. This will be adhered to strictly
1205 * and a read error will be issued if this many bytes cannot be read
1206 * from the real file at CHM generation time. */
1207 off_t length;
1208};
1209
1210/**
1211 * A structure which represents a section of a CHM helpfile.
1212 *
1213 * All fields are READ ONLY.
1214 *
1215 * Not used directly, but used as a generic base type for
1216 * mschmd_sec_uncompressed and mschmd_sec_mscompressed.
1217 */
1218struct mschmd_section {
1219 /** A pointer to the CHM helpfile that contains this section. */
1220 struct mschmd_header *chm;
1221
1222 /**
1223 * The section ID. Either 0 for the uncompressed section
1224 * mschmd_sec_uncompressed, or 1 for the LZX compressed section
1225 * mschmd_sec_mscompressed. No other section IDs are known.
1226 */
1227 unsigned int id;
1228};
1229
1230/**
1231 * A structure which represents the uncompressed section of a CHM helpfile.
1232 *
1233 * All fields are READ ONLY.
1234 */
1235struct mschmd_sec_uncompressed {
1236 /** Generic section data. */
1237 struct mschmd_section base;
1238
1239 /** The file offset of where this section begins in the CHM helpfile. */
1240 off_t offset;
1241};
1242
1243/**
1244 * A structure which represents the LZX compressed section of a CHM helpfile.
1245 *
1246 * All fields are READ ONLY.
1247 */
1248struct mschmd_sec_mscompressed {
1249 /** Generic section data. */
1250 struct mschmd_section base;
1251
1252 /** A pointer to the meta-file which represents all LZX compressed data. */
1253 struct mschmd_file *content;
1254
1255 /** A pointer to the file which contains the LZX control data. */
1256 struct mschmd_file *control;
1257
1258 /** A pointer to the file which contains the LZX reset table. */
1259 struct mschmd_file *rtable;
1260
1261 /** A pointer to the file which contains the LZX span information.
1262 * Available only in CHM decoder version 2 and above.
1263 */
1264 struct mschmd_file *spaninfo;
1265};
1266
1267/**
1268 * A structure which represents a CHM helpfile.
1269 *
1270 * All fields are READ ONLY.
1271 */
1272struct mschmd_header {
1273 /** The version of the CHM file format used in this file. */
1274 unsigned int version;
1275
1276 /**
1277 * The "timestamp" of the CHM helpfile.
1278 *
1279 * It is the lower 32 bits of a 64-bit value representing the number of
1280 * centiseconds since 1601-01-01 00:00:00 UTC, plus 42. It is not useful
1281 * as a timestamp, but it is useful as a semi-unique ID.
1282 */
1283 unsigned int timestamp;
1284
1285 /**
1286 * The default Language and Country ID (LCID) of the user who ran the
1287 * HTMLHelp Compiler. This is not the language of the CHM file itself.
1288 */
1289 unsigned int language;
1290
1291 /**
1292 * The filename of the CHM helpfile. This is given by the library user
1293 * and may be in any format.
1294 */
1295 const char *filename;
1296
1297 /** The length of the CHM helpfile, in bytes. */
1298 off_t length;
1299
1300 /** A list of all non-system files in the CHM helpfile. */
1301 struct mschmd_file *files;
1302
1303 /**
1304 * A list of all system files in the CHM helpfile.
1305 *
1306 * System files are files which begin with "::". They are meta-files
1307 * generated by the CHM creation process.
1308 */
1309 struct mschmd_file *sysfiles;
1310
1311 /** The section 0 (uncompressed) data in this CHM helpfile. */
1312 struct mschmd_sec_uncompressed sec0;
1313
1314 /** The section 1 (MSCompressed) data in this CHM helpfile. */
1315 struct mschmd_sec_mscompressed sec1;
1316
1317 /** The file offset of the first PMGL/PMGI directory chunk. */
1318 off_t dir_offset;
1319
1320 /** The number of PMGL/PMGI directory chunks in this CHM helpfile. */
1321 unsigned int num_chunks;
1322
1323 /** The size of each PMGL/PMGI chunk, in bytes. */
1324 unsigned int chunk_size;
1325
1326 /** The "density" of the quick-reference section in PMGL/PMGI chunks. */
1327 unsigned int density;
1328
1329 /** The depth of the index tree.
1330 *
1331 * - if 1, there are no PMGI chunks, only PMGL chunks.
1332 * - if 2, there is 1 PMGI chunk. All chunk indices point to PMGL chunks.
1333 * - if 3, the root PMGI chunk points to secondary PMGI chunks, which in
1334 * turn point to PMGL chunks.
1335 * - and so on...
1336 */
1337 unsigned int depth;
1338
1339 /**
1340 * The number of the root PMGI chunk.
1341 *
1342 * If there is no index in the CHM helpfile, this will be 0xFFFFFFFF.
1343 */
1344 unsigned int index_root;
1345
1346 /**
1347 * The number of the first PMGL chunk. Usually zero.
1348 * Available only in CHM decoder version 2 and above.
1349 */
1350 unsigned int first_pmgl;
1351
1352 /**
1353 * The number of the last PMGL chunk. Usually num_chunks-1.
1354 * Available only in CHM decoder version 2 and above.
1355 */
1356 unsigned int last_pmgl;
1357
1358 /**
1359 * A cache of loaded chunks, filled in by mschm_decoder::fast_find().
1360 * Available only in CHM decoder version 2 and above.
1361 */
1362 unsigned char **chunk_cache;
1363};
1364
1365/**
1366 * A structure which represents a file stored in a CHM helpfile.
1367 *
1368 * All fields are READ ONLY.
1369 */
1370struct mschmd_file {
1371 /**
1372 * A pointer to the next file in the list, or NULL if this is the final
1373 * file.
1374 */
1375 struct mschmd_file *next;
1376
1377 /**
1378 * A pointer to the section that this file is located in. Indirectly,
1379 * it also points to the CHM helpfile the file is located in.
1380 */
1381 struct mschmd_section *section;
1382
1383 /** The offset within the section data that this file is located at. */
1384 off_t offset;
1385
1386 /** The length of this file, in bytes */
1387 off_t length;
1388
1389 /** The filename of this file -- a null terminated string in UTF-8. */
1390 char *filename;
1391};
1392
1393/** mschmc_file::section value: end of CHM file list */
1394#define MSCHMC_ENDLIST (0)
1395/** mschmc_file::section value: this file is in the Uncompressed section */
1396#define MSCHMC_UNCOMP (1)
1397/** mschmc_file::section value: this file is in the MSCompressed section */
1398#define MSCHMC_MSCOMP (2)
1399
1400/** mschm_compressor::set_param() parameter: "timestamp" header */
1401#define MSCHMC_PARAM_TIMESTAMP (0)
1402/** mschm_compressor::set_param() parameter: "language" header */
1403#define MSCHMC_PARAM_LANGUAGE (1)
1404/** mschm_compressor::set_param() parameter: LZX window size */
1405#define MSCHMC_PARAM_LZXWINDOW (2)
1406/** mschm_compressor::set_param() parameter: intra-chunk quickref density */
1407#define MSCHMC_PARAM_DENSITY (3)
1408/** mschm_compressor::set_param() parameter: whether to create indices */
1409#define MSCHMC_PARAM_INDEX (4)
1410
1411/**
1412 * A compressor for .CHM (Microsoft HTMLHelp) files.
1413 *
1414 * All fields are READ ONLY.
1415 *
1416 * @see mspack_create_chm_compressor(), mspack_destroy_chm_compressor()
1417 */
1418struct mschm_compressor {
1419 /**
1420 * Generates a CHM help file.
1421 *
1422 * The help file will contain up to two sections, an Uncompressed
1423 * section and potentially an MSCompressed (LZX compressed)
1424 * section.
1425 *
1426 * While the contents listing of a CHM file is always in lexical order,
1427 * the file list passed in will be taken as the correct order for files
1428 * within the sections. It is in your interest to place similar files
1429 * together for better compression.
1430 *
1431 * There are two modes of generation, to use a temporary file or not to
1432 * use one. See use_temporary_file() for the behaviour of generate() in
1433 * these two different modes.
1434 *
1435 * @param self a self-referential pointer to the mschm_compressor
1436 * instance being called
1437 * @param file_list an array of mschmc_file structures, terminated
1438 * with an entry whose mschmc_file::section field is
1439 * #MSCHMC_ENDLIST. The order of the list is
1440 * preserved within each section. The length of any
1441 * mschmc_file::chm_filename string cannot exceed
1442 * roughly 4096 bytes. Each source file must be able
1443 * to supply as many bytes as given in the
1444 * mschmc_file::length field.
1445 * @param output_file the file to write the generated CHM helpfile to.
1446 * This is passed directly to mspack_system::open()
1447 * @return an error code, or MSPACK_ERR_OK if successful
1448 * @see use_temporary_file() set_param()
1449 */
1450 int (*generate)(struct mschm_compressor *self,
1451 struct mschmc_file file_list[],
1452 const char *output_file);
1453
1454 /**
1455 * Specifies whether a temporary file is used during CHM generation.
1456 *
1457 * The CHM file format includes data about the compressed section (such
1458 * as its overall size) that is stored in the output CHM file prior to
1459 * the compressed section itself. This unavoidably requires that the
1460 * compressed section has to be generated, before these details can be
1461 * set. There are several ways this can be handled. Firstly, the
1462 * compressed section could be generated entirely in memory before
1463 * writing any of the output CHM file. This approach is not used in
1464 * libmspack, as the compressed section can exceed the addressable
1465 * memory space on most architectures.
1466 *
1467 * libmspack has two options, either to write these unknowable sections
1468 * with blank data, generate the compressed section, then re-open the
1469 * output file for update once the compressed section has been
1470 * completed, or to write the compressed section to a temporary file,
1471 * then write the entire output file at once, performing a simple
1472 * file-to-file copy for the compressed section.
1473 *
1474 * The simple solution of buffering the entire compressed section in
1475 * memory can still be used, if desired. As the temporary file's
1476 * filename is passed directly to mspack_system::open(), it is possible
1477 * for a custom mspack_system implementation to hold this file in memory,
1478 * without writing to a disk.
1479 *
1480 * If a temporary file is set, generate() performs the following
1481 * sequence of events: the temporary file is opened for writing, the
1482 * compression algorithm writes to the temporary file, the temporary
1483 * file is closed. Then the output file is opened for writing and the
1484 * temporary file is re-opened for reading. The output file is written
1485 * and the temporary file is read from. Both files are then closed. The
1486 * temporary file itself is not deleted. If that is desired, the
1487 * temporary file should be deleted after the completion of generate(),
1488 * if it exists.
1489 *
1490 * If a temporary file is set not to be used, generate() performs the
1491 * following sequence of events: the output file is opened for writing,
1492 * then it is written and closed. The output file is then re-opened for
1493 * update, the appropriate sections are seek()ed to and re-written, then
1494 * the output file is closed.
1495 *
1496 * @param self a self-referential pointer to the
1497 * mschm_compressor instance being called
1498 * @param use_temp_file non-zero if the temporary file should be used,
1499 * zero if the temporary file should not be used.
1500 * @param temp_file a file to temporarily write compressed data to,
1501 * before opening it for reading and copying the
1502 * contents to the output file. This is passed
1503 * directly to mspack_system::open().
1504 * @return an error code, or MSPACK_ERR_OK if successful
1505 * @see generate()
1506 */
1507 int (*use_temporary_file)(struct mschm_compressor *self,
1508 int use_temp_file,
1509 const char *temp_file);
1510 /**
1511 * Sets a CHM compression engine parameter.
1512 *
1513 * The following parameters are defined:
1514
1515 * - #MSCHMC_PARAM_TIMESTAMP: Sets the "timestamp" of the CHM file
1516 * generated. This is not a timestamp, see mschmd_header::timestamp
1517 * for a description. If this timestamp is 0, generate() will use its
1518 * own algorithm for making a unique ID, based on the lengths and
1519 * names of files in the CHM itself. Defaults to 0, any value between
1520 * 0 and (2^32)-1 is valid.
1521 * - #MSCHMC_PARAM_LANGUAGE: Sets the "language" of the CHM file
1522 * generated. This is not the language used in the CHM file, but the
1523 * language setting of the user who ran the HTMLHelp compiler. It
1524 * defaults to 0x0409. The valid range is between 0x0000 and 0x7F7F.
1525 * - #MSCHMC_PARAM_LZXWINDOW: Sets the size of the LZX history window,
1526 * which is also the interval at which the compressed data stream can be
1527 * randomly accessed. The value is not a size in bytes, but a power of
1528 * two. The default value is 16 (which makes the window 2^16 bytes, or
1529 * 64 kilobytes), the valid range is from 15 (32 kilobytes) to 21 (2
1530 * megabytes).
1531 * - #MSCHMC_PARAM_DENSITY: Sets the "density" of quick reference
1532 * entries stored at the end of directory listing chunk. Each chunk is
1533 * 4096 bytes in size, and contains as many file entries as there is
1534 * room for. At the other end of the chunk, a list of "quick reference"
1535 * pointers is included. The offset of every 'N'th file entry is given a
1536 * quick reference, where N = (2^density) + 1. The default density is
1537 * 2. The smallest density is 0 (N=2), the maximum is 10 (N=1025). As
1538 * each file entry requires at least 5 bytes, the maximum number of
1539 * entries in a single chunk is roughly 800, so the maximum value 10
1540 * can be used to indicate there are no quickrefs at all.
1541 * - #MSCHMC_PARAM_INDEX: Sets whether or not to include quick lookup
1542 * index chunk(s), in addition to normal directory listing chunks. A
1543 * value of zero means no index chunks will be created, a non-zero value
1544 * means index chunks will be created. The default is zero, "don't
1545 * create an index".
1546 *
1547 * @param self a self-referential pointer to the mschm_compressor
1548 * instance being called
1549 * @param param the parameter to set
1550 * @param value the value to set the parameter to
1551 * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there
1552 * is a problem with either parameter or value.
1553 * @see generate()
1554 */
1555 int (*set_param)(struct mschm_compressor *self,
1556 int param,
1557 int value);
1558
1559 /**
1560 * Returns the error code set by the most recently called method.
1561 *
1562 * @param self a self-referential pointer to the mschm_compressor
1563 * instance being called
1564 * @return the most recent error code
1565 * @see set_param(), generate()
1566 */
1567 int (*last_error)(struct mschm_compressor *self);
1568};
1569
1570/**
1571 * A decompressor for .CHM (Microsoft HTMLHelp) files
1572 *
1573 * All fields are READ ONLY.
1574 *
1575 * @see mspack_create_chm_decompressor(), mspack_destroy_chm_decompressor()
1576 */
1577struct mschm_decompressor {
1578 /**
1579 * Opens a CHM helpfile and reads its contents.
1580 *
1581 * If the file opened is a valid CHM helpfile, all headers will be read
1582 * and a mschmd_header structure will be returned, with a full list of
1583 * files.
1584 *
1585 * In the case of an error occuring, NULL is returned and the error code
1586 * is available from last_error().
1587 *
1588 * The filename pointer should be considered "in use" until close() is
1589 * called on the CHM helpfile.
1590 *
1591 * @param self a self-referential pointer to the mschm_decompressor
1592 * instance being called
1593 * @param filename the filename of the CHM helpfile. This is passed
1594 * directly to mspack_system::open().
1595 * @return a pointer to a mschmd_header structure, or NULL on failure
1596 * @see close()
1597 */
1598 struct mschmd_header *(*open)(struct mschm_decompressor *self,
1599 const char *filename);
1600
1601 /**
1602 * Closes a previously opened CHM helpfile.
1603 *
1604 * This closes a CHM helpfile, frees the mschmd_header and all
1605 * mschmd_file structures associated with it (if any). This works on
1606 * both helpfiles opened with open() and helpfiles opened with
1607 * fast_open().
1608 *
1609 * The CHM header pointer is now invalid and cannot be used again. All
1610 * mschmd_file pointers referencing that CHM are also now invalid, and
1611 * cannot be used again.
1612 *
1613 * @param self a self-referential pointer to the mschm_decompressor
1614 * instance being called
1615 * @param chm the CHM helpfile to close
1616 * @see open(), fast_open()
1617 */
1618 void (*close)(struct mschm_decompressor *self,
1619 struct mschmd_header *chm);
1620
1621 /**
1622 * Extracts a file from a CHM helpfile.
1623 *
1624 * This extracts a file from a CHM helpfile and writes it to the given
1625 * filename. The filename of the file, mscabd_file::filename, is not
1626 * used by extract(), but can be used by the caller as a guide for
1627 * constructing an appropriate filename.
1628 *
1629 * This method works both with files found in the mschmd_header::files
1630 * and mschmd_header::sysfiles list and mschmd_file structures generated
1631 * on the fly by fast_find().
1632 *
1633 * @param self a self-referential pointer to the mschm_decompressor
1634 * instance being called
1635 * @param file the file to be decompressed
1636 * @param filename the filename of the file being written to
1637 * @return an error code, or MSPACK_ERR_OK if successful
1638 */
1639 int (*extract)(struct mschm_decompressor *self,
1640 struct mschmd_file *file,
1641 const char *filename);
1642
1643 /**
1644 * Returns the error code set by the most recently called method.
1645 *
1646 * This is useful for open() and fast_open(), which do not return an
1647 * error code directly.
1648 *
1649 * @param self a self-referential pointer to the mschm_decompressor
1650 * instance being called
1651 * @return the most recent error code
1652 * @see open(), extract()
1653 */
1654 int (*last_error)(struct mschm_decompressor *self);
1655
1656 /**
1657 * Opens a CHM helpfile quickly.
1658 *
1659 * If the file opened is a valid CHM helpfile, only essential headers
1660 * will be read. A mschmd_header structure will be still be returned, as
1661 * with open(), but the mschmd_header::files field will be NULL. No
1662 * files details will be automatically read. The fast_find() method
1663 * must be used to obtain file details.
1664 *
1665 * In the case of an error occuring, NULL is returned and the error code
1666 * is available from last_error().
1667 *
1668 * The filename pointer should be considered "in use" until close() is
1669 * called on the CHM helpfile.
1670 *
1671 * @param self a self-referential pointer to the mschm_decompressor
1672 * instance being called
1673 * @param filename the filename of the CHM helpfile. This is passed
1674 * directly to mspack_system::open().
1675 * @return a pointer to a mschmd_header structure, or NULL on failure
1676 * @see open(), close(), fast_find(), extract()
1677 */
1678 struct mschmd_header *(*fast_open)(struct mschm_decompressor *self,
1679 const char *filename);
1680
1681 /**
1682 * Finds file details quickly.
1683 *
1684 * Instead of reading all CHM helpfile headers and building a list of
1685 * files, fast_open() and fast_find() are intended for finding file
1686 * details only when they are needed. The CHM file format includes an
1687 * on-disk file index to allow this.
1688 *
1689 * Given a case-sensitive filename, fast_find() will search the on-disk
1690 * index for that file.
1691 *
1692 * If the file was found, the caller-provided mschmd_file structure will
1693 * be filled out like so:
1694 * - section: the correct value for the found file
1695 * - offset: the correct value for the found file
1696 * - length: the correct value for the found file
1697 * - all other structure elements: NULL or 0
1698 *
1699 * If the file was not found, MSPACK_ERR_OK will still be returned as the
1700 * result, but the caller-provided structure will be filled out like so:
1701 * - section: NULL
1702 * - offset: 0
1703 * - length: 0
1704 * - all other structure elements: NULL or 0
1705 *
1706 * This method is intended to be used in conjunction with CHM helpfiles
1707 * opened with fast_open(), but it also works with helpfiles opened
1708 * using the regular open().
1709 *
1710 * @param self a self-referential pointer to the mschm_decompressor
1711 * instance being called
1712 * @param chm the CHM helpfile to search for the file
1713 * @param filename the filename of the file to search for
1714 * @param f_ptr a pointer to a caller-provded mschmd_file structure
1715 * @param f_size <tt>sizeof(struct mschmd_file)</tt>
1716 * @return an error code, or MSPACK_ERR_OK if successful
1717 * @see open(), close(), fast_find(), extract()
1718 */
1719 int (*fast_find)(struct mschm_decompressor *self,
1720 struct mschmd_header *chm,
1721 const char *filename,
1722 struct mschmd_file *f_ptr,
1723 int f_size);
1724};
1725
1726/* --- support for .LIT (EBook) file format -------------------------------- */
1727
1728/** TODO */
1729struct mslit_compressor {
1730 int dummy;
1731};
1732
1733/** TODO */
1734struct mslit_decompressor {
1735 int dummy;
1736};
1737
1738
1739/* --- support for .HLP (MS Help) file format ------------------------------ */
1740
1741/** TODO */
1742struct mshlp_compressor {
1743 int dummy;
1744};
1745
1746/** TODO */
1747struct mshlp_decompressor {
1748 int dummy;
1749};
1750
1751
1752/* --- support for SZDD file format ---------------------------------------- */
1753
1754/** msszdd_compressor::set_param() parameter: the missing character */
1755#define MSSZDDC_PARAM_MISSINGCHAR (0)
1756
1757/** msszddd_header::format value - a regular SZDD file */
1758#define MSSZDD_FMT_NORMAL (0)
1759
1760/** msszddd_header::format value - a special QBasic SZDD file */
1761#define MSSZDD_FMT_QBASIC (1)
1762
1763/**
1764 * A structure which represents an SZDD compressed file.
1765 *
1766 * All fields are READ ONLY.
1767 */
1768struct msszddd_header {
1769 /** The file format; either #MSSZDD_FMT_NORMAL or #MSSZDD_FMT_QBASIC */
1770 int format;
1771
1772 /** The amount of data in the SZDD file once uncompressed. */
1773 off_t length;
1774
1775 /**
1776 * The last character in the filename, traditionally replaced with an
1777 * underscore to show the file is compressed. The null character is used
1778 * to show that this character has not been stored (e.g. because the
1779 * filename is not known). Generally, only characters that may appear in
1780 * an MS-DOS filename (except ".") are valid.
1781 */
1782 char missing_char;
1783};
1784
1785/**
1786 * A compressor for the SZDD file format.
1787 *
1788 * All fields are READ ONLY.
1789 *
1790 * @see mspack_create_szdd_compressor(), mspack_destroy_szdd_compressor()
1791 */
1792struct msszdd_compressor {
1793 /**
1794 * Reads an input file and creates a compressed output file in the
1795 * SZDD compressed file format. The SZDD compression format is quick
1796 * but gives poor compression. It is possible for the compressed output
1797 * file to be larger than the input file.
1798 *
1799 * Conventionally, SZDD compressed files have the final character in
1800 * their filename replaced with an underscore, to show they are
1801 * compressed. The missing character is stored in the compressed file
1802 * itself. This is due to the restricted filename conventions of MS-DOS,
1803 * most operating systems, such as UNIX, simply append another file
1804 * extension to the existing filename. As mspack does not deal with
1805 * filenames, this is left up to you. If you wish to set the missing
1806 * character stored in the file header, use set_param() with the
1807 * #MSSZDDC_PARAM_MISSINGCHAR parameter.
1808 *
1809 * "Stream" compression (where the length of the input data is not
1810 * known) is not possible. The length of the input data is stored in the
1811 * header of the SZDD file and must therefore be known before any data
1812 * is compressed. Due to technical limitations of the file format, the
1813 * maximum size of uncompressed file that will be accepted is 2147483647
1814 * bytes.
1815 *
1816 * @param self a self-referential pointer to the msszdd_compressor
1817 * instance being called
1818 * @param input the name of the file to compressed. This is passed
1819 * passed directly to mspack_system::open()
1820 * @param output the name of the file to write compressed data to.
1821 * This is passed directly to mspack_system::open().
1822 * @param length the length of the uncompressed file, or -1 to indicate
1823 * that this should be determined automatically by using
1824 * mspack_system::seek() on the input file.
1825 * @return an error code, or MSPACK_ERR_OK if successful
1826 * @see set_param()
1827 */
1828 int (*compress)(struct msszdd_compressor *self,
1829 const char *input,
1830 const char *output,
1831 off_t length);
1832
1833 /**
1834 * Sets an SZDD compression engine parameter.
1835 *
1836 * The following parameters are defined:
1837
1838 * - #MSSZDDC_PARAM_CHARACTER: the "missing character", the last character
1839 * in the uncompressed file's filename, which is traditionally replaced
1840 * with an underscore to show the file is compressed. Traditionally,
1841 * this can only be a character that is a valid part of an MS-DOS,
1842 * filename, but libmspack permits any character between 0x00 and 0xFF
1843 * to be stored. 0x00 is the default, and it represents "no character
1844 * stored".
1845 *
1846 * @param self a self-referential pointer to the msszdd_compressor
1847 * instance being called
1848 * @param param the parameter to set
1849 * @param value the value to set the parameter to
1850 * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there
1851 * is a problem with either parameter or value.
1852 * @see compress()
1853 */
1854 int (*set_param)(struct msszdd_compressor *self,
1855 int param,
1856 int value);
1857
1858 /**
1859 * Returns the error code set by the most recently called method.
1860 *
1861 * @param self a self-referential pointer to the msszdd_compressor
1862 * instance being called
1863 * @return the most recent error code
1864 * @see compress()
1865 */
1866 int (*last_error)(struct mschm_decompressor *self);
1867};
1868
1869/**
1870 * A decompressor for SZDD compressed files.
1871 *
1872 * All fields are READ ONLY.
1873 *
1874 * @see mspack_create_szdd_decompressor(), mspack_destroy_szdd_decompressor()
1875 */
1876struct msszdd_decompressor {
1877 /**
1878 * Opens a SZDD file and reads the header.
1879 *
1880 * If the file opened is a valid SZDD file, all headers will be read and
1881 * a msszddd_header structure will be returned.
1882 *
1883 * In the case of an error occuring, NULL is returned and the error code
1884 * is available from last_error().
1885 *
1886 * The filename pointer should be considered "in use" until close() is
1887 * called on the SZDD file.
1888 *
1889 * @param self a self-referential pointer to the msszdd_decompressor
1890 * instance being called
1891 * @param filename the filename of the SZDD compressed file. This is
1892 * passed directly to mspack_system::open().
1893 * @return a pointer to a msszddd_header structure, or NULL on failure
1894 * @see close()
1895 */
1896 struct msszddd_header *(*open)(struct msszdd_decompressor *self,
1897 const char *filename);
1898
1899 /**
1900 * Closes a previously opened SZDD file.
1901 *
1902 * This closes a SZDD file and frees the msszddd_header associated with
1903 * it.
1904 *
1905 * The SZDD header pointer is now invalid and cannot be used again.
1906 *
1907 * @param self a self-referential pointer to the msszdd_decompressor
1908 * instance being called
1909 * @param szdd the SZDD file to close
1910 * @see open()
1911 */
1912 void (*close)(struct msszdd_decompressor *self,
1913 struct msszddd_header *szdd);
1914
1915 /**
1916 * Extracts the compressed data from a SZDD file.
1917 *
1918 * This decompresses the compressed SZDD data stream and writes it to
1919 * an output file.
1920 *
1921 * @param self a self-referential pointer to the msszdd_decompressor
1922 * instance being called
1923 * @param szdd the SZDD file to extract data from
1924 * @param filename the filename to write the decompressed data to. This
1925 * is passed directly to mspack_system::open().
1926 * @return an error code, or MSPACK_ERR_OK if successful
1927 */
1928 int (*extract)(struct msszdd_decompressor *self,
1929 struct msszddd_header *szdd,
1930 const char *filename);
1931
1932 /**
1933 * Decompresses an SZDD file to an output file in one step.
1934 *
1935 * This opens an SZDD file as input, reads the header, then decompresses
1936 * the compressed data immediately to an output file, finally closing
1937 * both the input and output file. It is more convenient to use than
1938 * open() then extract() then close(), if you do not need to know the
1939 * SZDD output size or missing character.
1940 *
1941 * @param self a self-referential pointer to the msszdd_decompressor
1942 * instance being called
1943 * @param input the filename of the input SZDD file. This is passed
1944 * directly to mspack_system::open().
1945 * @param output the filename to write the decompressed data to. This
1946 * is passed directly to mspack_system::open().
1947 * @return an error code, or MSPACK_ERR_OK if successful
1948 */
1949 int (*decompress)(struct msszdd_decompressor *self,
1950 const char *input,
1951 const char *output);
1952
1953 /**
1954 * Returns the error code set by the most recently called method.
1955 *
1956 * This is useful for open() which does not return an
1957 * error code directly.
1958 *
1959 * @param self a self-referential pointer to the msszdd_decompressor
1960 * instance being called
1961 * @return the most recent error code
1962 * @see open(), extract(), decompress()
1963 */
1964 int (*last_error)(struct msszdd_decompressor *self);
1965};
1966
1967/* --- support for KWAJ file format ---------------------------------------- */
1968
1969/** mskwaj_compressor::set_param() parameter: compression type */
1970#define MSKWAJC_PARAM_COMP_TYPE (0)
1971
1972/** mskwaj_compressor::set_param() parameter: include the length of the
1973 * uncompressed file in the header?
1974 */
1975#define MSKWAJC_PARAM_INCLUDE_LENGTH (1)
1976
1977/** KWAJ compression type: no compression. */
1978#define MSKWAJ_COMP_NONE (0)
1979/** KWAJ compression type: no compression, 0xFF XOR "encryption". */
1980#define MSKWAJ_COMP_XOR (1)
1981/** KWAJ compression type: LZSS (same method as SZDD) */
1982#define MSKWAJ_COMP_SZDD (2)
1983/** KWAJ compression type: LZ+Huffman compression */
1984#define MSKWAJ_COMP_LZH (3)
1985/** KWAJ compression type: MSZIP */
1986#define MSKWAJ_COMP_MSZIP (4)
1987
1988/** KWAJ optional header flag: decompressed file length is included */
1989#define MSKWAJ_HDR_HASLENGTH (0x01)
1990
1991/** KWAJ optional header flag: unknown 2-byte structure is included */
1992#define MSKWAJ_HDR_HASUNKNOWN1 (0x02)
1993
1994/** KWAJ optional header flag: unknown multi-sized structure is included */
1995#define MSKWAJ_HDR_HASUNKNOWN2 (0x04)
1996
1997/** KWAJ optional header flag: file name (no extension) is included */
1998#define MSKWAJ_HDR_HASFILENAME (0x08)
1999
2000/** KWAJ optional header flag: file extension is included */
2001#define MSKWAJ_HDR_HASFILEEXT (0x10)
2002
2003/** KWAJ optional header flag: extra text is included */
2004#define MSKWAJ_HDR_HASEXTRATEXT (0x20)
2005
2006/**
2007 * A structure which represents an KWAJ compressed file.
2008 *
2009 * All fields are READ ONLY.
2010 */
2011struct mskwajd_header {
2012 /** The compression type; should be one of #MSKWAJ_COMP_NONE,
2013 * #MSKWAJ_COMP_XOR, #MSKWAJ_COMP_SZDD or #MSKWAJ_COMP_LZH
2014 */
2015 unsigned short comp_type;
2016
2017 /** The offset in the file where the compressed data stream begins */
2018 off_t data_offset;
2019
2020 /** Flags indicating which optional headers were included. */
2021 int headers;
2022
2023 /** The amount of uncompressed data in the file, or 0 if not present. */
2024 off_t length;
2025
2026 /** output filename, or NULL if not present */
2027 char *filename;
2028
2029 /** extra uncompressed data (usually text) in the header.
2030 * This data can contain nulls so use extra_length to get the size.
2031 */
2032 char *extra;
2033
2034 /** length of extra uncompressed data in the header */
2035 unsigned short extra_length;
2036};
2037
2038/**
2039 * A compressor for the KWAJ file format.
2040 *
2041 * All fields are READ ONLY.
2042 *
2043 * @see mspack_create_kwaj_compressor(), mspack_destroy_kwaj_compressor()
2044 */
2045struct mskwaj_compressor {
2046 /**
2047 * Reads an input file and creates a compressed output file in the
2048 * KWAJ compressed file format. The KWAJ compression format is quick
2049 * but gives poor compression. It is possible for the compressed output
2050 * file to be larger than the input file.
2051 *
2052 * @param self a self-referential pointer to the mskwaj_compressor
2053 * instance being called
2054 * @param input the name of the file to compressed. This is passed
2055 * passed directly to mspack_system::open()
2056 * @param output the name of the file to write compressed data to.
2057 * This is passed directly to mspack_system::open().
2058 * @param length the length of the uncompressed file, or -1 to indicate
2059 * that this should be determined automatically by using
2060 * mspack_system::seek() on the input file.
2061 * @return an error code, or MSPACK_ERR_OK if successful
2062 * @see set_param()
2063 */
2064 int (*compress)(struct mskwaj_compressor *self,
2065 const char *input,
2066 const char *output,
2067 off_t length);
2068
2069 /**
2070 * Sets an KWAJ compression engine parameter.
2071 *
2072 * The following parameters are defined:
2073 *
2074 * - #MSKWAJC_PARAM_COMP_TYPE: the compression method to use. Must
2075 * be one of #MSKWAJC_COMP_NONE, #MSKWAJC_COMP_XOR, #MSKWAJ_COMP_SZDD
2076 * or #MSKWAJ_COMP_LZH. The default is #MSKWAJ_COMP_LZH.
2077 *
2078 * - #MSKWAJC_PARAM_INCLUDE_LENGTH: a boolean; should the compressed
2079 * output file should include the uncompressed length of the input
2080 * file in the header? This adds 4 bytes to the size of the output
2081 * file. A value of zero says "no", non-zero says "yes". The default
2082 * is "no".
2083 *
2084 * @param self a self-referential pointer to the mskwaj_compressor
2085 * instance being called
2086 * @param param the parameter to set
2087 * @param value the value to set the parameter to
2088 * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there
2089 * is a problem with either parameter or value.
2090 * @see generate()
2091 */
2092 int (*set_param)(struct mskwaj_compressor *self,
2093 int param,
2094 int value);
2095
2096
2097 /**
2098 * Sets the original filename of the file before compression,
2099 * which will be stored in the header of the output file.
2100 *
2101 * The filename should be a null-terminated string, it must be an
2102 * MS-DOS "8.3" type filename (up to 8 bytes for the filename, then
2103 * optionally a "." and up to 3 bytes for a filename extension).
2104 *
2105 * If NULL is passed as the filename, no filename is included in the
2106 * header. This is the default.
2107 *
2108 * @param self a self-referential pointer to the mskwaj_compressor
2109 * instance being called
2110 * @param filename the original filename to use
2111 * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if the
2112 * filename is too long
2113 */
2114 int (*set_filename)(struct mskwaj_compressor *self,
2115 const char *filename);
2116
2117 /**
2118 * Sets arbitrary data that will be stored in the header of the
2119 * output file, uncompressed. It can be up to roughly 64 kilobytes,
2120 * as the overall size of the header must not exceed 65535 bytes.
2121 * The data can contain null bytes if desired.
2122 *
2123 * If NULL is passed as the data pointer, or zero is passed as the
2124 * length, no extra data is included in the header. This is the
2125 * default.
2126 *
2127 * @param self a self-referential pointer to the mskwaj_compressor
2128 * instance being called
2129 * @param data a pointer to the data to be stored in the header
2130 * @param bytes the length of the data in bytes
2131 * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS extra data
2132 * is too long
2133 */
2134 int (*set_extra_data)(struct mskwaj_compressor *self,
2135 void *data,
2136 size_t bytes);
2137
2138 /**
2139 * Returns the error code set by the most recently called method.
2140 *
2141 * @param self a self-referential pointer to the mskwaj_compressor
2142 * instance being called
2143 * @return the most recent error code
2144 * @see compress()
2145 */
2146 int (*last_error)(struct mschm_decompressor *self);
2147};
2148
2149/**
2150 * A decompressor for KWAJ compressed files.
2151 *
2152 * All fields are READ ONLY.
2153 *
2154 * @see mspack_create_kwaj_decompressor(), mspack_destroy_kwaj_decompressor()
2155 */
2156struct mskwaj_decompressor {
2157 /**
2158 * Opens a KWAJ file and reads the header.
2159 *
2160 * If the file opened is a valid KWAJ file, all headers will be read and
2161 * a mskwajd_header structure will be returned.
2162 *
2163 * In the case of an error occuring, NULL is returned and the error code
2164 * is available from last_error().
2165 *
2166 * The filename pointer should be considered "in use" until close() is
2167 * called on the KWAJ file.
2168 *
2169 * @param self a self-referential pointer to the mskwaj_decompressor
2170 * instance being called
2171 * @param filename the filename of the KWAJ compressed file. This is
2172 * passed directly to mspack_system::open().
2173 * @return a pointer to a mskwajd_header structure, or NULL on failure
2174 * @see close()
2175 */
2176 struct mskwajd_header *(*open)(struct mskwaj_decompressor *self,
2177 const char *filename);
2178
2179 /**
2180 * Closes a previously opened KWAJ file.
2181 *
2182 * This closes a KWAJ file and frees the mskwajd_header associated
2183 * with it. The KWAJ header pointer is now invalid and cannot be
2184 * used again.
2185 *
2186 * @param self a self-referential pointer to the mskwaj_decompressor
2187 * instance being called
2188 * @param kwaj the KWAJ file to close
2189 * @see open()
2190 */
2191 void (*close)(struct mskwaj_decompressor *self,
2192 struct mskwajd_header *kwaj);
2193
2194 /**
2195 * Extracts the compressed data from a KWAJ file.
2196 *
2197 * This decompresses the compressed KWAJ data stream and writes it to
2198 * an output file.
2199 *
2200 * @param self a self-referential pointer to the mskwaj_decompressor
2201 * instance being called
2202 * @param kwaj the KWAJ file to extract data from
2203 * @param filename the filename to write the decompressed data to. This
2204 * is passed directly to mspack_system::open().
2205 * @return an error code, or MSPACK_ERR_OK if successful
2206 */
2207 int (*extract)(struct mskwaj_decompressor *self,
2208 struct mskwajd_header *kwaj,
2209 const char *filename);
2210
2211 /**
2212 * Decompresses an KWAJ file to an output file in one step.
2213 *
2214 * This opens an KWAJ file as input, reads the header, then decompresses
2215 * the compressed data immediately to an output file, finally closing
2216 * both the input and output file. It is more convenient to use than
2217 * open() then extract() then close(), if you do not need to know the
2218 * KWAJ output size or output filename.
2219 *
2220 * @param self a self-referential pointer to the mskwaj_decompressor
2221 * instance being called
2222 * @param input the filename of the input KWAJ file. This is passed
2223 * directly to mspack_system::open().
2224 * @param output the filename to write the decompressed data to. This
2225 * is passed directly to mspack_system::open().
2226 * @return an error code, or MSPACK_ERR_OK if successful
2227 */
2228 int (*decompress)(struct mskwaj_decompressor *self,
2229 const char *input,
2230 const char *output);
2231
2232 /**
2233 * Returns the error code set by the most recently called method.
2234 *
2235 * This is useful for open() which does not return an
2236 * error code directly.
2237 *
2238 * @param self a self-referential pointer to the mskwaj_decompressor
2239 * instance being called
2240 * @return the most recent error code
2241 * @see open(), search()
2242 */
2243 int (*last_error)(struct mskwaj_decompressor *self);
2244};
2245
2246/* --- support for .LZX (Offline Address Book) file format ----------------- */
2247
2248/**
2249 * A compressor for the Offline Address Book (OAB) format.
2250 *
2251 * All fields are READ ONLY.
2252 *
2253 * @see mspack_create_oab_compressor(), mspack_destroy_oab_compressor()
2254 */
2255struct msoab_compressor {
2256 /**
2257 * Compress a full OAB file.
2258 *
2259 * The input file will be read and the compressed contents written to the
2260 * output file.
2261 *
2262 * @param self a self-referential pointer to the msoab_decompressor
2263 * instance being called
2264 * @param input the filename of the input file. This is passed
2265 * directly to mspack_system::open().
2266 * @param output the filename of the output file. This is passed
2267 * directly to mspack_system::open().
2268 * @return an error code, or MSPACK_ERR_OK if successful
2269 */
2270 int (*compress) (struct msoab_compressor *self,
2271 const char *input,
2272 const char *output);
2273
2274 /**
2275 * Generate a compressed incremental OAB patch file.
2276 *
2277 * The two uncompressed files "input" and "base" will be read, and an
2278 * incremental patch to generate "input" from "base" will be written to
2279 * the output file.
2280 *
2281 * @param self a self-referential pointer to the msoab_compressor
2282 * instance being called
2283 * @param input the filename of the input file containing the new
2284 * version of its contents. This is passed directly
2285 * to mspack_system::open().
2286 * @param base the filename of the original base file containing
2287 * the old version of its contents, against which the
2288 * incremental patch shall generated. This is passed
2289 * directly to mspack_system::open().
2290 * @param output the filename of the output file. This is passed
2291 * directly to mspack_system::open().
2292 * @return an error code, or MSPACK_ERR_OK if successful
2293 */
2294 int (*compress_incremental) (struct msoab_compressor *self,
2295 const char *input,
2296 const char *base,
2297 const char *output);
2298};
2299
2300/**
2301 * A decompressor for .LZX (Offline Address Book) files
2302 *
2303 * All fields are READ ONLY.
2304 *
2305 * @see mspack_create_oab_decompressor(), mspack_destroy_oab_decompressor()
2306 */
2307struct msoab_decompressor {
2308 /**
2309 * Decompresses a full Offline Address Book file.
2310 *
2311 * If the input file is a valid compressed Offline Address Book file,
2312 * it will be read and the decompressed contents will be written to
2313 * the output file.
2314 *
2315 * @param self a self-referential pointer to the msoab_decompressor
2316 * instance being called
2317 * @param input the filename of the input file. This is passed
2318 * directly to mspack_system::open().
2319 * @param output the filename of the output file. This is passed
2320 * directly to mspack_system::open().
2321 * @return an error code, or MSPACK_ERR_OK if successful
2322 */
2323 int (*decompress) (struct msoab_decompressor *self,
2324 const char *input,
2325 const char *output);
2326
2327 /**
2328 * Decompresses an Offline Address Book with an incremental patch file.
2329 *
2330 * This requires both a full UNCOMPRESSED Offline Address Book file to
2331 * act as the "base", and a compressed incremental patch file as input.
2332 * If the input file is valid, it will be decompressed with reference to
2333 * the base file, and the decompressed contents will be written to the
2334 * output file.
2335 *
2336 * There is no way to tell what the right base file is for the given
2337 * incremental patch, but if you get it wrong, this will usually result
2338 * in incorrect data being decompressed, which will then fail a checksum
2339 * test.
2340 *
2341 * @param self a self-referential pointer to the msoab_decompressor
2342 * instance being called
2343 * @param input the filename of the input file. This is passed
2344 * directly to mspack_system::open().
2345 * @param base the filename of the base file to which the
2346 * incremental patch shall be applied. This is passed
2347 * directly to mspack_system::open().
2348 * @param output the filename of the output file. This is passed
2349 * directly to mspack_system::open().
2350 * @return an error code, or MSPACK_ERR_OK if successful
2351 */
2352 int (*decompress_incremental) (struct msoab_decompressor *self,
2353 const char *input,
2354 const char *base,
2355 const char *output);
2356
2357 /**
2358 * Sets an OAB decompression engine parameter. Available only in OAB
2359 * decompressor version 2 and above.
2360 *
2361 * - #MSOABD_PARAM_DECOMPBUF: How many bytes should be used as an input
2362 * buffer by decompressors? The minimum value is 16. The default value
2363 * is 4096.
2364 *
2365 * @param self a self-referential pointer to the msoab_decompressor
2366 * instance being called
2367 * @param param the parameter to set
2368 * @param value the value to set the parameter to
2369 * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there
2370 * is a problem with either parameter or value.
2371 */
2372 int (*set_param)(struct msoab_decompressor *self,
2373 int param,
2374 int value);
2375
2376};
2377
2378/** msoab_decompressor::set_param() parameter: size of decompression buffer */
2379#define MSOABD_PARAM_DECOMPBUF (0)
2380
2381#ifdef __cplusplus
2382}
2383#endif
2384
2385#endif
diff --git a/utils/rbutilqt/mspack/mszip.h b/utils/rbutilqt/mspack/mszip.h
new file mode 100644
index 0000000000..2cd608234e
--- /dev/null
+++ b/utils/rbutilqt/mspack/mszip.h
@@ -0,0 +1,126 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * The deflate method was created by Phil Katz. MSZIP is equivalent to the
5 * deflate method.
6 *
7 * libmspack is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
9 *
10 * For further details, see the file COPYING.LIB distributed with libmspack
11 */
12
13#ifndef MSPACK_MSZIP_H
14#define MSPACK_MSZIP_H 1
15
16#ifdef __cplusplus
17extern "C" {
18#endif
19
20/* MSZIP (deflate) compression / (inflate) decompression definitions */
21
22#define MSZIP_FRAME_SIZE (32768) /* size of LZ history window */
23#define MSZIP_LITERAL_MAXSYMBOLS (288) /* literal/length huffman tree */
24#define MSZIP_LITERAL_TABLEBITS (9)
25#define MSZIP_DISTANCE_MAXSYMBOLS (32) /* distance huffman tree */
26#define MSZIP_DISTANCE_TABLEBITS (6)
27
28/* if there are less direct lookup entries than symbols, the longer
29 * code pointers will be <= maxsymbols. This must not happen, or we
30 * will decode entries badly */
31#if (1 << MSZIP_LITERAL_TABLEBITS) < (MSZIP_LITERAL_MAXSYMBOLS * 2)
32# define MSZIP_LITERAL_TABLESIZE (MSZIP_LITERAL_MAXSYMBOLS * 4)
33#else
34# define MSZIP_LITERAL_TABLESIZE ((1 << MSZIP_LITERAL_TABLEBITS) + \
35 (MSZIP_LITERAL_MAXSYMBOLS * 2))
36#endif
37
38#if (1 << MSZIP_DISTANCE_TABLEBITS) < (MSZIP_DISTANCE_MAXSYMBOLS * 2)
39# define MSZIP_DISTANCE_TABLESIZE (MSZIP_DISTANCE_MAXSYMBOLS * 4)
40#else
41# define MSZIP_DISTANCE_TABLESIZE ((1 << MSZIP_DISTANCE_TABLEBITS) + \
42 (MSZIP_DISTANCE_MAXSYMBOLS * 2))
43#endif
44
45struct mszipd_stream {
46 struct mspack_system *sys; /* I/O routines */
47 struct mspack_file *input; /* input file handle */
48 struct mspack_file *output; /* output file handle */
49 unsigned int window_posn; /* offset within window */
50
51 /* inflate() will call this whenever the window should be emptied. */
52 int (*flush_window)(struct mszipd_stream *, unsigned int);
53
54 int error, repair_mode, bytes_output;
55
56 /* I/O buffering */
57 unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end, input_end;
58 unsigned int bit_buffer, bits_left, inbuf_size;
59
60
61 /* huffman code lengths */
62 unsigned char LITERAL_len[MSZIP_LITERAL_MAXSYMBOLS];
63 unsigned char DISTANCE_len[MSZIP_DISTANCE_MAXSYMBOLS];
64
65 /* huffman decoding tables */
66 unsigned short LITERAL_table [MSZIP_LITERAL_TABLESIZE];
67 unsigned short DISTANCE_table[MSZIP_DISTANCE_TABLESIZE];
68
69 /* 32kb history window */
70 unsigned char window[MSZIP_FRAME_SIZE];
71};
72
73/* allocates MS-ZIP decompression stream for decoding the given stream.
74 *
75 * - uses system->alloc() to allocate memory
76 *
77 * - returns NULL if not enough memory
78 *
79 * - input_buffer_size is how many bytes to use as an input bitstream buffer
80 *
81 * - if repair_mode is non-zero, errors in decompression will be skipped
82 * and 'holes' left will be filled with zero bytes. This allows at least
83 * a partial recovery of erroneous data.
84 */
85extern struct mszipd_stream *mszipd_init(struct mspack_system *system,
86 struct mspack_file *input,
87 struct mspack_file *output,
88 int input_buffer_size,
89 int repair_mode);
90
91/* decompresses, or decompresses more of, an MS-ZIP stream.
92 *
93 * - out_bytes of data will be decompressed and the function will return
94 * with an MSPACK_ERR_OK return code.
95 *
96 * - decompressing will stop as soon as out_bytes is reached. if the true
97 * amount of bytes decoded spills over that amount, they will be kept for
98 * a later invocation of mszipd_decompress().
99 *
100 * - the output bytes will be passed to the system->write() function given in
101 * mszipd_init(), using the output file handle given in mszipd_init(). More
102 * than one call may be made to system->write()
103 *
104 * - MS-ZIP will read input bytes as necessary using the system->read()
105 * function given in mszipd_init(), using the input file handle given in
106 * mszipd_init(). This will continue until system->read() returns 0 bytes,
107 * or an error.
108 */
109extern int mszipd_decompress(struct mszipd_stream *zip, off_t out_bytes);
110
111/* decompresses an entire MS-ZIP stream in a KWAJ file. Acts very much
112 * like mszipd_decompress(), but doesn't take an out_bytes parameter
113 */
114extern int mszipd_decompress_kwaj(struct mszipd_stream *zip);
115
116/* frees all stream associated with an MS-ZIP data stream
117 *
118 * - calls system->free() using the system pointer given in mszipd_init()
119 */
120void mszipd_free(struct mszipd_stream *zip);
121
122#ifdef __cplusplus
123}
124#endif
125
126#endif
diff --git a/utils/rbutilqt/mspack/mszipc.c b/utils/rbutilqt/mspack/mszipc.c
new file mode 100644
index 0000000000..2f14b9ac83
--- /dev/null
+++ b/utils/rbutilqt/mspack/mszipc.c
@@ -0,0 +1,18 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * The deflate method was created by Phil Katz. MSZIP is equivalent to the
5 * deflate method.
6 *
7 * libmspack is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
9 *
10 * For further details, see the file COPYING.LIB distributed with libmspack
11 */
12
13/* MS-ZIP compression implementation */
14
15#include "system-mspack.h"
16#include "mszip.h"
17
18/* todo */
diff --git a/utils/rbutilqt/mspack/mszipd.c b/utils/rbutilqt/mspack/mszipd.c
new file mode 100644
index 0000000000..c1b02b1207
--- /dev/null
+++ b/utils/rbutilqt/mspack/mszipd.c
@@ -0,0 +1,515 @@
1/* This file is part of libmspack.
2 * (C) 2003-2010 Stuart Caie.
3 *
4 * The deflate method was created by Phil Katz. MSZIP is equivalent to the
5 * deflate method.
6 *
7 * libmspack is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
9 *
10 * For further details, see the file COPYING.LIB distributed with libmspack
11 */
12
13/* MS-ZIP decompression implementation. */
14
15#include "system-mspack.h"
16#include "mszip.h"
17
18/* import bit-reading macros and code */
19#define BITS_TYPE struct mszipd_stream
20#define BITS_VAR zip
21#define BITS_ORDER_LSB
22#define BITS_LSB_TABLE
23#define READ_BYTES do { \
24 READ_IF_NEEDED; \
25 INJECT_BITS(*i_ptr++, 8); \
26} while (0)
27#include "readbits.h"
28
29/* import huffman macros and code */
30#define TABLEBITS(tbl) MSZIP_##tbl##_TABLEBITS
31#define MAXSYMBOLS(tbl) MSZIP_##tbl##_MAXSYMBOLS
32#define HUFF_TABLE(tbl,idx) zip->tbl##_table[idx]
33#define HUFF_LEN(tbl,idx) zip->tbl##_len[idx]
34#define HUFF_ERROR return INF_ERR_HUFFSYM
35#include "readhuff.h"
36
37#define FLUSH_IF_NEEDED do { \
38 if (zip->window_posn == MSZIP_FRAME_SIZE) { \
39 if (zip->flush_window(zip, MSZIP_FRAME_SIZE)) { \
40 return INF_ERR_FLUSH; \
41 } \
42 zip->window_posn = 0; \
43 } \
44} while (0)
45
46/* match lengths for literal codes 257.. 285 */
47static const unsigned short lit_lengths[29] = {
48 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27,
49 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
50};
51
52/* match offsets for distance codes 0 .. 29 */
53static const unsigned short dist_offsets[30] = {
54 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385,
55 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
56};
57
58/* extra bits required for literal codes 257.. 285 */
59static const unsigned char lit_extrabits[29] = {
60 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2,
61 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
62};
63
64/* extra bits required for distance codes 0 .. 29 */
65static const unsigned char dist_extrabits[30] = {
66 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6,
67 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
68};
69
70/* the order of the bit length Huffman code lengths */
71static const unsigned char bitlen_order[19] = {
72 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
73};
74
75/* inflate() error codes */
76#define INF_ERR_BLOCKTYPE (-1) /* unknown block type */
77#define INF_ERR_COMPLEMENT (-2) /* block size complement mismatch */
78#define INF_ERR_FLUSH (-3) /* error from flush_window() callback */
79#define INF_ERR_BITBUF (-4) /* too many bits in bit buffer */
80#define INF_ERR_SYMLENS (-5) /* too many symbols in blocktype 2 header */
81#define INF_ERR_BITLENTBL (-6) /* failed to build bitlens huffman table */
82#define INF_ERR_LITERALTBL (-7) /* failed to build literals huffman table */
83#define INF_ERR_DISTANCETBL (-8) /* failed to build distance huffman table */
84#define INF_ERR_BITOVERRUN (-9) /* bitlen RLE code goes over table size */
85#define INF_ERR_BADBITLEN (-10) /* invalid bit-length code */
86#define INF_ERR_LITCODE (-11) /* out-of-range literal code */
87#define INF_ERR_DISTCODE (-12) /* out-of-range distance code */
88#define INF_ERR_DISTANCE (-13) /* somehow, distance is beyond 32k */
89#define INF_ERR_HUFFSYM (-14) /* out of bits decoding huffman symbol */
90
91static int zip_read_lens(struct mszipd_stream *zip) {
92 /* for the bit buffer and huffman decoding */
93 register unsigned int bit_buffer;
94 register int bits_left;
95 unsigned char *i_ptr, *i_end;
96
97 /* bitlen Huffman codes -- immediate lookup, 7 bit max code length */
98 unsigned short bl_table[(1 << 7)];
99 unsigned char bl_len[19];
100
101 unsigned char lens[MSZIP_LITERAL_MAXSYMBOLS + MSZIP_DISTANCE_MAXSYMBOLS];
102 unsigned int lit_codes, dist_codes, code, last_code=0, bitlen_codes, i, run;
103
104 RESTORE_BITS;
105
106 /* read the number of codes */
107 READ_BITS(lit_codes, 5); lit_codes += 257;
108 READ_BITS(dist_codes, 5); dist_codes += 1;
109 READ_BITS(bitlen_codes, 4); bitlen_codes += 4;
110 if (lit_codes > MSZIP_LITERAL_MAXSYMBOLS) return INF_ERR_SYMLENS;
111 if (dist_codes > MSZIP_DISTANCE_MAXSYMBOLS) return INF_ERR_SYMLENS;
112
113 /* read in the bit lengths in their unusual order */
114 for (i = 0; i < bitlen_codes; i++) READ_BITS(bl_len[bitlen_order[i]], 3);
115 while (i < 19) bl_len[bitlen_order[i++]] = 0;
116
117 /* create decoding table with an immediate lookup */
118 if (make_decode_table(19, 7, &bl_len[0], &bl_table[0])) {
119 return INF_ERR_BITLENTBL;
120 }
121
122 /* read literal / distance code lengths */
123 for (i = 0; i < (lit_codes + dist_codes); i++) {
124 /* single-level huffman lookup */
125 ENSURE_BITS(7);
126 code = bl_table[PEEK_BITS(7)];
127 REMOVE_BITS(bl_len[code]);
128
129 if (code < 16) lens[i] = last_code = code;
130 else {
131 switch (code) {
132 case 16: READ_BITS(run, 2); run += 3; code = last_code; break;
133 case 17: READ_BITS(run, 3); run += 3; code = 0; break;
134 case 18: READ_BITS(run, 7); run += 11; code = 0; break;
135 default: D(("bad code!: %u", code)) return INF_ERR_BADBITLEN;
136 }
137 if ((i + run) > (lit_codes + dist_codes)) return INF_ERR_BITOVERRUN;
138 while (run--) lens[i++] = code;
139 i--;
140 }
141 }
142
143 /* copy LITERAL code lengths and clear any remaining */
144 i = lit_codes;
145 zip->sys->copy(&lens[0], &zip->LITERAL_len[0], i);
146 while (i < MSZIP_LITERAL_MAXSYMBOLS) zip->LITERAL_len[i++] = 0;
147
148 i = dist_codes;
149 zip->sys->copy(&lens[lit_codes], &zip->DISTANCE_len[0], i);
150 while (i < MSZIP_DISTANCE_MAXSYMBOLS) zip->DISTANCE_len[i++] = 0;
151
152 STORE_BITS;
153 return 0;
154}
155
156/* a clean implementation of RFC 1951 / inflate */
157static int inflate(struct mszipd_stream *zip) {
158 unsigned int last_block, block_type, distance, length, this_run, i;
159
160 /* for the bit buffer and huffman decoding */
161 register unsigned int bit_buffer;
162 register int bits_left;
163 register unsigned short sym;
164 unsigned char *i_ptr, *i_end;
165
166 RESTORE_BITS;
167
168 do {
169 /* read in last block bit */
170 READ_BITS(last_block, 1);
171
172 /* read in block type */
173 READ_BITS(block_type, 2);
174
175 if (block_type == 0) {
176 /* uncompressed block */
177 unsigned char lens_buf[4];
178
179 /* go to byte boundary */
180 i = bits_left & 7; REMOVE_BITS(i);
181
182 /* read 4 bytes of data, emptying the bit-buffer if necessary */
183 for (i = 0; (bits_left >= 8); i++) {
184 if (i == 4) return INF_ERR_BITBUF;
185 lens_buf[i] = PEEK_BITS(8);
186 REMOVE_BITS(8);
187 }
188 if (bits_left != 0) return INF_ERR_BITBUF;
189 while (i < 4) {
190 READ_IF_NEEDED;
191 lens_buf[i++] = *i_ptr++;
192 }
193
194 /* get the length and its complement */
195 length = lens_buf[0] | (lens_buf[1] << 8);
196 i = lens_buf[2] | (lens_buf[3] << 8);
197 if (length != (~i & 0xFFFF)) return INF_ERR_COMPLEMENT;
198
199 /* read and copy the uncompressed data into the window */
200 while (length > 0) {
201 READ_IF_NEEDED;
202
203 this_run = length;
204 if (this_run > (unsigned int)(i_end - i_ptr)) this_run = i_end - i_ptr;
205 if (this_run > (MSZIP_FRAME_SIZE - zip->window_posn))
206 this_run = MSZIP_FRAME_SIZE - zip->window_posn;
207
208 zip->sys->copy(i_ptr, &zip->window[zip->window_posn], this_run);
209 zip->window_posn += this_run;
210 i_ptr += this_run;
211 length -= this_run;
212 FLUSH_IF_NEEDED;
213 }
214 }
215 else if ((block_type == 1) || (block_type == 2)) {
216 /* Huffman-compressed LZ77 block */
217 unsigned int match_posn, code;
218
219 if (block_type == 1) {
220 /* block with fixed Huffman codes */
221 i = 0;
222 while (i < 144) zip->LITERAL_len[i++] = 8;
223 while (i < 256) zip->LITERAL_len[i++] = 9;
224 while (i < 280) zip->LITERAL_len[i++] = 7;
225 while (i < 288) zip->LITERAL_len[i++] = 8;
226 for (i = 0; i < 32; i++) zip->DISTANCE_len[i] = 5;
227 }
228 else {
229 /* block with dynamic Huffman codes */
230 STORE_BITS;
231 if ((i = zip_read_lens(zip))) return i;
232 RESTORE_BITS;
233 }
234
235 /* now huffman lengths are read for either kind of block,
236 * create huffman decoding tables */
237 if (make_decode_table(MSZIP_LITERAL_MAXSYMBOLS, MSZIP_LITERAL_TABLEBITS,
238 &zip->LITERAL_len[0], &zip->LITERAL_table[0]))
239 {
240 return INF_ERR_LITERALTBL;
241 }
242
243 if (make_decode_table(MSZIP_DISTANCE_MAXSYMBOLS,MSZIP_DISTANCE_TABLEBITS,
244 &zip->DISTANCE_len[0], &zip->DISTANCE_table[0]))
245 {
246 return INF_ERR_DISTANCETBL;
247 }
248
249 /* decode forever until end of block code */
250 for (;;) {
251 READ_HUFFSYM(LITERAL, code);
252 if (code < 256) {
253 zip->window[zip->window_posn++] = (unsigned char) code;
254 FLUSH_IF_NEEDED;
255 }
256 else if (code == 256) {
257 /* END OF BLOCK CODE: loop break point */
258 break;
259 }
260 else {
261 code -= 257; /* codes 257-285 are matches */
262 if (code >= 29) return INF_ERR_LITCODE; /* codes 286-287 are illegal */
263 READ_BITS_T(length, lit_extrabits[code]);
264 length += lit_lengths[code];
265
266 READ_HUFFSYM(DISTANCE, code);
267 if (code >= 30) return INF_ERR_DISTCODE;
268 READ_BITS_T(distance, dist_extrabits[code]);
269 distance += dist_offsets[code];
270
271 /* match position is window position minus distance. If distance
272 * is more than window position numerically, it must 'wrap
273 * around' the frame size. */
274 match_posn = ((distance > zip->window_posn) ? MSZIP_FRAME_SIZE : 0)
275 + zip->window_posn - distance;
276
277 /* copy match */
278 if (length < 12) {
279 /* short match, use slower loop but no loop setup code */
280 while (length--) {
281 zip->window[zip->window_posn++] = zip->window[match_posn++];
282 match_posn &= MSZIP_FRAME_SIZE - 1;
283 FLUSH_IF_NEEDED;
284 }
285 }
286 else {
287 /* longer match, use faster loop but with setup expense */
288 unsigned char *runsrc, *rundest;
289 do {
290 this_run = length;
291 if ((match_posn + this_run) > MSZIP_FRAME_SIZE)
292 this_run = MSZIP_FRAME_SIZE - match_posn;
293 if ((zip->window_posn + this_run) > MSZIP_FRAME_SIZE)
294 this_run = MSZIP_FRAME_SIZE - zip->window_posn;
295
296 rundest = &zip->window[zip->window_posn]; zip->window_posn += this_run;
297 runsrc = &zip->window[match_posn]; match_posn += this_run;
298 length -= this_run;
299 while (this_run--) *rundest++ = *runsrc++;
300 if (match_posn == MSZIP_FRAME_SIZE) match_posn = 0;
301 FLUSH_IF_NEEDED;
302 } while (length > 0);
303 }
304
305 } /* else (code >= 257) */
306
307 } /* for(;;) -- break point at 'code == 256' */
308 }
309 else {
310 /* block_type == 3 -- bad block type */
311 return INF_ERR_BLOCKTYPE;
312 }
313 } while (!last_block);
314
315 /* flush the remaining data */
316 if (zip->window_posn) {
317 if (zip->flush_window(zip, zip->window_posn)) return INF_ERR_FLUSH;
318 }
319 STORE_BITS;
320
321 /* return success */
322 return 0;
323}
324
325/* inflate() calls this whenever the window should be flushed. As
326 * MSZIP only expands to the size of the window, the implementation used
327 * simply keeps track of the amount of data flushed, and if more than 32k
328 * is flushed, an error is raised.
329 */
330static int mszipd_flush_window(struct mszipd_stream *zip,
331 unsigned int data_flushed)
332{
333 zip->bytes_output += data_flushed;
334 if (zip->bytes_output > MSZIP_FRAME_SIZE) {
335 D(("overflow: %u bytes flushed, total is now %u",
336 data_flushed, zip->bytes_output))
337 return 1;
338 }
339 return 0;
340}
341
342struct mszipd_stream *mszipd_init(struct mspack_system *system,
343 struct mspack_file *input,
344 struct mspack_file *output,
345 int input_buffer_size,
346 int repair_mode)
347{
348 struct mszipd_stream *zip;
349
350 if (!system) return NULL;
351
352 /* round up input buffer size to multiple of two */
353 input_buffer_size = (input_buffer_size + 1) & -2;
354 if (input_buffer_size < 2) return NULL;
355
356 /* allocate decompression state */
357 if (!(zip = (struct mszipd_stream *) system->alloc(system, sizeof(struct mszipd_stream)))) {
358 return NULL;
359 }
360
361 /* allocate input buffer */
362 zip->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size);
363 if (!zip->inbuf) {
364 system->free(zip);
365 return NULL;
366 }
367
368 /* initialise decompression state */
369 zip->sys = system;
370 zip->input = input;
371 zip->output = output;
372 zip->inbuf_size = input_buffer_size;
373 zip->input_end = 0;
374 zip->error = MSPACK_ERR_OK;
375 zip->repair_mode = repair_mode;
376 zip->flush_window = &mszipd_flush_window;
377
378 zip->i_ptr = zip->i_end = &zip->inbuf[0];
379 zip->o_ptr = zip->o_end = NULL;
380 zip->bit_buffer = 0; zip->bits_left = 0;
381 return zip;
382}
383
384int mszipd_decompress(struct mszipd_stream *zip, off_t out_bytes) {
385 /* for the bit buffer */
386 register unsigned int bit_buffer;
387 register int bits_left;
388 unsigned char *i_ptr, *i_end;
389
390 int i, state, error;
391
392 /* easy answers */
393 if (!zip || (out_bytes < 0)) return MSPACK_ERR_ARGS;
394 if (zip->error) return zip->error;
395
396 /* flush out any stored-up bytes before we begin */
397 i = zip->o_end - zip->o_ptr;
398 if ((off_t) i > out_bytes) i = (int) out_bytes;
399 if (i) {
400 if (zip->sys->write(zip->output, zip->o_ptr, i) != i) {
401 return zip->error = MSPACK_ERR_WRITE;
402 }
403 zip->o_ptr += i;
404 out_bytes -= i;
405 }
406 if (out_bytes == 0) return MSPACK_ERR_OK;
407
408
409 while (out_bytes > 0) {
410 /* unpack another block */
411 RESTORE_BITS;
412
413 /* skip to next read 'CK' header */
414 i = bits_left & 7; REMOVE_BITS(i); /* align to bytestream */
415 state = 0;
416 do {
417 READ_BITS(i, 8);
418 if (i == 'C') state = 1;
419 else if ((state == 1) && (i == 'K')) state = 2;
420 else state = 0;
421 } while (state != 2);
422
423 /* inflate a block, repair and realign if necessary */
424 zip->window_posn = 0;
425 zip->bytes_output = 0;
426 STORE_BITS;
427 if ((error = inflate(zip))) {
428 D(("inflate error %d", error))
429 if (zip->repair_mode) {
430 /* recover partially-inflated buffers */
431 if (zip->bytes_output == 0 && zip->window_posn > 0) {
432 zip->flush_window(zip, zip->window_posn);
433 }
434 zip->sys->message(NULL, "MSZIP error, %u bytes of data lost.",
435 MSZIP_FRAME_SIZE - zip->bytes_output);
436 for (i = zip->bytes_output; i < MSZIP_FRAME_SIZE; i++) {
437 zip->window[i] = '\0';
438 }
439 zip->bytes_output = MSZIP_FRAME_SIZE;
440 }
441 else {
442 return zip->error = (error > 0) ? error : MSPACK_ERR_DECRUNCH;
443 }
444 }
445 zip->o_ptr = &zip->window[0];
446 zip->o_end = &zip->o_ptr[zip->bytes_output];
447
448 /* write a frame */
449 i = (out_bytes < (off_t)zip->bytes_output) ?
450 (int)out_bytes : zip->bytes_output;
451 if (zip->sys->write(zip->output, zip->o_ptr, i) != i) {
452 return zip->error = MSPACK_ERR_WRITE;
453 }
454
455 /* mspack errors (i.e. read errors) are fatal and can't be recovered */
456 if ((error > 0) && zip->repair_mode) return error;
457
458 zip->o_ptr += i;
459 out_bytes -= i;
460 }
461
462 if (out_bytes) {
463 D(("bytes left to output"))
464 return zip->error = MSPACK_ERR_DECRUNCH;
465 }
466 return MSPACK_ERR_OK;
467}
468
469int mszipd_decompress_kwaj(struct mszipd_stream *zip) {
470 /* for the bit buffer */
471 register unsigned int bit_buffer;
472 register int bits_left;
473 unsigned char *i_ptr, *i_end;
474
475 int i, error, block_len;
476
477 /* unpack blocks until block_len == 0 */
478 for (;;) {
479 RESTORE_BITS;
480
481 /* align to bytestream, read block_len */
482 i = bits_left & 7; REMOVE_BITS(i);
483 READ_BITS(block_len, 8);
484 READ_BITS(i, 8); block_len |= i << 8;
485
486 if (block_len == 0) break;
487
488 /* read "CK" header */
489 READ_BITS(i, 8); if (i != 'C') return MSPACK_ERR_DATAFORMAT;
490 READ_BITS(i, 8); if (i != 'K') return MSPACK_ERR_DATAFORMAT;
491
492 /* inflate block */
493 zip->window_posn = 0;
494 zip->bytes_output = 0;
495 STORE_BITS;
496 if ((error = inflate(zip))) {
497 D(("inflate error %d", error))
498 return zip->error = (error > 0) ? error : MSPACK_ERR_DECRUNCH;
499 }
500
501 /* write inflated block */
502 if (zip->sys->write(zip->output, &zip->window[0], zip->bytes_output)
503 != zip->bytes_output) return zip->error = MSPACK_ERR_WRITE;
504 }
505 return MSPACK_ERR_OK;
506}
507
508void mszipd_free(struct mszipd_stream *zip) {
509 struct mspack_system *sys;
510 if (zip) {
511 sys = zip->sys;
512 sys->free(zip->inbuf);
513 sys->free(zip);
514 }
515}
diff --git a/utils/rbutilqt/mspack/qtm.h b/utils/rbutilqt/mspack/qtm.h
new file mode 100644
index 0000000000..20a38538a2
--- /dev/null
+++ b/utils/rbutilqt/mspack/qtm.h
@@ -0,0 +1,128 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * The Quantum method was created by David Stafford, adapted by Microsoft
5 * Corporation.
6 *
7 * libmspack is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
9 *
10 * For further details, see the file COPYING.LIB distributed with libmspack
11 */
12
13#ifndef MSPACK_QTM_H
14#define MSPACK_QTM_H 1
15
16#ifdef __cplusplus
17extern "C" {
18#endif
19
20/* Quantum compression / decompression definitions */
21
22#define QTM_FRAME_SIZE (32768)
23
24struct qtmd_modelsym {
25 unsigned short sym, cumfreq;
26};
27
28struct qtmd_model {
29 int shiftsleft, entries;
30 struct qtmd_modelsym *syms;
31};
32
33struct qtmd_stream {
34 struct mspack_system *sys; /* I/O routines */
35 struct mspack_file *input; /* input file handle */
36 struct mspack_file *output; /* output file handle */
37
38 unsigned char *window; /* decoding window */
39 unsigned int window_size; /* window size */
40 unsigned int window_posn; /* decompression offset within window */
41 unsigned int frame_todo; /* bytes remaining for current frame */
42
43 unsigned short H, L, C; /* high/low/current: arith coding state */
44 unsigned char header_read; /* have we started decoding a new frame? */
45
46 int error;
47
48 /* I/O buffers */
49 unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end;
50 unsigned int bit_buffer, inbuf_size;
51 unsigned char bits_left, input_end;
52
53 /* four literal models, each representing 64 symbols
54 * model0 for literals from 0 to 63 (selector = 0)
55 * model1 for literals from 64 to 127 (selector = 1)
56 * model2 for literals from 128 to 191 (selector = 2)
57 * model3 for literals from 129 to 255 (selector = 3) */
58 struct qtmd_model model0, model1, model2, model3;
59
60 /* three match models.
61 * model4 for match with fixed length of 3 bytes
62 * model5 for match with fixed length of 4 bytes
63 * model6 for variable length match, encoded with model6len model */
64 struct qtmd_model model4, model5, model6, model6len;
65
66 /* selector model. 0-6 to say literal (0,1,2,3) or match (4,5,6) */
67 struct qtmd_model model7;
68
69 /* symbol arrays for all models */
70 struct qtmd_modelsym m0sym[64 + 1];
71 struct qtmd_modelsym m1sym[64 + 1];
72 struct qtmd_modelsym m2sym[64 + 1];
73 struct qtmd_modelsym m3sym[64 + 1];
74 struct qtmd_modelsym m4sym[24 + 1];
75 struct qtmd_modelsym m5sym[36 + 1];
76 struct qtmd_modelsym m6sym[42 + 1], m6lsym[27 + 1];
77 struct qtmd_modelsym m7sym[7 + 1];
78};
79
80/* allocates Quantum decompression state for decoding the given stream.
81 *
82 * - returns NULL if window_bits is outwith the range 10 to 21 (inclusive).
83 *
84 * - uses system->alloc() to allocate memory
85 *
86 * - returns NULL if not enough memory
87 *
88 * - window_bits is the size of the Quantum window, from 1Kb (10) to 2Mb (21).
89 *
90 * - input_buffer_size is the number of bytes to use to store bitstream data.
91 */
92extern struct qtmd_stream *qtmd_init(struct mspack_system *system,
93 struct mspack_file *input,
94 struct mspack_file *output,
95 int window_bits,
96 int input_buffer_size);
97
98/* decompresses, or decompresses more of, a Quantum stream.
99 *
100 * - out_bytes of data will be decompressed and the function will return
101 * with an MSPACK_ERR_OK return code.
102 *
103 * - decompressing will stop as soon as out_bytes is reached. if the true
104 * amount of bytes decoded spills over that amount, they will be kept for
105 * a later invocation of qtmd_decompress().
106 *
107 * - the output bytes will be passed to the system->write() function given in
108 * qtmd_init(), using the output file handle given in qtmd_init(). More
109 * than one call may be made to system->write()
110 *
111 * - Quantum will read input bytes as necessary using the system->read()
112 * function given in qtmd_init(), using the input file handle given in
113 * qtmd_init(). This will continue until system->read() returns 0 bytes,
114 * or an error.
115 */
116extern int qtmd_decompress(struct qtmd_stream *qtm, off_t out_bytes);
117
118/* frees all state associated with a Quantum data stream
119 *
120 * - calls system->free() using the system pointer given in qtmd_init()
121 */
122void qtmd_free(struct qtmd_stream *qtm);
123
124#ifdef __cplusplus
125}
126#endif
127
128#endif
diff --git a/utils/rbutilqt/mspack/qtmd.c b/utils/rbutilqt/mspack/qtmd.c
new file mode 100644
index 0000000000..58e4787b7f
--- /dev/null
+++ b/utils/rbutilqt/mspack/qtmd.c
@@ -0,0 +1,490 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * The Quantum method was created by David Stafford, adapted by Microsoft
5 * Corporation.
6 *
7 * This decompressor is based on an implementation by Matthew Russotto, used
8 * with permission.
9 *
10 * libmspack is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
12 *
13 * For further details, see the file COPYING.LIB distributed with libmspack
14 */
15
16/* Quantum decompression implementation */
17
18/* This decompressor was researched and implemented by Matthew Russotto. It
19 * has since been tidied up by Stuart Caie. More information can be found at
20 * http://www.speakeasy.org/~russotto/quantumcomp.html
21 */
22
23#include "system-mspack.h"
24#include "qtm.h"
25
26/* import bit-reading macros and code */
27#define BITS_TYPE struct qtmd_stream
28#define BITS_VAR qtm
29#define BITS_ORDER_MSB
30#define READ_BYTES do { \
31 unsigned char b0, b1; \
32 READ_IF_NEEDED; b0 = *i_ptr++; \
33 READ_IF_NEEDED; b1 = *i_ptr++; \
34 INJECT_BITS((b0 << 8) | b1, 16); \
35} while (0)
36#include "readbits.h"
37
38/* Quantum static data tables:
39 *
40 * Quantum uses 'position slots' to represent match offsets. For every
41 * match, a small 'position slot' number and a small offset from that slot
42 * are encoded instead of one large offset.
43 *
44 * position_base[] is an index to the position slot bases
45 *
46 * extra_bits[] states how many bits of offset-from-base data is needed.
47 *
48 * length_base[] and length_extra[] are equivalent in function, but are
49 * used for encoding selector 6 (variable length match) match lengths,
50 * instead of match offsets.
51 *
52 * They are generated with the following code:
53 * unsigned int i, offset;
54 * for (i = 0, offset = 0; i < 42; i++) {
55 * position_base[i] = offset;
56 * extra_bits[i] = ((i < 2) ? 0 : (i - 2)) >> 1;
57 * offset += 1 << extra_bits[i];
58 * }
59 * for (i = 0, offset = 0; i < 26; i++) {
60 * length_base[i] = offset;
61 * length_extra[i] = (i < 2 ? 0 : i - 2) >> 2;
62 * offset += 1 << length_extra[i];
63 * }
64 * length_base[26] = 254; length_extra[26] = 0;
65 */
66static const unsigned int position_base[42] = {
67 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
68 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152,
69 65536, 98304, 131072, 196608, 262144, 393216, 524288, 786432, 1048576, 1572864
70};
71static const unsigned char extra_bits[42] = {
72 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
73 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19
74};
75static const unsigned char length_base[27] = {
76 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 18, 22, 26,
77 30, 38, 46, 54, 62, 78, 94, 110, 126, 158, 190, 222, 254
78};
79static const unsigned char length_extra[27] = {
80 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
81 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
82};
83
84
85/* Arithmetic decoder:
86 *
87 * GET_SYMBOL(model, var) fetches the next symbol from the stated model
88 * and puts it in var.
89 *
90 * If necessary, qtmd_update_model() is called.
91 */
92#define GET_SYMBOL(model, var) do { \
93 range = ((H - L) & 0xFFFF) + 1; \
94 symf = ((((C - L + 1) * model.syms[0].cumfreq)-1) / range) & 0xFFFF; \
95 \
96 for (i = 1; i < model.entries; i++) { \
97 if (model.syms[i].cumfreq <= symf) break; \
98 } \
99 (var) = model.syms[i-1].sym; \
100 \
101 range = (H - L) + 1; \
102 symf = model.syms[0].cumfreq; \
103 H = L + ((model.syms[i-1].cumfreq * range) / symf) - 1; \
104 L = L + ((model.syms[i].cumfreq * range) / symf); \
105 \
106 do { model.syms[--i].cumfreq += 8; } while (i > 0); \
107 if (model.syms[0].cumfreq > 3800) qtmd_update_model(&model); \
108 \
109 while (1) { \
110 if ((L & 0x8000) != (H & 0x8000)) { \
111 if ((L & 0x4000) && !(H & 0x4000)) { \
112 /* underflow case */ \
113 C ^= 0x4000; L &= 0x3FFF; H |= 0x4000; \
114 } \
115 else break; \
116 } \
117 L <<= 1; H = (H << 1) | 1; \
118 ENSURE_BITS(1); \
119 C = (C << 1) | PEEK_BITS(1); \
120 REMOVE_BITS(1); \
121 } \
122} while (0)
123
124static void qtmd_update_model(struct qtmd_model *model) {
125 struct qtmd_modelsym tmp;
126 int i, j;
127
128 if (--model->shiftsleft) {
129 for (i = model->entries - 1; i >= 0; i--) {
130 /* -1, not -2; the 0 entry saves this */
131 model->syms[i].cumfreq >>= 1;
132 if (model->syms[i].cumfreq <= model->syms[i+1].cumfreq) {
133 model->syms[i].cumfreq = model->syms[i+1].cumfreq + 1;
134 }
135 }
136 }
137 else {
138 model->shiftsleft = 50;
139 for (i = 0; i < model->entries; i++) {
140 /* no -1, want to include the 0 entry */
141 /* this converts cumfreqs into frequencies, then shifts right */
142 model->syms[i].cumfreq -= model->syms[i+1].cumfreq;
143 model->syms[i].cumfreq++; /* avoid losing things entirely */
144 model->syms[i].cumfreq >>= 1;
145 }
146
147 /* now sort by frequencies, decreasing order -- this must be an
148 * inplace selection sort, or a sort with the same (in)stability
149 * characteristics */
150 for (i = 0; i < model->entries - 1; i++) {
151 for (j = i + 1; j < model->entries; j++) {
152 if (model->syms[i].cumfreq < model->syms[j].cumfreq) {
153 tmp = model->syms[i];
154 model->syms[i] = model->syms[j];
155 model->syms[j] = tmp;
156 }
157 }
158 }
159
160 /* then convert frequencies back to cumfreq */
161 for (i = model->entries - 1; i >= 0; i--) {
162 model->syms[i].cumfreq += model->syms[i+1].cumfreq;
163 }
164 }
165}
166
167/* Initialises a model to decode symbols from [start] to [start]+[len]-1 */
168static void qtmd_init_model(struct qtmd_model *model,
169 struct qtmd_modelsym *syms, int start, int len)
170{
171 int i;
172
173 model->shiftsleft = 4;
174 model->entries = len;
175 model->syms = syms;
176
177 for (i = 0; i <= len; i++) {
178 syms[i].sym = start + i; /* actual symbol */
179 syms[i].cumfreq = len - i; /* current frequency of that symbol */
180 }
181}
182
183
184/*-------- main Quantum code --------*/
185
186struct qtmd_stream *qtmd_init(struct mspack_system *system,
187 struct mspack_file *input,
188 struct mspack_file *output,
189 int window_bits, int input_buffer_size)
190{
191 unsigned int window_size = 1 << window_bits;
192 struct qtmd_stream *qtm;
193 int i;
194
195 if (!system) return NULL;
196
197 /* Quantum supports window sizes of 2^10 (1Kb) through 2^21 (2Mb) */
198 if (window_bits < 10 || window_bits > 21) return NULL;
199
200 /* round up input buffer size to multiple of two */
201 input_buffer_size = (input_buffer_size + 1) & -2;
202 if (input_buffer_size < 2) return NULL;
203
204 /* allocate decompression state */
205 if (!(qtm = (struct qtmd_stream *) system->alloc(system, sizeof(struct qtmd_stream)))) {
206 return NULL;
207 }
208
209 /* allocate decompression window and input buffer */
210 qtm->window = (unsigned char *) system->alloc(system, (size_t) window_size);
211 qtm->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size);
212 if (!qtm->window || !qtm->inbuf) {
213 system->free(qtm->window);
214 system->free(qtm->inbuf);
215 system->free(qtm);
216 return NULL;
217 }
218
219 /* initialise decompression state */
220 qtm->sys = system;
221 qtm->input = input;
222 qtm->output = output;
223 qtm->inbuf_size = input_buffer_size;
224 qtm->window_size = window_size;
225 qtm->window_posn = 0;
226 qtm->frame_todo = QTM_FRAME_SIZE;
227 qtm->header_read = 0;
228 qtm->error = MSPACK_ERR_OK;
229
230 qtm->i_ptr = qtm->i_end = &qtm->inbuf[0];
231 qtm->o_ptr = qtm->o_end = &qtm->window[0];
232 qtm->input_end = 0;
233 qtm->bits_left = 0;
234 qtm->bit_buffer = 0;
235
236 /* initialise arithmetic coding models
237 * - model 4 depends on window size, ranges from 20 to 24
238 * - model 5 depends on window size, ranges from 20 to 36
239 * - model 6pos depends on window size, ranges from 20 to 42
240 */
241 i = window_bits * 2;
242 qtmd_init_model(&qtm->model0, &qtm->m0sym[0], 0, 64);
243 qtmd_init_model(&qtm->model1, &qtm->m1sym[0], 64, 64);
244 qtmd_init_model(&qtm->model2, &qtm->m2sym[0], 128, 64);
245 qtmd_init_model(&qtm->model3, &qtm->m3sym[0], 192, 64);
246 qtmd_init_model(&qtm->model4, &qtm->m4sym[0], 0, (i > 24) ? 24 : i);
247 qtmd_init_model(&qtm->model5, &qtm->m5sym[0], 0, (i > 36) ? 36 : i);
248 qtmd_init_model(&qtm->model6, &qtm->m6sym[0], 0, i);
249 qtmd_init_model(&qtm->model6len, &qtm->m6lsym[0], 0, 27);
250 qtmd_init_model(&qtm->model7, &qtm->m7sym[0], 0, 7);
251
252 /* all ok */
253 return qtm;
254}
255
256int qtmd_decompress(struct qtmd_stream *qtm, off_t out_bytes) {
257 unsigned int frame_todo, frame_end, window_posn, match_offset, range;
258 unsigned char *window, *i_ptr, *i_end, *runsrc, *rundest;
259 int i, j, selector, extra, sym, match_length;
260 unsigned short H, L, C, symf;
261
262 register unsigned int bit_buffer;
263 register unsigned char bits_left;
264
265 /* easy answers */
266 if (!qtm || (out_bytes < 0)) return MSPACK_ERR_ARGS;
267 if (qtm->error) return qtm->error;
268
269 /* flush out any stored-up bytes before we begin */
270 i = qtm->o_end - qtm->o_ptr;
271 if ((off_t) i > out_bytes) i = (int) out_bytes;
272 if (i) {
273 if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) {
274 return qtm->error = MSPACK_ERR_WRITE;
275 }
276 qtm->o_ptr += i;
277 out_bytes -= i;
278 }
279 if (out_bytes == 0) return MSPACK_ERR_OK;
280
281 /* restore local state */
282 RESTORE_BITS;
283 window = qtm->window;
284 window_posn = qtm->window_posn;
285 frame_todo = qtm->frame_todo;
286 H = qtm->H;
287 L = qtm->L;
288 C = qtm->C;
289
290 /* while we do not have enough decoded bytes in reserve: */
291 while ((qtm->o_end - qtm->o_ptr) < out_bytes) {
292 /* read header if necessary. Initialises H, L and C */
293 if (!qtm->header_read) {
294 H = 0xFFFF; L = 0; READ_BITS(C, 16);
295 qtm->header_read = 1;
296 }
297
298 /* decode more, up to the number of bytes needed, the frame boundary,
299 * or the window boundary, whichever comes first */
300 frame_end = window_posn + (out_bytes - (qtm->o_end - qtm->o_ptr));
301 if ((window_posn + frame_todo) < frame_end) {
302 frame_end = window_posn + frame_todo;
303 }
304 if (frame_end > qtm->window_size) {
305 frame_end = qtm->window_size;
306 }
307
308 while (window_posn < frame_end) {
309 GET_SYMBOL(qtm->model7, selector);
310 if (selector < 4) {
311 /* literal byte */
312 struct qtmd_model *mdl = (selector == 0) ? &qtm->model0 :
313 ((selector == 1) ? &qtm->model1 :
314 ((selector == 2) ? &qtm->model2 :
315 &qtm->model3));
316 GET_SYMBOL((*mdl), sym);
317 window[window_posn++] = sym;
318 frame_todo--;
319 }
320 else {
321 /* match repeated string */
322 switch (selector) {
323 case 4: /* selector 4 = fixed length match (3 bytes) */
324 GET_SYMBOL(qtm->model4, sym);
325 READ_MANY_BITS(extra, extra_bits[sym]);
326 match_offset = position_base[sym] + extra + 1;
327 match_length = 3;
328 break;
329
330 case 5: /* selector 5 = fixed length match (4 bytes) */
331 GET_SYMBOL(qtm->model5, sym);
332 READ_MANY_BITS(extra, extra_bits[sym]);
333 match_offset = position_base[sym] + extra + 1;
334 match_length = 4;
335 break;
336
337 case 6: /* selector 6 = variable length match */
338 GET_SYMBOL(qtm->model6len, sym);
339 READ_MANY_BITS(extra, length_extra[sym]);
340 match_length = length_base[sym] + extra + 5;
341
342 GET_SYMBOL(qtm->model6, sym);
343 READ_MANY_BITS(extra, extra_bits[sym]);
344 match_offset = position_base[sym] + extra + 1;
345 break;
346
347 default:
348 /* should be impossible, model7 can only return 0-6 */
349 D(("got %d from selector", selector))
350 return qtm->error = MSPACK_ERR_DECRUNCH;
351 }
352
353 rundest = &window[window_posn];
354 frame_todo -= match_length;
355
356 /* does match destination wrap the window? This situation is possible
357 * where the window size is less than the 32k frame size, but matches
358 * must not go beyond a frame boundary */
359 if ((window_posn + match_length) > qtm->window_size) {
360 /* copy first part of match, before window end */
361 i = qtm->window_size - window_posn;
362 j = window_posn - match_offset;
363 while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)];
364
365 /* flush currently stored data */
366 i = (&window[qtm->window_size] - qtm->o_ptr);
367
368 /* this should not happen, but if it does then this code
369 * can't handle the situation (can't flush up to the end of
370 * the window, but can't break out either because we haven't
371 * finished writing the match). bail out in this case */
372 if (i > out_bytes) {
373 D(("during window-wrap match; %d bytes to flush but only need %d",
374 i, (int) out_bytes))
375 return qtm->error = MSPACK_ERR_DECRUNCH;
376 }
377 if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) {
378 return qtm->error = MSPACK_ERR_WRITE;
379 }
380 out_bytes -= i;
381 qtm->o_ptr = &window[0];
382 qtm->o_end = &window[0];
383
384 /* copy second part of match, after window wrap */
385 rundest = &window[0];
386 i = match_length - (qtm->window_size - window_posn);
387 while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)];
388 window_posn = window_posn + match_length - qtm->window_size;
389
390 break; /* because "window_posn < frame_end" has now failed */
391 }
392 else {
393 /* normal match - output won't wrap window or frame end */
394 i = match_length;
395
396 /* does match _offset_ wrap the window? */
397 if (match_offset > window_posn) {
398 /* j = length from match offset to end of window */
399 j = match_offset - window_posn;
400 if (j > (int) qtm->window_size) {
401 D(("match offset beyond window boundaries"))
402 return qtm->error = MSPACK_ERR_DECRUNCH;
403 }
404 runsrc = &window[qtm->window_size - j];
405 if (j < i) {
406 /* if match goes over the window edge, do two copy runs */
407 i -= j; while (j-- > 0) *rundest++ = *runsrc++;
408 runsrc = window;
409 }
410 while (i-- > 0) *rundest++ = *runsrc++;
411 }
412 else {
413 runsrc = rundest - match_offset;
414 while (i-- > 0) *rundest++ = *runsrc++;
415 }
416 window_posn += match_length;
417 }
418 } /* if (window_posn+match_length > frame_end) */
419 } /* while (window_posn < frame_end) */
420
421 qtm->o_end = &window[window_posn];
422
423 /* if we subtracted too much from frame_todo, it will
424 * wrap around past zero and go above its max value */
425 if (frame_todo > QTM_FRAME_SIZE) {
426 D(("overshot frame alignment"))
427 return qtm->error = MSPACK_ERR_DECRUNCH;
428 }
429
430 /* another frame completed? */
431 if (frame_todo == 0) {
432 /* re-align input */
433 if (bits_left & 7) REMOVE_BITS(bits_left & 7);
434
435 /* special Quantum hack -- cabd.c injects a trailer byte to allow the
436 * decompressor to realign itself. CAB Quantum blocks, unlike LZX
437 * blocks, can have anything from 0 to 4 trailing null bytes. */
438 do { READ_BITS(i, 8); } while (i != 0xFF);
439
440 qtm->header_read = 0;
441
442 frame_todo = QTM_FRAME_SIZE;
443 }
444
445 /* window wrap? */
446 if (window_posn == qtm->window_size) {
447 /* flush all currently stored data */
448 i = (qtm->o_end - qtm->o_ptr);
449 /* break out if we have more than enough to finish this request */
450 if (i >= out_bytes) break;
451 if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) {
452 return qtm->error = MSPACK_ERR_WRITE;
453 }
454 out_bytes -= i;
455 qtm->o_ptr = &window[0];
456 qtm->o_end = &window[0];
457 window_posn = 0;
458 }
459
460 } /* while (more bytes needed) */
461
462 if (out_bytes) {
463 i = (int) out_bytes;
464 if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) {
465 return qtm->error = MSPACK_ERR_WRITE;
466 }
467 qtm->o_ptr += i;
468 }
469
470 /* store local state */
471
472 STORE_BITS;
473 qtm->window_posn = window_posn;
474 qtm->frame_todo = frame_todo;
475 qtm->H = H;
476 qtm->L = L;
477 qtm->C = C;
478
479 return MSPACK_ERR_OK;
480}
481
482void qtmd_free(struct qtmd_stream *qtm) {
483 struct mspack_system *sys;
484 if (qtm) {
485 sys = qtm->sys;
486 sys->free(qtm->window);
487 sys->free(qtm->inbuf);
488 sys->free(qtm);
489 }
490}
diff --git a/utils/rbutilqt/mspack/readbits.h b/utils/rbutilqt/mspack/readbits.h
new file mode 100644
index 0000000000..9b237a3693
--- /dev/null
+++ b/utils/rbutilqt/mspack/readbits.h
@@ -0,0 +1,207 @@
1/* This file is part of libmspack.
2 * (C) 2003-2010 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifndef MSPACK_READBITS_H
11#define MSPACK_READBITS_H 1
12
13/* this header defines macros that read data streams by
14 * the individual bits
15 *
16 * INIT_BITS initialises bitstream state in state structure
17 * STORE_BITS stores bitstream state in state structure
18 * RESTORE_BITS restores bitstream state from state structure
19 * ENSURE_BITS(n) ensure there are at least N bits in the bit buffer
20 * READ_BITS(var,n) takes N bits from the buffer and puts them in var
21 * PEEK_BITS(n) extracts without removing N bits from the bit buffer
22 * REMOVE_BITS(n) removes N bits from the bit buffer
23 *
24 * READ_BITS simply calls ENSURE_BITS, PEEK_BITS and REMOVE_BITS,
25 * which means it's limited to reading the number of bits you can
26 * ensure at any one time. It also fails if asked to read zero bits.
27 * If you need to read zero bits, or more bits than can be ensured in
28 * one go, use READ_MANY_BITS instead.
29 *
30 * These macros have variable names baked into them, so to use them
31 * you have to define some macros:
32 * - BITS_TYPE: the type name of your state structure
33 * - BITS_VAR: the variable that points to your state structure
34 * - define BITS_ORDER_MSB if bits are read from the MSB, or
35 * define BITS_ORDER_LSB if bits are read from the LSB
36 * - READ_BYTES: some code that reads more data into the bit buffer,
37 * it should use READ_IF_NEEDED (calls read_input if the byte buffer
38 * is empty), then INJECT_BITS(data,n) to put data from the byte
39 * buffer into the bit buffer.
40 *
41 * You also need to define some variables and structure members:
42 * - unsigned char *i_ptr; // current position in the byte buffer
43 * - unsigned char *i_end; // end of the byte buffer
44 * - unsigned int bit_buffer; // the bit buffer itself
45 * - unsigned int bits_left; // number of bits remaining
46 *
47 * If you use read_input() and READ_IF_NEEDED, they also expect these
48 * structure members:
49 * - struct mspack_system *sys; // to access sys->read()
50 * - unsigned int error; // to record/return read errors
51 * - unsigned char input_end; // to mark reaching the EOF
52 * - unsigned char *inbuf; // the input byte buffer
53 * - unsigned int inbuf_size; // the size of the input byte buffer
54 *
55 * Your READ_BYTES implementation should read data from *i_ptr and
56 * put them in the bit buffer. READ_IF_NEEDED will call read_input()
57 * if i_ptr reaches i_end, and will fill up inbuf and set i_ptr to
58 * the start of inbuf and i_end to the end of inbuf.
59 *
60 * If you're reading in MSB order, the routines work by using the area
61 * beyond the MSB and the LSB of the bit buffer as a free source of
62 * zeroes when shifting. This avoids having to mask any bits. So we
63 * have to know the bit width of the bit buffer variable. We use
64 * <limits.h> and CHAR_BIT to find the size of the bit buffer in bits.
65 *
66 * If you are reading in LSB order, bits need to be masked. Normally
67 * this is done by computing the mask: N bits are masked by the value
68 * (1<<N)-1). However, you can define BITS_LSB_TABLE to use a lookup
69 * table instead of computing this. This adds two new macros,
70 * PEEK_BITS_T and READ_BITS_T which work the same way as PEEK_BITS
71 * and READ_BITS, except they use this lookup table. This is useful if
72 * you need to look up a number of bits that are only known at
73 * runtime, so the bit mask can't be turned into a constant by the
74 * compiler.
75
76 * The bit buffer datatype should be at least 32 bits wide: it must be
77 * possible to ENSURE_BITS(17), so it must be possible to add 16 new bits
78 * to the bit buffer when the bit buffer already has 1 to 15 bits left.
79 */
80
81#ifndef BITS_VAR
82# error "define BITS_VAR as the state structure poiner variable name"
83#endif
84#ifndef BITS_TYPE
85# error "define BITS_TYPE as the state structure type"
86#endif
87#if defined(BITS_ORDER_MSB) && defined(BITS_ORDER_LSB)
88# error "you must define either BITS_ORDER_MSB or BITS_ORDER_LSB"
89#else
90# if !(defined(BITS_ORDER_MSB) || defined(BITS_ORDER_LSB))
91# error "you must define BITS_ORDER_MSB or BITS_ORDER_LSB"
92# endif
93#endif
94
95#if HAVE_LIMITS_H
96# include <limits.h>
97#endif
98#ifndef CHAR_BIT
99# define CHAR_BIT (8)
100#endif
101#define BITBUF_WIDTH (sizeof(bit_buffer) * CHAR_BIT)
102
103#define INIT_BITS do { \
104 BITS_VAR->i_ptr = &BITS_VAR->inbuf[0]; \
105 BITS_VAR->i_end = &BITS_VAR->inbuf[0]; \
106 BITS_VAR->bit_buffer = 0; \
107 BITS_VAR->bits_left = 0; \
108 BITS_VAR->input_end = 0; \
109} while (0)
110
111#define STORE_BITS do { \
112 BITS_VAR->i_ptr = i_ptr; \
113 BITS_VAR->i_end = i_end; \
114 BITS_VAR->bit_buffer = bit_buffer; \
115 BITS_VAR->bits_left = bits_left; \
116} while (0)
117
118#define RESTORE_BITS do { \
119 i_ptr = BITS_VAR->i_ptr; \
120 i_end = BITS_VAR->i_end; \
121 bit_buffer = BITS_VAR->bit_buffer; \
122 bits_left = BITS_VAR->bits_left; \
123} while (0)
124
125#define ENSURE_BITS(nbits) do { \
126 while (bits_left < (nbits)) READ_BYTES; \
127} while (0)
128
129#define READ_BITS(val, nbits) do { \
130 ENSURE_BITS(nbits); \
131 (val) = PEEK_BITS(nbits); \
132 REMOVE_BITS(nbits); \
133} while (0)
134
135#define READ_MANY_BITS(val, bits) do { \
136 unsigned char needed = (bits), bitrun; \
137 (val) = 0; \
138 while (needed > 0) { \
139 if (bits_left <= (BITBUF_WIDTH - 16)) READ_BYTES; \
140 bitrun = (bits_left < needed) ? bits_left : needed; \
141 (val) = ((val) << bitrun) | PEEK_BITS(bitrun); \
142 REMOVE_BITS(bitrun); \
143 needed -= bitrun; \
144 } \
145} while (0)
146
147#ifdef BITS_ORDER_MSB
148# define PEEK_BITS(nbits) (bit_buffer >> (BITBUF_WIDTH - (nbits)))
149# define REMOVE_BITS(nbits) ((bit_buffer <<= (nbits)), (bits_left -= (nbits)))
150# define INJECT_BITS(bitdata,nbits) ((bit_buffer |= \
151 (bitdata) << (BITBUF_WIDTH - (nbits) - bits_left)), (bits_left += (nbits)))
152#else /* BITS_ORDER_LSB */
153# define PEEK_BITS(nbits) (bit_buffer & ((1 << (nbits))-1))
154# define REMOVE_BITS(nbits) ((bit_buffer >>= (nbits)), (bits_left -= (nbits)))
155# define INJECT_BITS(bitdata,nbits) ((bit_buffer |= \
156 (bitdata) << bits_left), (bits_left += (nbits)))
157#endif
158
159#ifdef BITS_LSB_TABLE
160/* lsb_bit_mask[n] = (1 << n) - 1 */
161static const unsigned short lsb_bit_mask[17] = {
162 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
163 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
164};
165# define PEEK_BITS_T(nbits) (bit_buffer & lsb_bit_mask[(nbits)])
166# define READ_BITS_T(val, nbits) do { \
167 ENSURE_BITS(nbits); \
168 (val) = PEEK_BITS_T(nbits); \
169 REMOVE_BITS(nbits); \
170} while (0)
171#endif
172
173#ifndef BITS_NO_READ_INPUT
174# define READ_IF_NEEDED do { \
175 if (i_ptr >= i_end) { \
176 if (read_input(BITS_VAR)) \
177 return BITS_VAR->error; \
178 i_ptr = BITS_VAR->i_ptr; \
179 i_end = BITS_VAR->i_end; \
180 } \
181} while (0)
182
183static int read_input(BITS_TYPE *p) {
184 int read = p->sys->read(p->input, &p->inbuf[0], (int)p->inbuf_size);
185 if (read < 0) return p->error = MSPACK_ERR_READ;
186
187 /* we might overrun the input stream by asking for bits we don't use,
188 * so fake 2 more bytes at the end of input */
189 if (read == 0) {
190 if (p->input_end) {
191 D(("out of input bytes"))
192 return p->error = MSPACK_ERR_READ;
193 }
194 else {
195 read = 2;
196 p->inbuf[0] = p->inbuf[1] = 0;
197 p->input_end = 1;
198 }
199 }
200
201 /* update i_ptr and i_end */
202 p->i_ptr = &p->inbuf[0];
203 p->i_end = &p->inbuf[read];
204 return MSPACK_ERR_OK;
205}
206#endif
207#endif
diff --git a/utils/rbutilqt/mspack/readhuff.h b/utils/rbutilqt/mspack/readhuff.h
new file mode 100644
index 0000000000..4d94225789
--- /dev/null
+++ b/utils/rbutilqt/mspack/readhuff.h
@@ -0,0 +1,172 @@
1/* This file is part of libmspack.
2 * (C) 2003-2014 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifndef MSPACK_READHUFF_H
11#define MSPACK_READHUFF_H 1
12
13/* This implements a fast Huffman tree decoding system. */
14
15#if !(defined(BITS_ORDER_MSB) || defined(BITS_ORDER_LSB))
16# error "readhuff.h is used in conjunction with readbits.h, include that first"
17#endif
18#if !(defined(TABLEBITS) && defined(MAXSYMBOLS))
19# error "define TABLEBITS(tbl) and MAXSYMBOLS(tbl) before using readhuff.h"
20#endif
21#if !(defined(HUFF_TABLE) && defined(HUFF_LEN))
22# error "define HUFF_TABLE(tbl) and HUFF_LEN(tbl) before using readhuff.h"
23#endif
24#ifndef HUFF_ERROR
25# error "define HUFF_ERROR before using readhuff.h"
26#endif
27#ifndef HUFF_MAXBITS
28# define HUFF_MAXBITS 16
29#endif
30
31/* Decodes the next huffman symbol from the input bitstream into var.
32 * Do not use this macro on a table unless build_decode_table() succeeded.
33 */
34#define READ_HUFFSYM(tbl, var) do { \
35 ENSURE_BITS(HUFF_MAXBITS); \
36 sym = HUFF_TABLE(tbl, PEEK_BITS(TABLEBITS(tbl))); \
37 if (sym >= MAXSYMBOLS(tbl)) HUFF_TRAVERSE(tbl); \
38 (var) = sym; \
39 i = HUFF_LEN(tbl, sym); \
40 REMOVE_BITS(i); \
41} while (0)
42
43#ifdef BITS_ORDER_LSB
44# define HUFF_TRAVERSE(tbl) do { \
45 i = TABLEBITS(tbl) - 1; \
46 do { \
47 if (i++ > HUFF_MAXBITS) HUFF_ERROR; \
48 sym = HUFF_TABLE(tbl, \
49 (sym << 1) | ((bit_buffer >> i) & 1)); \
50 } while (sym >= MAXSYMBOLS(tbl)); \
51} while (0)
52#else
53#define HUFF_TRAVERSE(tbl) do { \
54 i = 1 << (BITBUF_WIDTH - TABLEBITS(tbl)); \
55 do { \
56 if ((i >>= 1) == 0) HUFF_ERROR; \
57 sym = HUFF_TABLE(tbl, \
58 (sym << 1) | ((bit_buffer & i) ? 1 : 0)); \
59 } while (sym >= MAXSYMBOLS(tbl)); \
60} while (0)
61#endif
62
63/* make_decode_table(nsyms, nbits, length[], table[])
64 *
65 * This function was originally coded by David Tritscher.
66 * It builds a fast huffman decoding table from
67 * a canonical huffman code lengths table.
68 *
69 * nsyms = total number of symbols in this huffman tree.
70 * nbits = any symbols with a code length of nbits or less can be decoded
71 * in one lookup of the table.
72 * length = A table to get code lengths from [0 to nsyms-1]
73 * table = The table to fill up with decoded symbols and pointers.
74 * Should be ((1<<nbits) + (nsyms*2)) in length.
75 *
76 * Returns 0 for OK or 1 for error
77 */
78static int make_decode_table(unsigned int nsyms, unsigned int nbits,
79 unsigned char *length, unsigned short *table)
80{
81 register unsigned short sym, next_symbol;
82 register unsigned int leaf, fill;
83#ifdef BITS_ORDER_LSB
84 register unsigned int reverse;
85#endif
86 register unsigned char bit_num;
87 unsigned int pos = 0; /* the current position in the decode table */
88 unsigned int table_mask = 1 << nbits;
89 unsigned int bit_mask = table_mask >> 1; /* don't do 0 length codes */
90
91 /* fill entries for codes short enough for a direct mapping */
92 for (bit_num = 1; bit_num <= nbits; bit_num++) {
93 for (sym = 0; sym < nsyms; sym++) {
94 if (length[sym] != bit_num) continue;
95#ifdef BITS_ORDER_MSB
96 leaf = pos;
97#else
98 /* reverse the significant bits */
99 fill = length[sym]; reverse = pos >> (nbits - fill); leaf = 0;
100 do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill);
101#endif
102
103 if((pos += bit_mask) > table_mask) return 1; /* table overrun */
104
105 /* fill all possible lookups of this symbol with the symbol itself */
106#ifdef BITS_ORDER_MSB
107 for (fill = bit_mask; fill-- > 0;) table[leaf++] = sym;
108#else
109 fill = bit_mask; next_symbol = 1 << bit_num;
110 do { table[leaf] = sym; leaf += next_symbol; } while (--fill);
111#endif
112 }
113 bit_mask >>= 1;
114 }
115
116 /* exit with success if table is now complete */
117 if (pos == table_mask) return 0;
118
119 /* mark all remaining table entries as unused */
120 for (sym = pos; sym < table_mask; sym++) {
121#ifdef BITS_ORDER_MSB
122 table[sym] = 0xFFFF;
123#else
124 reverse = sym; leaf = 0; fill = nbits;
125 do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill);
126 table[leaf] = 0xFFFF;
127#endif
128 }
129
130 /* next_symbol = base of allocation for long codes */
131 next_symbol = ((table_mask >> 1) < nsyms) ? nsyms : (table_mask >> 1);
132
133 /* give ourselves room for codes to grow by up to 16 more bits.
134 * codes now start at bit nbits+16 and end at (nbits+16-codelength) */
135 pos <<= 16;
136 table_mask <<= 16;
137 bit_mask = 1 << 15;
138
139 for (bit_num = nbits+1; bit_num <= HUFF_MAXBITS; bit_num++) {
140 for (sym = 0; sym < nsyms; sym++) {
141 if (length[sym] != bit_num) continue;
142 if (pos >= table_mask) return 1; /* table overflow */
143
144#ifdef BITS_ORDER_MSB
145 leaf = pos >> 16;
146#else
147 /* leaf = the first nbits of the code, reversed */
148 reverse = pos >> 16; leaf = 0; fill = nbits;
149 do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill);
150#endif
151 for (fill = 0; fill < (bit_num - nbits); fill++) {
152 /* if this path hasn't been taken yet, 'allocate' two entries */
153 if (table[leaf] == 0xFFFF) {
154 table[(next_symbol << 1) ] = 0xFFFF;
155 table[(next_symbol << 1) + 1 ] = 0xFFFF;
156 table[leaf] = next_symbol++;
157 }
158
159 /* follow the path and select either left or right for next bit */
160 leaf = table[leaf] << 1;
161 if ((pos >> (15-fill)) & 1) leaf++;
162 }
163 table[leaf] = sym;
164 pos += bit_mask;
165 }
166 bit_mask >>= 1;
167 }
168
169 /* full table? */
170 return (pos == table_mask) ? 0 : 1;
171}
172#endif
diff --git a/utils/rbutilqt/mspack/sha.h b/utils/rbutilqt/mspack/sha.h
new file mode 100644
index 0000000000..360521519b
--- /dev/null
+++ b/utils/rbutilqt/mspack/sha.h
@@ -0,0 +1,15 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifndef MSPACK_LZSS_H
11#define MSPACK_LZSS_H 1
12
13/* SHA-1 message digest definitions */
14
15#endif
diff --git a/utils/rbutilqt/mspack/system-mspack.c b/utils/rbutilqt/mspack/system-mspack.c
new file mode 100644
index 0000000000..9d4886a8db
--- /dev/null
+++ b/utils/rbutilqt/mspack/system-mspack.c
@@ -0,0 +1,240 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifdef HAVE_CONFIG_H
11# include "config.h"
12#endif
13
14#include "system-mspack.h"
15
16#ifndef LARGEFILE_SUPPORT
17const char *largefile_msg = "library not compiled to support large files.";
18#endif
19
20
21int mspack_version(int entity) {
22 switch (entity) {
23 /* CHM decoder version 1 -> 2 changes:
24 * - added mschmd_sec_mscompressed::spaninfo
25 * - added mschmd_header::first_pmgl
26 * - added mschmd_header::last_pmgl
27 * - added mschmd_header::chunk_cache;
28 */
29 case MSPACK_VER_MSCHMD:
30 return 2;
31 case MSPACK_VER_LIBRARY:
32 case MSPACK_VER_SYSTEM:
33 case MSPACK_VER_MSCABD:
34 case MSPACK_VER_MSSZDDD:
35 case MSPACK_VER_MSKWAJD:
36 return 1;
37 case MSPACK_VER_MSCABC:
38 case MSPACK_VER_MSCHMC:
39 case MSPACK_VER_MSLITD:
40 case MSPACK_VER_MSLITC:
41 case MSPACK_VER_MSHLPD:
42 case MSPACK_VER_MSHLPC:
43 case MSPACK_VER_MSSZDDC:
44 case MSPACK_VER_MSKWAJC:
45 return 0;
46 }
47 return -1;
48}
49
50int mspack_sys_selftest_internal(int offt_size) {
51 return (sizeof(off_t) == offt_size) ? MSPACK_ERR_OK : MSPACK_ERR_SEEK;
52}
53
54/* validates a system structure */
55int mspack_valid_system(struct mspack_system *sys) {
56 return (sys != NULL) && (sys->open != NULL) && (sys->close != NULL) &&
57 (sys->read != NULL) && (sys->write != NULL) && (sys->seek != NULL) &&
58 (sys->tell != NULL) && (sys->message != NULL) && (sys->alloc != NULL) &&
59 (sys->free != NULL) && (sys->copy != NULL) && (sys->null_ptr == NULL);
60}
61
62/* returns the length of a file opened for reading */
63int mspack_sys_filelen(struct mspack_system *system,
64 struct mspack_file *file, off_t *length)
65{
66 off_t current;
67
68 if (!system || !file || !length) return MSPACK_ERR_OPEN;
69
70 /* get current offset */
71 current = system->tell(file);
72
73 /* seek to end of file */
74 if (system->seek(file, (off_t) 0, MSPACK_SYS_SEEK_END)) {
75 return MSPACK_ERR_SEEK;
76 }
77
78 /* get offset of end of file */
79 *length = system->tell(file);
80
81 /* seek back to original offset */
82 if (system->seek(file, current, MSPACK_SYS_SEEK_START)) {
83 return MSPACK_ERR_SEEK;
84 }
85
86 return MSPACK_ERR_OK;
87}
88
89
90
91/* definition of mspack_default_system -- if the library is compiled with
92 * MSPACK_NO_DEFAULT_SYSTEM, no default system will be provided. Otherwise,
93 * an appropriate default system (e.g. the standard C library, or some native
94 * API calls)
95 */
96
97#ifdef MSPACK_NO_DEFAULT_SYSTEM
98struct mspack_system *mspack_default_system = NULL;
99#else
100
101/* implementation of mspack_default_system for standard C library */
102
103#include <stdio.h>
104#include <stdlib.h>
105#include <string.h>
106#include <stdarg.h>
107
108struct mspack_file_p {
109 FILE *fh;
110 const char *name;
111};
112
113static struct mspack_file *msp_open(struct mspack_system *self,
114 const char *filename, int mode)
115{
116 struct mspack_file_p *fh;
117 const char *fmode;
118 (void)self;
119
120 switch (mode) {
121 case MSPACK_SYS_OPEN_READ: fmode = "rb"; break;
122 case MSPACK_SYS_OPEN_WRITE: fmode = "wb"; break;
123 case MSPACK_SYS_OPEN_UPDATE: fmode = "r+b"; break;
124 case MSPACK_SYS_OPEN_APPEND: fmode = "ab"; break;
125 default: return NULL;
126 }
127
128 if ((fh = (struct mspack_file_p *) malloc(sizeof(struct mspack_file_p)))) {
129 fh->name = filename;
130 if ((fh->fh = fopen(filename, fmode))) return (struct mspack_file *) fh;
131 free(fh);
132 }
133 return NULL;
134}
135
136static void msp_close(struct mspack_file *file) {
137 struct mspack_file_p *self = (struct mspack_file_p *) file;
138 if (self) {
139 fclose(self->fh);
140 free(self);
141 }
142}
143
144static int msp_read(struct mspack_file *file, void *buffer, int bytes) {
145 struct mspack_file_p *self = (struct mspack_file_p *) file;
146 if (self && buffer && bytes >= 0) {
147 size_t count = fread(buffer, 1, (size_t) bytes, self->fh);
148 if (!ferror(self->fh)) return (int) count;
149 }
150 return -1;
151}
152
153static int msp_write(struct mspack_file *file, void *buffer, int bytes) {
154 struct mspack_file_p *self = (struct mspack_file_p *) file;
155 if (self && buffer && bytes >= 0) {
156 size_t count = fwrite(buffer, 1, (size_t) bytes, self->fh);
157 if (!ferror(self->fh)) return (int) count;
158 }
159 return -1;
160}
161
162static int msp_seek(struct mspack_file *file, off_t offset, int mode) {
163 struct mspack_file_p *self = (struct mspack_file_p *) file;
164 if (self) {
165 switch (mode) {
166 case MSPACK_SYS_SEEK_START: mode = SEEK_SET; break;
167 case MSPACK_SYS_SEEK_CUR: mode = SEEK_CUR; break;
168 case MSPACK_SYS_SEEK_END: mode = SEEK_END; break;
169 default: return -1;
170 }
171#ifdef HAVE_FSEEKO
172 return fseeko(self->fh, offset, mode);
173#else
174 return fseek(self->fh, offset, mode);
175#endif
176 }
177 return -1;
178}
179
180static off_t msp_tell(struct mspack_file *file) {
181 struct mspack_file_p *self = (struct mspack_file_p *) file;
182#ifdef HAVE_FSEEKO
183 return (self) ? (off_t) ftello(self->fh) : 0;
184#else
185 return (self) ? (off_t) ftell(self->fh) : 0;
186#endif
187}
188
189static void msp_msg(struct mspack_file *file, const char *format, ...) {
190 va_list ap;
191 if (file) fprintf(stderr, "%s: ", ((struct mspack_file_p *) file)->name);
192 va_start(ap, format);
193 vfprintf(stderr, format, ap);
194 va_end(ap);
195 fputc((int) '\n', stderr);
196 fflush(stderr);
197}
198
199static void *msp_alloc(struct mspack_system *self, size_t bytes) {
200#ifdef DEBUG
201 /* make uninitialised data obvious */
202 char *buf = malloc(bytes + 8);
203 (void)self;
204 if (buf) memset(buf, 0xDC, bytes);
205 *((size_t *)buf) = bytes;
206 return &buf[8];
207#else
208 (void)self;
209 return malloc(bytes);
210#endif
211}
212
213static void msp_free(void *buffer) {
214#ifdef DEBUG
215 char *buf = buffer;
216 size_t bytes;
217 if (buf) {
218 buf -= 8;
219 bytes = *((size_t *)buf);
220 /* make freed data obvious */
221 memset(buf, 0xED, bytes);
222 free(buf);
223 }
224#else
225 free(buffer);
226#endif
227}
228
229static void msp_copy(void *src, void *dest, size_t bytes) {
230 memcpy(dest, src, bytes);
231}
232
233static struct mspack_system msp_system = {
234 &msp_open, &msp_close, &msp_read, &msp_write, &msp_seek,
235 &msp_tell, &msp_msg, &msp_alloc, &msp_free, &msp_copy, NULL
236};
237
238struct mspack_system *mspack_default_system = &msp_system;
239
240#endif
diff --git a/utils/rbutilqt/mspack/system-mspack.h b/utils/rbutilqt/mspack/system-mspack.h
new file mode 100644
index 0000000000..a0e6cf3ca8
--- /dev/null
+++ b/utils/rbutilqt/mspack/system-mspack.h
@@ -0,0 +1,129 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifndef MSPACK_SYSTEM_H
11#define MSPACK_SYSTEM_H 1
12
13#ifdef __cplusplus
14extern "C" {
15#endif
16
17/* ensure config.h is read before mspack.h */
18#ifdef HAVE_CONFIG_H
19# include "config.h"
20#endif
21
22#include "mspack.h"
23
24/* fix for problem with GCC 4 and glibc (thanks to Ville Skytta)
25 * http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=150429
26 */
27#ifdef read
28# undef read
29#endif
30
31#ifdef DEBUG
32# include <stdio.h>
33/* Old GCCs don't have __func__, but __FUNCTION__:
34 * http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html
35 */
36# if __STDC_VERSION__ < 199901L
37# if __GNUC__ >= 2
38# define __func__ __FUNCTION__
39# else
40# define __func__ "<unknown>"
41# endif
42# endif
43# define D(x) do { printf("%s:%d (%s) ",__FILE__, __LINE__, __func__); \
44 printf x ; fputc('\n', stdout); fflush(stdout);} while (0);
45#else
46# define D(x)
47#endif
48
49/* CAB supports searching through files over 4GB in size, and the CHM file
50 * format actively uses 64-bit offsets. These can only be fully supported
51 * if the system the code runs on supports large files. If not, the library
52 * will work as normal using only 32-bit arithmetic, but if an offset
53 * greater than 2GB is detected, an error message indicating the library
54 * can't support the file should be printed.
55 */
56#ifdef HAVE_LIMITS_H
57# include <limits.h>
58#endif
59
60#if ((defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS >= 64) || \
61 (defined(FILESIZEBITS) && FILESIZEBITS >= 64) || \
62 (defined(SIZEOF_OFF_T) && SIZEOF_OFF_T >= 8) || \
63 defined(_LARGEFILE_SOURCE) || defined(_LARGEFILE64_SOURCE))
64# define LARGEFILE_SUPPORT 1
65# define LD "lld"
66# define LU "llu"
67#else
68extern const char *largefile_msg;
69# define LD "ld"
70# define LU "lu"
71#endif
72
73/* endian-neutral reading of little-endian data */
74#define __egi32(a,n) ( ((((unsigned char *) a)[n+3]) << 24) | \
75 ((((unsigned char *) a)[n+2]) << 16) | \
76 ((((unsigned char *) a)[n+1]) << 8) | \
77 ((((unsigned char *) a)[n+0])))
78#define EndGetI64(a) ((((unsigned long long int) __egi32(a,4)) << 32) | \
79 ((unsigned int) __egi32(a,0)))
80#define EndGetI32(a) __egi32(a,0)
81#define EndGetI16(a) ((((a)[1])<<8)|((a)[0]))
82
83/* endian-neutral reading of big-endian data */
84#define EndGetM32(a) (((((unsigned char *) a)[0]) << 24) | \
85 ((((unsigned char *) a)[1]) << 16) | \
86 ((((unsigned char *) a)[2]) << 8) | \
87 ((((unsigned char *) a)[3])))
88#define EndGetM16(a) ((((a)[0])<<8)|((a)[1]))
89
90extern struct mspack_system *mspack_default_system;
91
92/* returns the length of a file opened for reading */
93extern int mspack_sys_filelen(struct mspack_system *system,
94 struct mspack_file *file, off_t *length);
95
96/* validates a system structure */
97extern int mspack_valid_system(struct mspack_system *sys);
98
99#if HAVE_STRINGS_H
100# include <strings.h>
101#endif
102
103#if HAVE_STRING_H
104# include <string.h>
105#endif
106
107#if HAVE_MEMCMP
108# define mspack_memcmp memcmp
109#else
110/* inline memcmp() */
111#ifdef _MSC_VER /* MSVC requires use of __inline instead of inline */
112#define INLINE __inline
113#else
114#define INLINE inline
115#endif
116static INLINE int mspack_memcmp(const void *s1, const void *s2, size_t n) {
117 unsigned char *c1 = (unsigned char *) s1;
118 unsigned char *c2 = (unsigned char *) s2;
119 if (n == 0) return 0;
120 while (--n && (*c1 == *c2)) c1++, c2++;
121 return *c1 - *c2;
122}
123#endif
124
125#ifdef __cplusplus
126}
127#endif
128
129#endif
diff --git a/utils/rbutilqt/mspack/szdd.h b/utils/rbutilqt/mspack/szdd.h
new file mode 100644
index 0000000000..b9936b42d1
--- /dev/null
+++ b/utils/rbutilqt/mspack/szdd.h
@@ -0,0 +1,39 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10#ifndef MSPACK_SZDD_H
11#define MSPACK_SZDD_H 1
12
13#include "lzss.h"
14
15/* input buffer size during decompression - not worth parameterising IMHO */
16#define SZDD_INPUT_SIZE (2048)
17
18/* SZDD compression definitions */
19
20struct msszdd_compressor_p {
21 struct msszdd_compressor base;
22 struct mspack_system *system;
23 int error;
24};
25
26/* SZDD decompression definitions */
27
28struct msszdd_decompressor_p {
29 struct msszdd_decompressor base;
30 struct mspack_system *system;
31 int error;
32};
33
34struct msszddd_header_p {
35 struct msszddd_header base;
36 struct mspack_file *fh;
37};
38
39#endif
diff --git a/utils/rbutilqt/mspack/szddc.c b/utils/rbutilqt/mspack/szddc.c
new file mode 100644
index 0000000000..6ad6501217
--- /dev/null
+++ b/utils/rbutilqt/mspack/szddc.c
@@ -0,0 +1,24 @@
1/* This file is part of libmspack.
2 * (C) 2003-2004 Stuart Caie.
3 *
4 * libmspack is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6 *
7 * For further details, see the file COPYING.LIB distributed with libmspack
8 */
9
10/* SZDD compression implementation */
11
12#include "system-mspack.h"
13#include "szdd.h"
14
15struct msszdd_compressor *
16 mspack_create_szdd_compressor(struct mspack_system *sys)
17{
18 /* todo */
19 return NULL;
20}
21
22void mspack_destroy_szdd_compressor(struct msszdd_compressor *self) {
23 /* todo */
24}
diff --git a/utils/rbutilqt/mspack/szddd.c b/utils/rbutilqt/mspack/szddd.c
new file mode 100644
index 0000000000..1d6d05f844
--- /dev/null
+++ b/utils/rbutilqt/mspack/szddd.c
@@ -0,0 +1,247 @@
1/* This file is part of libmspack.
2 * (C) 2003-2010 Stuart Caie.
3 *
4 * SZDD is a format used in the MS-DOS commands COMPRESS.EXE and
5 * EXPAND.EXE. The compression method is attributed to Steven Zeck,
6 * however it's pretty much identical to LZSS.
7 *
8 * libmspack is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
10 *
11 * For further details, see the file COPYING.LIB distributed with libmspack
12 */
13
14/* SZDD decompression implementation */
15
16#include "system-mspack.h"
17#include "szdd.h"
18
19/* prototypes */
20static struct msszddd_header *szddd_open(
21 struct msszdd_decompressor *base, const char *filename);
22static void szddd_close(
23 struct msszdd_decompressor *base, struct msszddd_header *hdr);
24static int szddd_read_headers(
25 struct mspack_system *sys, struct mspack_file *fh,
26 struct msszddd_header *hdr);
27static int szddd_extract(
28 struct msszdd_decompressor *base, struct msszddd_header *hdr,
29 const char *filename);
30static int szddd_decompress(
31 struct msszdd_decompressor *base, const char *input, const char *output);
32static int szddd_error(
33 struct msszdd_decompressor *base);
34
35/***************************************
36 * MSPACK_CREATE_SZDD_DECOMPRESSOR
37 ***************************************
38 * constructor
39 */
40struct msszdd_decompressor *
41 mspack_create_szdd_decompressor(struct mspack_system *sys)
42{
43 struct msszdd_decompressor_p *self = NULL;
44
45 if (!sys) sys = mspack_default_system;
46 if (!mspack_valid_system(sys)) return NULL;
47
48 if ((self = (struct msszdd_decompressor_p *) sys->alloc(sys, sizeof(struct msszdd_decompressor_p)))) {
49 self->base.open = &szddd_open;
50 self->base.close = &szddd_close;
51 self->base.extract = &szddd_extract;
52 self->base.decompress = &szddd_decompress;
53 self->base.last_error = &szddd_error;
54 self->system = sys;
55 self->error = MSPACK_ERR_OK;
56 }
57 return (struct msszdd_decompressor *) self;
58}
59
60/***************************************
61 * MSPACK_DESTROY_SZDD_DECOMPRESSOR
62 ***************************************
63 * destructor
64 */
65void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *base)
66{
67 struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
68 if (self) {
69 struct mspack_system *sys = self->system;
70 sys->free(self);
71 }
72}
73
74/***************************************
75 * SZDDD_OPEN
76 ***************************************
77 * opens an SZDD file without decompressing, reads header
78 */
79static struct msszddd_header *szddd_open(struct msszdd_decompressor *base,
80 const char *filename)
81{
82 struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
83 struct msszddd_header *hdr;
84 struct mspack_system *sys;
85 struct mspack_file *fh;
86
87 if (!self) return NULL;
88 sys = self->system;
89
90 fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ);
91 hdr = (struct msszddd_header *) sys->alloc(sys, sizeof(struct msszddd_header_p));
92 if (fh && hdr) {
93 ((struct msszddd_header_p *) hdr)->fh = fh;
94 self->error = szddd_read_headers(sys, fh, hdr);
95 }
96 else {
97 if (!fh) self->error = MSPACK_ERR_OPEN;
98 if (!hdr) self->error = MSPACK_ERR_NOMEMORY;
99 }
100
101 if (self->error) {
102 if (fh) sys->close(fh);
103 sys->free(hdr);
104 hdr = NULL;
105 }
106
107 return hdr;
108}
109
110/***************************************
111 * SZDDD_CLOSE
112 ***************************************
113 * closes an SZDD file
114 */
115static void szddd_close(struct msszdd_decompressor *base,
116 struct msszddd_header *hdr)
117{
118 struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
119 struct msszddd_header_p *hdr_p = (struct msszddd_header_p *) hdr;
120
121 if (!self || !self->system) return;
122
123 /* close the file handle associated */
124 self->system->close(hdr_p->fh);
125
126 /* free the memory associated */
127 self->system->free(hdr);
128
129 self->error = MSPACK_ERR_OK;
130}
131
132/***************************************
133 * SZDDD_READ_HEADERS
134 ***************************************
135 * reads the headers of an SZDD format file
136 */
137static unsigned char szdd_signature_expand[8] = {
138 0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33
139};
140static unsigned char szdd_signature_qbasic[8] = {
141 0x53, 0x5A, 0x20, 0x88, 0xF0, 0x27, 0x33, 0xD1
142};
143
144static int szddd_read_headers(struct mspack_system *sys,
145 struct mspack_file *fh,
146 struct msszddd_header *hdr)
147{
148 unsigned char buf[8];
149
150 /* read and check signature */
151 if (sys->read(fh, buf, 8) != 8) return MSPACK_ERR_READ;
152
153 if ((memcmp(buf, szdd_signature_expand, 8) == 0)) {
154 /* common SZDD */
155 hdr->format = MSSZDD_FMT_NORMAL;
156
157 /* read the rest of the header */
158 if (sys->read(fh, buf, 6) != 6) return MSPACK_ERR_READ;
159 if (buf[0] != 0x41) return MSPACK_ERR_DATAFORMAT;
160 hdr->missing_char = buf[1];
161 hdr->length = EndGetI32(&buf[2]);
162 }
163 else if ((memcmp(buf, szdd_signature_qbasic, 8) == 0)) {
164 /* special QBasic SZDD */
165 hdr->format = MSSZDD_FMT_QBASIC;
166 if (sys->read(fh, buf, 4) != 4) return MSPACK_ERR_READ;
167 hdr->missing_char = '\0';
168 hdr->length = EndGetI32(buf);
169 }
170 else {
171 return MSPACK_ERR_SIGNATURE;
172 }
173 return MSPACK_ERR_OK;
174}
175
176/***************************************
177 * SZDDD_EXTRACT
178 ***************************************
179 * decompresses an SZDD file
180 */
181static int szddd_extract(struct msszdd_decompressor *base,
182 struct msszddd_header *hdr, const char *filename)
183{
184 struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
185 struct mspack_file *fh, *outfh;
186 struct mspack_system *sys;
187 off_t data_offset;
188
189 if (!self) return MSPACK_ERR_ARGS;
190 if (!hdr) return self->error = MSPACK_ERR_ARGS;
191 sys = self->system;
192
193 fh = ((struct msszddd_header_p *) hdr)->fh;
194
195 /* seek to the compressed data */
196 data_offset = (hdr->format == MSSZDD_FMT_NORMAL) ? 14 : 12;
197 if (sys->seek(fh, data_offset, MSPACK_SYS_SEEK_START)) {
198 return self->error = MSPACK_ERR_SEEK;
199 }
200
201 /* open file for output */
202 if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) {
203 return self->error = MSPACK_ERR_OPEN;
204 }
205
206 /* decompress the data */
207 self->error = lzss_decompress(sys, fh, outfh, SZDD_INPUT_SIZE,
208 hdr->format == MSSZDD_FMT_NORMAL
209 ? LZSS_MODE_EXPAND
210 : LZSS_MODE_QBASIC);
211
212 /* close output file */
213 sys->close(outfh);
214
215 return self->error;
216}
217
218/***************************************
219 * SZDDD_DECOMPRESS
220 ***************************************
221 * unpacks directly from input to output
222 */
223static int szddd_decompress(struct msszdd_decompressor *base,
224 const char *input, const char *output)
225{
226 struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
227 struct msszddd_header *hdr;
228 int error;
229
230 if (!self) return MSPACK_ERR_ARGS;
231
232 if (!(hdr = szddd_open(base, input))) return self->error;
233 error = szddd_extract(base, hdr, output);
234 szddd_close(base, hdr);
235 return self->error = error;
236}
237
238/***************************************
239 * SZDDD_ERROR
240 ***************************************
241 * returns the last error that occurred
242 */
243static int szddd_error(struct msszdd_decompressor *base)
244{
245 struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
246 return (self) ? self->error : MSPACK_ERR_ARGS;
247}