diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2013-03-11 18:46:03 +0100 |
---|---|---|
committer | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2013-11-04 22:14:17 +0100 |
commit | 739a7ae0e9acb27227f5473a003833ea5a9c97ef (patch) | |
tree | 81f6dfd81b4343745b21a6c00c286791827175e4 /rbutil | |
parent | 27111d83be815602ac35354e6a8e4e158c5968f9 (diff) | |
download | rockbox-739a7ae0e9acb27227f5473a003833ea5a9c97ef.tar.gz rockbox-739a7ae0e9acb27227f5473a003833ea5a9c97ef.zip |
Add libmspack to rbutil
Change-Id: I520c14131ec1e12013f106c13cba00aac058ad83
Reviewed-on: http://gerrit.rockbox.org/391
Reviewed-by: Dominik Riebeling <Dominik.Riebeling@gmail.com>
Diffstat (limited to 'rbutil')
37 files changed, 10082 insertions, 0 deletions
diff --git a/rbutil/rbutilqt/mspack/COPYING.LIB b/rbutil/rbutilqt/mspack/COPYING.LIB new file mode 100644 index 0000000000..b1e3f5a263 --- /dev/null +++ b/rbutil/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 | ||
16 | freedom to share and change it. By contrast, the GNU General Public | ||
17 | Licenses are intended to guarantee your freedom to share and change | ||
18 | free software--to make sure the software is free for all its users. | ||
19 | |||
20 | This license, the Lesser General Public License, applies to some | ||
21 | specially designated software packages--typically libraries--of the | ||
22 | Free Software Foundation and other authors who decide to use it. You | ||
23 | can use it too, but we suggest you first think carefully about whether | ||
24 | this license or the ordinary General Public License is the better | ||
25 | strategy 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, | ||
28 | not price. Our General Public Licenses are designed to make sure that | ||
29 | you have the freedom to distribute copies of free software (and charge | ||
30 | for this service if you wish); that you receive source code or can get | ||
31 | it if you want it; that you can change the software and use pieces of | ||
32 | it in new free programs; and that you are informed that you can do | ||
33 | these things. | ||
34 | |||
35 | To protect your rights, we need to make restrictions that forbid | ||
36 | distributors to deny you these rights or to ask you to surrender these | ||
37 | rights. These restrictions translate to certain responsibilities for | ||
38 | you 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 | ||
41 | or for a fee, you must give the recipients all the rights that we gave | ||
42 | you. You must make sure that they, too, receive or can get the source | ||
43 | code. If you link other code with the library, you must provide | ||
44 | complete object files to the recipients, so that they can relink them | ||
45 | with the library after making changes to the library and recompiling | ||
46 | it. 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 | ||
49 | library, and (2) we offer you this license, which gives you legal | ||
50 | permission to copy, distribute and/or modify the library. | ||
51 | |||
52 | To protect each distributor, we want to make it very clear that | ||
53 | there is no warranty for the free library. Also, if the library is | ||
54 | modified by someone else and passed on, the recipients should know | ||
55 | that what they have is not the original version, so that the original | ||
56 | author's reputation will not be affected by problems that might be | ||
57 | introduced by others. | ||
58 | |||
59 | Finally, software patents pose a constant threat to the existence of | ||
60 | any free program. We wish to make sure that a company cannot | ||
61 | effectively restrict the users of a free program by obtaining a | ||
62 | restrictive license from a patent holder. Therefore, we insist that | ||
63 | any patent license obtained for a version of the library must be | ||
64 | consistent with the full freedom of use specified in this license. | ||
65 | |||
66 | Most GNU software, including some libraries, is covered by the | ||
67 | ordinary GNU General Public License. This license, the GNU Lesser | ||
68 | General Public License, applies to certain designated libraries, and | ||
69 | is quite different from the ordinary General Public License. We use | ||
70 | this license for certain libraries in order to permit linking those | ||
71 | libraries into non-free programs. | ||
72 | |||
73 | When a program is linked with a library, whether statically or using | ||
74 | a shared library, the combination of the two is legally speaking a | ||
75 | combined work, a derivative of the original library. The ordinary | ||
76 | General Public License therefore permits such linking only if the | ||
77 | entire combination fits its criteria of freedom. The Lesser General | ||
78 | Public License permits more lax criteria for linking other code with | ||
79 | the library. | ||
80 | |||
81 | We call this license the "Lesser" General Public License because it | ||
82 | does Less to protect the user's freedom than the ordinary General | ||
83 | Public License. It also provides other free software developers Less | ||
84 | of an advantage over competing non-free programs. These disadvantages | ||
85 | are the reason we use the ordinary General Public License for many | ||
86 | libraries. However, the Lesser license provides advantages in certain | ||
87 | special circumstances. | ||
88 | |||
89 | For example, on rare occasions, there may be a special need to | ||
90 | encourage the widest possible use of a certain library, so that it becomes | ||
91 | a de-facto standard. To achieve this, non-free programs must be | ||
92 | allowed to use the library. A more frequent case is that a free | ||
93 | library does the same job as widely used non-free libraries. In this | ||
94 | case, there is little to gain by limiting the free library to free | ||
95 | software only, so we use the Lesser General Public License. | ||
96 | |||
97 | In other cases, permission to use a particular library in non-free | ||
98 | programs enables a greater number of people to use a large body of | ||
99 | free software. For example, permission to use the GNU C Library in | ||
100 | non-free programs enables many more people to use the whole GNU | ||
101 | operating system, as well as its variant, the GNU/Linux operating | ||
102 | system. | ||
103 | |||
104 | Although the Lesser General Public License is Less protective of the | ||
105 | users' freedom, it does ensure that the user of a program that is | ||
106 | linked with the Library has the freedom and the wherewithal to run | ||
107 | that program using a modified version of the Library. | ||
108 | |||
109 | The precise terms and conditions for copying, distribution and | ||
110 | modification follow. Pay close attention to the difference between a | ||
111 | "work based on the library" and a "work that uses the library". The | ||
112 | former contains code derived from the library, whereas the latter must | ||
113 | be 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 | ||
119 | program which contains a notice placed by the copyright holder or | ||
120 | other authorized party saying it may be distributed under the terms of | ||
121 | this Lesser General Public License (also called "this License"). | ||
122 | Each licensee is addressed as "you". | ||
123 | |||
124 | A "library" means a collection of software functions and/or data | ||
125 | prepared 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 | ||
129 | which has been distributed under these terms. A "work based on the | ||
130 | Library" means either the Library or any derivative work under | ||
131 | copyright law: that is to say, a work containing the Library or a | ||
132 | portion of it, either verbatim or with modifications and/or translated | ||
133 | straightforwardly into another language. (Hereinafter, translation is | ||
134 | included without limitation in the term "modification".) | ||
135 | |||
136 | "Source code" for a work means the preferred form of the work for | ||
137 | making modifications to it. For a library, complete source code means | ||
138 | all the source code for all modules it contains, plus any associated | ||
139 | interface definition files, plus the scripts used to control compilation | ||
140 | and installation of the library. | ||
141 | |||
142 | Activities other than copying, distribution and modification are not | ||
143 | covered by this License; they are outside its scope. The act of | ||
144 | running a program using the Library is not restricted, and output from | ||
145 | such a program is covered only if its contents constitute a work based | ||
146 | on the Library (independent of the use of the Library in a tool for | ||
147 | writing it). Whether that is true depends on what the Library does | ||
148 | and what the program that uses the Library does. | ||
149 | |||
150 | 1. You may copy and distribute verbatim copies of the Library's | ||
151 | complete source code as you receive it, in any medium, provided that | ||
152 | you conspicuously and appropriately publish on each copy an | ||
153 | appropriate copyright notice and disclaimer of warranty; keep intact | ||
154 | all the notices that refer to this License and to the absence of any | ||
155 | warranty; and distribute a copy of this License along with the | ||
156 | Library. | ||
157 | |||
158 | You may charge a fee for the physical act of transferring a copy, | ||
159 | and you may at your option offer warranty protection in exchange for a | ||
160 | fee. | ||
161 | |||
162 | 2. You may modify your copy or copies of the Library or any portion | ||
163 | of it, thus forming a work based on the Library, and copy and | ||
164 | distribute such modifications or work under the terms of Section 1 | ||
165 | above, 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 | |||
190 | These requirements apply to the modified work as a whole. If | ||
191 | identifiable sections of that work are not derived from the Library, | ||
192 | and can be reasonably considered independent and separate works in | ||
193 | themselves, then this License, and its terms, do not apply to those | ||
194 | sections when you distribute them as separate works. But when you | ||
195 | distribute the same sections as part of a whole which is a work based | ||
196 | on the Library, the distribution of the whole must be on the terms of | ||
197 | this License, whose permissions for other licensees extend to the | ||
198 | entire whole, and thus to each and every part regardless of who wrote | ||
199 | it. | ||
200 | |||
201 | Thus, it is not the intent of this section to claim rights or contest | ||
202 | your rights to work written entirely by you; rather, the intent is to | ||
203 | exercise the right to control the distribution of derivative or | ||
204 | collective works based on the Library. | ||
205 | |||
206 | In addition, mere aggregation of another work not based on the Library | ||
207 | with the Library (or with a work based on the Library) on a volume of | ||
208 | a storage or distribution medium does not bring the other work under | ||
209 | the scope of this License. | ||
210 | |||
211 | 3. You may opt to apply the terms of the ordinary GNU General Public | ||
212 | License instead of this License to a given copy of the Library. To do | ||
213 | this, you must alter all the notices that refer to this License, so | ||
214 | that they refer to the ordinary GNU General Public License, version 2, | ||
215 | instead of to this License. (If a newer version than version 2 of the | ||
216 | ordinary GNU General Public License has appeared, then you can specify | ||
217 | that version instead if you wish.) Do not make any other change in | ||
218 | these notices. | ||
219 | |||
220 | Once this change is made in a given copy, it is irreversible for | ||
221 | that copy, so the ordinary GNU General Public License applies to all | ||
222 | subsequent copies and derivative works made from that copy. | ||
223 | |||
224 | This option is useful when you wish to copy part of the code of | ||
225 | the Library into a program that is not a library. | ||
226 | |||
227 | 4. You may copy and distribute the Library (or a portion or | ||
228 | derivative of it, under Section 2) in object code or executable form | ||
229 | under the terms of Sections 1 and 2 above provided that you accompany | ||
230 | it with the complete corresponding machine-readable source code, which | ||
231 | must be distributed under the terms of Sections 1 and 2 above on a | ||
232 | medium customarily used for software interchange. | ||
233 | |||
234 | If distribution of object code is made by offering access to copy | ||
235 | from a designated place, then offering equivalent access to copy the | ||
236 | source code from the same place satisfies the requirement to | ||
237 | distribute the source code, even though third parties are not | ||
238 | compelled to copy the source along with the object code. | ||
239 | |||
240 | 5. A program that contains no derivative of any portion of the | ||
241 | Library, but is designed to work with the Library by being compiled or | ||
242 | linked with it, is called a "work that uses the Library". Such a | ||
243 | work, in isolation, is not a derivative work of the Library, and | ||
244 | therefore falls outside the scope of this License. | ||
245 | |||
246 | However, linking a "work that uses the Library" with the Library | ||
247 | creates an executable that is a derivative of the Library (because it | ||
248 | contains portions of the Library), rather than a "work that uses the | ||
249 | library". The executable is therefore covered by this License. | ||
250 | Section 6 states terms for distribution of such executables. | ||
251 | |||
252 | When a "work that uses the Library" uses material from a header file | ||
253 | that is part of the Library, the object code for the work may be a | ||
254 | derivative work of the Library even though the source code is not. | ||
255 | Whether this is true is especially significant if the work can be | ||
256 | linked without the Library, or if the work is itself a library. The | ||
257 | threshold for this to be true is not precisely defined by law. | ||
258 | |||
259 | If such an object file uses only numerical parameters, data | ||
260 | structure layouts and accessors, and small macros and small inline | ||
261 | functions (ten lines or less in length), then the use of the object | ||
262 | file is unrestricted, regardless of whether it is legally a derivative | ||
263 | work. (Executables containing this object code plus portions of the | ||
264 | Library will still fall under Section 6.) | ||
265 | |||
266 | Otherwise, if the work is a derivative of the Library, you may | ||
267 | distribute the object code for the work under the terms of Section 6. | ||
268 | Any executables containing that work also fall under Section 6, | ||
269 | whether 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 | ||
272 | link a "work that uses the Library" with the Library to produce a | ||
273 | work containing portions of the Library, and distribute that work | ||
274 | under terms of your choice, provided that the terms permit | ||
275 | modification of the work for the customer's own use and reverse | ||
276 | engineering for debugging such modifications. | ||
277 | |||
278 | You must give prominent notice with each copy of the work that the | ||
279 | Library is used in it and that the Library and its use are covered by | ||
280 | this License. You must supply a copy of this License. If the work | ||
281 | during execution displays copyright notices, you must include the | ||
282 | copyright notice for the Library among them, as well as a reference | ||
283 | directing the user to the copy of this License. Also, you must do one | ||
284 | of 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 | ||
319 | Library" must include any data and utility programs needed for | ||
320 | reproducing the executable from it. However, as a special exception, | ||
321 | the materials to be distributed need not include anything that is | ||
322 | normally distributed (in either source or binary form) with the major | ||
323 | components (compiler, kernel, and so on) of the operating system on | ||
324 | which the executable runs, unless that component itself accompanies | ||
325 | the executable. | ||
326 | |||
327 | It may happen that this requirement contradicts the license | ||
328 | restrictions of other proprietary libraries that do not normally | ||
329 | accompany the operating system. Such a contradiction means you cannot | ||
330 | use both them and the Library together in an executable that you | ||
331 | distribute. | ||
332 | |||
333 | 7. You may place library facilities that are a work based on the | ||
334 | Library side-by-side in a single library together with other library | ||
335 | facilities not covered by this License, and distribute such a combined | ||
336 | library, provided that the separate distribution of the work based on | ||
337 | the Library and of the other library facilities is otherwise | ||
338 | permitted, 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 | ||
350 | the Library except as expressly provided under this License. Any | ||
351 | attempt otherwise to copy, modify, sublicense, link with, or | ||
352 | distribute the Library is void, and will automatically terminate your | ||
353 | rights under this License. However, parties who have received copies, | ||
354 | or rights, from you under this License will not have their licenses | ||
355 | terminated so long as such parties remain in full compliance. | ||
356 | |||
357 | 9. You are not required to accept this License, since you have not | ||
358 | signed it. However, nothing else grants you permission to modify or | ||
359 | distribute the Library or its derivative works. These actions are | ||
360 | prohibited by law if you do not accept this License. Therefore, by | ||
361 | modifying or distributing the Library (or any work based on the | ||
362 | Library), you indicate your acceptance of this License to do so, and | ||
363 | all its terms and conditions for copying, distributing or modifying | ||
364 | the Library or works based on it. | ||
365 | |||
366 | 10. Each time you redistribute the Library (or any work based on the | ||
367 | Library), the recipient automatically receives a license from the | ||
368 | original licensor to copy, distribute, link with or modify the Library | ||
369 | subject to these terms and conditions. You may not impose any further | ||
370 | restrictions on the recipients' exercise of the rights granted herein. | ||
371 | You are not responsible for enforcing compliance by third parties with | ||
372 | this License. | ||
373 | |||
374 | 11. If, as a consequence of a court judgment or allegation of patent | ||
375 | infringement or for any other reason (not limited to patent issues), | ||
376 | conditions are imposed on you (whether by court order, agreement or | ||
377 | otherwise) that contradict the conditions of this License, they do not | ||
378 | excuse you from the conditions of this License. If you cannot | ||
379 | distribute so as to satisfy simultaneously your obligations under this | ||
380 | License and any other pertinent obligations, then as a consequence you | ||
381 | may not distribute the Library at all. For example, if a patent | ||
382 | license would not permit royalty-free redistribution of the Library by | ||
383 | all those who receive copies directly or indirectly through you, then | ||
384 | the only way you could satisfy both it and this License would be to | ||
385 | refrain entirely from distribution of the Library. | ||
386 | |||
387 | If any portion of this section is held invalid or unenforceable under any | ||
388 | particular circumstance, the balance of the section is intended to apply, | ||
389 | and the section as a whole is intended to apply in other circumstances. | ||
390 | |||
391 | It is not the purpose of this section to induce you to infringe any | ||
392 | patents or other property right claims or to contest validity of any | ||
393 | such claims; this section has the sole purpose of protecting the | ||
394 | integrity of the free software distribution system which is | ||
395 | implemented by public license practices. Many people have made | ||
396 | generous contributions to the wide range of software distributed | ||
397 | through that system in reliance on consistent application of that | ||
398 | system; it is up to the author/donor to decide if he or she is willing | ||
399 | to distribute software through any other system and a licensee cannot | ||
400 | impose that choice. | ||
401 | |||
402 | This section is intended to make thoroughly clear what is believed to | ||
403 | be a consequence of the rest of this License. | ||
404 | |||
405 | 12. If the distribution and/or use of the Library is restricted in | ||
406 | certain countries either by patents or by copyrighted interfaces, the | ||
407 | original copyright holder who places the Library under this License may add | ||
408 | an explicit geographical distribution limitation excluding those countries, | ||
409 | so that distribution is permitted only in or among countries not thus | ||
410 | excluded. In such case, this License incorporates the limitation as if | ||
411 | written in the body of this License. | ||
412 | |||
413 | 13. The Free Software Foundation may publish revised and/or new | ||
414 | versions of the Lesser General Public License from time to time. | ||
415 | Such new versions will be similar in spirit to the present version, | ||
416 | but may differ in detail to address new problems or concerns. | ||
417 | |||
418 | Each version is given a distinguishing version number. If the Library | ||
419 | specifies a version number of this License which applies to it and | ||
420 | "any later version", you have the option of following the terms and | ||
421 | conditions either of that version or of any later version published by | ||
422 | the Free Software Foundation. If the Library does not specify a | ||
423 | license version number, you may choose any version ever published by | ||
424 | the Free Software Foundation. | ||
425 | |||
426 | 14. If you wish to incorporate parts of the Library into other free | ||
427 | programs whose distribution conditions are incompatible with these, | ||
428 | write to the author to ask for permission. For software which is | ||
429 | copyrighted by the Free Software Foundation, write to the Free | ||
430 | Software Foundation; we sometimes make exceptions for this. Our | ||
431 | decision will be guided by the two goals of preserving the free status | ||
432 | of all derivatives of our free software and of promoting the sharing | ||
433 | and reuse of software generally. | ||
434 | |||
435 | NO WARRANTY | ||
436 | |||
437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO | ||
438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. | ||
439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR | ||
440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY | ||
441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE | ||
442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE | ||
444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME | ||
445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | ||
446 | |||
447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN | ||
448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY | ||
449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU | ||
450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR | ||
451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE | ||
452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING | ||
453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A | ||
454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF | ||
455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | ||
456 | DAMAGES. | ||
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 | ||
463 | possible use to the public, we recommend making it free software that | ||
464 | everyone can redistribute and change. You can do so by permitting | ||
465 | redistribution under these terms (or, alternatively, under the terms of the | ||
466 | ordinary General Public License). | ||
467 | |||
468 | To apply these terms, attach the following notices to the library. It is | ||
469 | safest to attach them to the start of each source file to most effectively | ||
470 | convey 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 | |||
490 | Also add information on how to contact you by electronic and paper mail. | ||
491 | |||
492 | You should also get your employer (if you work as a programmer) or your | ||
493 | school, if any, to sign a "copyright disclaimer" for the library, if | ||
494 | necessary. 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 | |||
502 | That's all there is to it! | ||
503 | |||
504 | |||
diff --git a/rbutil/rbutilqt/mspack/README.ROCKBOX b/rbutil/rbutilqt/mspack/README.ROCKBOX new file mode 100644 index 0000000000..db5ba7f482 --- /dev/null +++ b/rbutil/rbutilqt/mspack/README.ROCKBOX | |||
@@ -0,0 +1,6 @@ | |||
1 | This folder contains the mspack project for MS files compression/decompression. | ||
2 | These files are distributed under the LGPL. | ||
3 | The source files have been last synced with libmspack-0.3alpha | ||
4 | http://sourceforge.net/projects/libmspack/on January 28, 2013 | ||
5 | |||
6 | |||
diff --git a/rbutil/rbutilqt/mspack/cab.h b/rbutil/rbutilqt/mspack/cab.h new file mode 100644 index 0000000000..9f449d1f29 --- /dev/null +++ b/rbutil/rbutilqt/mspack/cab.h | |||
@@ -0,0 +1,127 @@ | |||
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_CAB_H | ||
11 | #define MSPACK_CAB_H 1 | ||
12 | |||
13 | #include <mszip.h> | ||
14 | #include <qtm.h> | ||
15 | #include <lzx.h> | ||
16 | |||
17 | /* generic CAB definitions */ | ||
18 | |||
19 | /* structure offsets */ | ||
20 | #define cfhead_Signature (0x00) | ||
21 | #define cfhead_CabinetSize (0x08) | ||
22 | #define cfhead_FileOffset (0x10) | ||
23 | #define cfhead_MinorVersion (0x18) | ||
24 | #define cfhead_MajorVersion (0x19) | ||
25 | #define cfhead_NumFolders (0x1A) | ||
26 | #define cfhead_NumFiles (0x1C) | ||
27 | #define cfhead_Flags (0x1E) | ||
28 | #define cfhead_SetID (0x20) | ||
29 | #define cfhead_CabinetIndex (0x22) | ||
30 | #define cfhead_SIZEOF (0x24) | ||
31 | #define cfheadext_HeaderReserved (0x00) | ||
32 | #define cfheadext_FolderReserved (0x02) | ||
33 | #define cfheadext_DataReserved (0x03) | ||
34 | #define cfheadext_SIZEOF (0x04) | ||
35 | #define cffold_DataOffset (0x00) | ||
36 | #define cffold_NumBlocks (0x04) | ||
37 | #define cffold_CompType (0x06) | ||
38 | #define cffold_SIZEOF (0x08) | ||
39 | #define cffile_UncompressedSize (0x00) | ||
40 | #define cffile_FolderOffset (0x04) | ||
41 | #define cffile_FolderIndex (0x08) | ||
42 | #define cffile_Date (0x0A) | ||
43 | #define cffile_Time (0x0C) | ||
44 | #define cffile_Attribs (0x0E) | ||
45 | #define cffile_SIZEOF (0x10) | ||
46 | #define cfdata_CheckSum (0x00) | ||
47 | #define cfdata_CompressedSize (0x04) | ||
48 | #define cfdata_UncompressedSize (0x06) | ||
49 | #define cfdata_SIZEOF (0x08) | ||
50 | |||
51 | /* flags */ | ||
52 | #define cffoldCOMPTYPE_MASK (0x000f) | ||
53 | #define cffoldCOMPTYPE_NONE (0x0000) | ||
54 | #define cffoldCOMPTYPE_MSZIP (0x0001) | ||
55 | #define cffoldCOMPTYPE_QUANTUM (0x0002) | ||
56 | #define cffoldCOMPTYPE_LZX (0x0003) | ||
57 | #define cfheadPREV_CABINET (0x0001) | ||
58 | #define cfheadNEXT_CABINET (0x0002) | ||
59 | #define cfheadRESERVE_PRESENT (0x0004) | ||
60 | #define cffileCONTINUED_FROM_PREV (0xFFFD) | ||
61 | #define cffileCONTINUED_TO_NEXT (0xFFFE) | ||
62 | #define cffileCONTINUED_PREV_AND_NEXT (0xFFFF) | ||
63 | |||
64 | /* CAB data blocks are <= 32768 bytes in uncompressed form. Uncompressed | ||
65 | * blocks have zero growth. MSZIP guarantees that it won't grow above | ||
66 | * uncompressed size by more than 12 bytes. LZX guarantees it won't grow | ||
67 | * more than 6144 bytes. Quantum has no documentation, but the largest | ||
68 | * block seen in the wild is 337 bytes above uncompressed size. | ||
69 | */ | ||
70 | #define CAB_BLOCKMAX (32768) | ||
71 | #define CAB_INPUTMAX (CAB_BLOCKMAX+6144) | ||
72 | |||
73 | /* CAB compression definitions */ | ||
74 | |||
75 | struct mscab_compressor_p { | ||
76 | struct mscab_compressor base; | ||
77 | struct mspack_system *system; | ||
78 | /* todo */ | ||
79 | }; | ||
80 | |||
81 | /* CAB decompression definitions */ | ||
82 | |||
83 | struct mscabd_decompress_state { | ||
84 | struct mscabd_folder_p *folder; /* current folder we're extracting from */ | ||
85 | struct mscabd_folder_data *data; /* current folder split we're in */ | ||
86 | unsigned int offset; /* uncompressed offset within folder */ | ||
87 | unsigned int block; /* which block are we decompressing? */ | ||
88 | struct mspack_system sys; /* special I/O code for decompressor */ | ||
89 | int comp_type; /* type of compression used by folder */ | ||
90 | int (*decompress)(void *, off_t); /* decompressor code */ | ||
91 | void *state; /* decompressor state */ | ||
92 | struct mscabd_cabinet_p *incab; /* cabinet where input data comes from */ | ||
93 | struct mspack_file *infh; /* input file handle */ | ||
94 | struct mspack_file *outfh; /* output file handle */ | ||
95 | unsigned char *i_ptr, *i_end; /* input data consumed, end */ | ||
96 | unsigned char input[CAB_INPUTMAX]; /* one input block of data */ | ||
97 | }; | ||
98 | |||
99 | struct mscab_decompressor_p { | ||
100 | struct mscab_decompressor base; | ||
101 | struct mscabd_decompress_state *d; | ||
102 | struct mspack_system *system; | ||
103 | int param[3]; /* !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!! */ | ||
104 | int error, read_error; | ||
105 | }; | ||
106 | |||
107 | struct mscabd_cabinet_p { | ||
108 | struct mscabd_cabinet base; | ||
109 | off_t blocks_off; /* offset to data blocks */ | ||
110 | int block_resv; /* reserved space in data blocks */ | ||
111 | }; | ||
112 | |||
113 | /* there is one of these for every cabinet a folder spans */ | ||
114 | struct mscabd_folder_data { | ||
115 | struct mscabd_folder_data *next; | ||
116 | struct mscabd_cabinet_p *cab; /* cabinet file of this folder span */ | ||
117 | off_t offset; /* cabinet offset of first datablock */ | ||
118 | }; | ||
119 | |||
120 | struct mscabd_folder_p { | ||
121 | struct mscabd_folder base; | ||
122 | struct mscabd_folder_data data; /* where are the data blocks? */ | ||
123 | struct mscabd_file *merge_prev; /* first file needing backwards merge */ | ||
124 | struct mscabd_file *merge_next; /* first file needing forwards merge */ | ||
125 | }; | ||
126 | |||
127 | #endif | ||
diff --git a/rbutil/rbutilqt/mspack/cabc.c b/rbutil/rbutilqt/mspack/cabc.c new file mode 100644 index 0000000000..242e0347c3 --- /dev/null +++ b/rbutil/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.h> | ||
13 | #include <cab.h> | ||
14 | |||
15 | struct mscab_compressor * | ||
16 | mspack_create_cab_compressor(struct mspack_system *sys) | ||
17 | { | ||
18 | /* todo */ | ||
19 | return NULL; | ||
20 | } | ||
21 | |||
22 | void mspack_destroy_cab_compressor(struct mscab_compressor *self) { | ||
23 | /* todo */ | ||
24 | } | ||
diff --git a/rbutil/rbutilqt/mspack/cabd.c b/rbutil/rbutilqt/mspack/cabd.c new file mode 100644 index 0000000000..24ff2031f3 --- /dev/null +++ b/rbutil/rbutilqt/mspack/cabd.c | |||
@@ -0,0 +1,1444 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2011 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.h> | ||
25 | #include <cab.h> | ||
26 | #include <assert.h> | ||
27 | |||
28 | /* Notes on compliance with cabinet specification: | ||
29 | * | ||
30 | * One of the main changes between cabextract 0.6 and libmspack's cab | ||
31 | * decompressor is the move from block-oriented decompression to | ||
32 | * stream-oriented decompression. | ||
33 | * | ||
34 | * cabextract would read one data block from disk, decompress it with the | ||
35 | * appropriate method, then write the decompressed data. The CAB | ||
36 | * specification is specifically designed to work like this, as it ensures | ||
37 | * compression matches do not span the maximum decompressed block size | ||
38 | * limit of 32kb. | ||
39 | * | ||
40 | * However, the compression algorithms used are stream oriented, with | ||
41 | * specific hacks added to them to enforce the "individual 32kb blocks" | ||
42 | * rule in CABs. In other file formats, they do not have this limitation. | ||
43 | * | ||
44 | * In order to make more generalised decompressors, libmspack's CAB | ||
45 | * decompressor has moved from being block-oriented to more stream | ||
46 | * oriented. This also makes decompression slightly faster. | ||
47 | * | ||
48 | * However, this leads to incompliance with the CAB specification. The | ||
49 | * CAB controller can no longer ensure each block of input given to the | ||
50 | * decompressors is matched with their output. The "decompressed size" of | ||
51 | * each individual block is thrown away. | ||
52 | * | ||
53 | * Each CAB block is supposed to be seen as individually compressed. This | ||
54 | * means each consecutive data block can have completely different | ||
55 | * "uncompressed" sizes, ranging from 1 to 32768 bytes. However, in | ||
56 | * reality, all data blocks in a folder decompress to exactly 32768 bytes, | ||
57 | * excepting the final block. | ||
58 | * | ||
59 | * Given this situation, the decompression algorithms are designed to | ||
60 | * realign their input bitstreams on 32768 output-byte boundaries, and | ||
61 | * various other special cases have been made. libmspack will not | ||
62 | * correctly decompress LZX or Quantum compressed folders where the blocks | ||
63 | * do not follow this "32768 bytes until last block" pattern. It could be | ||
64 | * implemented if needed, but hopefully this is not necessary -- it has | ||
65 | * not been seen in over 3Gb of CAB archives. | ||
66 | */ | ||
67 | |||
68 | /* prototypes */ | ||
69 | static struct mscabd_cabinet * cabd_open( | ||
70 | struct mscab_decompressor *base, const char *filename); | ||
71 | static void cabd_close( | ||
72 | struct mscab_decompressor *base, struct mscabd_cabinet *origcab); | ||
73 | static int cabd_read_headers( | ||
74 | struct mspack_system *sys, struct mspack_file *fh, | ||
75 | struct mscabd_cabinet_p *cab, off_t offset, int quiet); | ||
76 | static char *cabd_read_string( | ||
77 | struct mspack_system *sys, struct mspack_file *fh, | ||
78 | struct mscabd_cabinet_p *cab, int *error); | ||
79 | |||
80 | static struct mscabd_cabinet *cabd_search( | ||
81 | struct mscab_decompressor *base, const char *filename); | ||
82 | static int cabd_find( | ||
83 | struct mscab_decompressor_p *self, unsigned char *buf, | ||
84 | struct mspack_file *fh, const char *filename, off_t flen, | ||
85 | off_t *firstlen, struct mscabd_cabinet_p **firstcab); | ||
86 | |||
87 | static int cabd_prepend( | ||
88 | struct mscab_decompressor *base, struct mscabd_cabinet *cab, | ||
89 | struct mscabd_cabinet *prevcab); | ||
90 | static int cabd_append( | ||
91 | struct mscab_decompressor *base, struct mscabd_cabinet *cab, | ||
92 | struct mscabd_cabinet *nextcab); | ||
93 | static int cabd_merge( | ||
94 | struct mscab_decompressor *base, struct mscabd_cabinet *lcab, | ||
95 | struct mscabd_cabinet *rcab); | ||
96 | static int cabd_can_merge_folders( | ||
97 | struct mspack_system *sys, struct mscabd_folder_p *lfol, | ||
98 | struct mscabd_folder_p *rfol); | ||
99 | |||
100 | static int cabd_extract( | ||
101 | struct mscab_decompressor *base, struct mscabd_file *file, | ||
102 | const char *filename); | ||
103 | static int cabd_init_decomp( | ||
104 | struct mscab_decompressor_p *self, unsigned int ct); | ||
105 | static void cabd_free_decomp( | ||
106 | struct mscab_decompressor_p *self); | ||
107 | static int cabd_sys_read( | ||
108 | struct mspack_file *file, void *buffer, int bytes); | ||
109 | static int cabd_sys_write( | ||
110 | struct mspack_file *file, void *buffer, int bytes); | ||
111 | static int cabd_sys_read_block( | ||
112 | struct mspack_system *sys, struct mscabd_decompress_state *d, int *out, | ||
113 | int ignore_cksum); | ||
114 | static unsigned int cabd_checksum( | ||
115 | unsigned char *data, unsigned int bytes, unsigned int cksum); | ||
116 | static struct noned_state *noned_init( | ||
117 | struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out, | ||
118 | int bufsize); | ||
119 | |||
120 | static int noned_decompress( | ||
121 | struct noned_state *s, off_t bytes); | ||
122 | static void noned_free( | ||
123 | struct noned_state *state); | ||
124 | |||
125 | static int cabd_param( | ||
126 | struct mscab_decompressor *base, int param, int value); | ||
127 | |||
128 | static int cabd_error( | ||
129 | struct mscab_decompressor *base); | ||
130 | |||
131 | |||
132 | /*************************************** | ||
133 | * MSPACK_CREATE_CAB_DECOMPRESSOR | ||
134 | *************************************** | ||
135 | * constructor | ||
136 | */ | ||
137 | struct mscab_decompressor * | ||
138 | mspack_create_cab_decompressor(struct mspack_system *sys) | ||
139 | { | ||
140 | struct mscab_decompressor_p *self = NULL; | ||
141 | |||
142 | if (!sys) sys = mspack_default_system; | ||
143 | if (!mspack_valid_system(sys)) return NULL; | ||
144 | |||
145 | if ((self = (struct mscab_decompressor_p *) sys->alloc(sys, sizeof(struct mscab_decompressor_p)))) { | ||
146 | self->base.open = &cabd_open; | ||
147 | self->base.close = &cabd_close; | ||
148 | self->base.search = &cabd_search; | ||
149 | self->base.extract = &cabd_extract; | ||
150 | self->base.prepend = &cabd_prepend; | ||
151 | self->base.append = &cabd_append; | ||
152 | self->base.set_param = &cabd_param; | ||
153 | self->base.last_error = &cabd_error; | ||
154 | self->system = sys; | ||
155 | self->d = NULL; | ||
156 | self->error = MSPACK_ERR_OK; | ||
157 | |||
158 | self->param[MSCABD_PARAM_SEARCHBUF] = 32768; | ||
159 | self->param[MSCABD_PARAM_FIXMSZIP] = 0; | ||
160 | self->param[MSCABD_PARAM_DECOMPBUF] = 4096; | ||
161 | } | ||
162 | return (struct mscab_decompressor *) self; | ||
163 | } | ||
164 | |||
165 | /*************************************** | ||
166 | * MSPACK_DESTROY_CAB_DECOMPRESSOR | ||
167 | *************************************** | ||
168 | * destructor | ||
169 | */ | ||
170 | void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) { | ||
171 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
172 | if (self) { | ||
173 | struct mspack_system *sys = self->system; | ||
174 | cabd_free_decomp(self); | ||
175 | if (self->d) { | ||
176 | if (self->d->infh) sys->close(self->d->infh); | ||
177 | sys->free(self->d); | ||
178 | } | ||
179 | sys->free(self); | ||
180 | } | ||
181 | } | ||
182 | |||
183 | |||
184 | /*************************************** | ||
185 | * CABD_OPEN | ||
186 | *************************************** | ||
187 | * opens a file and tries to read it as a cabinet file | ||
188 | */ | ||
189 | static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, | ||
190 | const char *filename) | ||
191 | { | ||
192 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
193 | struct mscabd_cabinet_p *cab = NULL; | ||
194 | struct mspack_system *sys; | ||
195 | struct mspack_file *fh; | ||
196 | int error; | ||
197 | |||
198 | if (!base) return NULL; | ||
199 | sys = self->system; | ||
200 | |||
201 | if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { | ||
202 | if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { | ||
203 | cab->base.filename = filename; | ||
204 | error = cabd_read_headers(sys, fh, cab, (off_t) 0, 0); | ||
205 | if (error) { | ||
206 | cabd_close(base, (struct mscabd_cabinet *) cab); | ||
207 | cab = NULL; | ||
208 | } | ||
209 | self->error = error; | ||
210 | } | ||
211 | else { | ||
212 | self->error = MSPACK_ERR_NOMEMORY; | ||
213 | } | ||
214 | sys->close(fh); | ||
215 | } | ||
216 | else { | ||
217 | self->error = MSPACK_ERR_OPEN; | ||
218 | } | ||
219 | return (struct mscabd_cabinet *) cab; | ||
220 | } | ||
221 | |||
222 | /*************************************** | ||
223 | * CABD_CLOSE | ||
224 | *************************************** | ||
225 | * frees all memory associated with a given mscabd_cabinet. | ||
226 | */ | ||
227 | static void cabd_close(struct mscab_decompressor *base, | ||
228 | struct mscabd_cabinet *origcab) | ||
229 | { | ||
230 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
231 | struct mscabd_folder_data *dat, *ndat; | ||
232 | struct mscabd_cabinet *cab, *ncab; | ||
233 | struct mscabd_folder *fol, *nfol; | ||
234 | struct mscabd_file *fi, *nfi; | ||
235 | struct mspack_system *sys; | ||
236 | |||
237 | if (!base) return; | ||
238 | sys = self->system; | ||
239 | |||
240 | self->error = MSPACK_ERR_OK; | ||
241 | |||
242 | while (origcab) { | ||
243 | /* free files */ | ||
244 | for (fi = origcab->files; fi; fi = nfi) { | ||
245 | nfi = fi->next; | ||
246 | sys->free(fi->filename); | ||
247 | sys->free(fi); | ||
248 | } | ||
249 | |||
250 | /* free folders */ | ||
251 | for (fol = origcab->folders; fol; fol = nfol) { | ||
252 | nfol = fol->next; | ||
253 | |||
254 | /* free folder decompression state if it has been decompressed */ | ||
255 | if (self->d && (self->d->folder == (struct mscabd_folder_p *) fol)) { | ||
256 | if (self->d->infh) sys->close(self->d->infh); | ||
257 | cabd_free_decomp(self); | ||
258 | sys->free(self->d); | ||
259 | self->d = NULL; | ||
260 | } | ||
261 | |||
262 | /* free folder data segments */ | ||
263 | for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) { | ||
264 | ndat = dat->next; | ||
265 | sys->free(dat); | ||
266 | } | ||
267 | sys->free(fol); | ||
268 | } | ||
269 | |||
270 | /* free predecessor cabinets (and the original cabinet's strings) */ | ||
271 | for (cab = origcab; cab; cab = ncab) { | ||
272 | ncab = cab->prevcab; | ||
273 | sys->free(cab->prevname); | ||
274 | sys->free(cab->nextname); | ||
275 | sys->free(cab->previnfo); | ||
276 | sys->free(cab->nextinfo); | ||
277 | if (cab != origcab) sys->free(cab); | ||
278 | } | ||
279 | |||
280 | /* free successor cabinets */ | ||
281 | for (cab = origcab->nextcab; cab; cab = ncab) { | ||
282 | ncab = cab->nextcab; | ||
283 | sys->free(cab->prevname); | ||
284 | sys->free(cab->nextname); | ||
285 | sys->free(cab->previnfo); | ||
286 | sys->free(cab->nextinfo); | ||
287 | sys->free(cab); | ||
288 | } | ||
289 | |||
290 | /* free actual cabinet structure */ | ||
291 | cab = origcab->next; | ||
292 | sys->free(origcab); | ||
293 | |||
294 | /* repeat full procedure again with the cab->next pointer (if set) */ | ||
295 | origcab = cab; | ||
296 | } | ||
297 | } | ||
298 | |||
299 | /*************************************** | ||
300 | * CABD_READ_HEADERS | ||
301 | *************************************** | ||
302 | * reads the cabinet file header, folder list and file list. | ||
303 | * fills out a pre-existing mscabd_cabinet structure, allocates memory | ||
304 | * for folders and files as necessary | ||
305 | */ | ||
306 | static int cabd_read_headers(struct mspack_system *sys, | ||
307 | struct mspack_file *fh, | ||
308 | struct mscabd_cabinet_p *cab, | ||
309 | off_t offset, int quiet) | ||
310 | { | ||
311 | int num_folders, num_files, folder_resv, i, x; | ||
312 | struct mscabd_folder_p *fol, *linkfol = NULL; | ||
313 | struct mscabd_file *file, *linkfile = NULL; | ||
314 | unsigned char buf[64]; | ||
315 | |||
316 | /* initialise pointers */ | ||
317 | cab->base.next = NULL; | ||
318 | cab->base.files = NULL; | ||
319 | cab->base.folders = NULL; | ||
320 | cab->base.prevcab = cab->base.nextcab = NULL; | ||
321 | cab->base.prevname = cab->base.nextname = NULL; | ||
322 | cab->base.previnfo = cab->base.nextinfo = NULL; | ||
323 | |||
324 | cab->base.base_offset = offset; | ||
325 | |||
326 | /* seek to CFHEADER */ | ||
327 | if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { | ||
328 | return MSPACK_ERR_SEEK; | ||
329 | } | ||
330 | |||
331 | /* read in the CFHEADER */ | ||
332 | if (sys->read(fh, &buf[0], cfhead_SIZEOF) != cfhead_SIZEOF) { | ||
333 | return MSPACK_ERR_READ; | ||
334 | } | ||
335 | |||
336 | /* check for "MSCF" signature */ | ||
337 | if (EndGetI32(&buf[cfhead_Signature]) != 0x4643534D) { | ||
338 | return MSPACK_ERR_SIGNATURE; | ||
339 | } | ||
340 | |||
341 | /* some basic header fields */ | ||
342 | cab->base.length = EndGetI32(&buf[cfhead_CabinetSize]); | ||
343 | cab->base.set_id = EndGetI16(&buf[cfhead_SetID]); | ||
344 | cab->base.set_index = EndGetI16(&buf[cfhead_CabinetIndex]); | ||
345 | |||
346 | /* get the number of folders */ | ||
347 | num_folders = EndGetI16(&buf[cfhead_NumFolders]); | ||
348 | if (num_folders == 0) { | ||
349 | if (!quiet) sys->message(fh, "no folders in cabinet."); | ||
350 | return MSPACK_ERR_DATAFORMAT; | ||
351 | } | ||
352 | |||
353 | /* get the number of files */ | ||
354 | num_files = EndGetI16(&buf[cfhead_NumFiles]); | ||
355 | if (num_files == 0) { | ||
356 | if (!quiet) sys->message(fh, "no files in cabinet."); | ||
357 | return MSPACK_ERR_DATAFORMAT; | ||
358 | } | ||
359 | |||
360 | /* check cabinet version */ | ||
361 | if ((buf[cfhead_MajorVersion] != 1) && (buf[cfhead_MinorVersion] != 3)) { | ||
362 | if (!quiet) sys->message(fh, "WARNING; cabinet version is not 1.3"); | ||
363 | } | ||
364 | |||
365 | /* read the reserved-sizes part of header, if present */ | ||
366 | cab->base.flags = EndGetI16(&buf[cfhead_Flags]); | ||
367 | if (cab->base.flags & cfheadRESERVE_PRESENT) { | ||
368 | if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) { | ||
369 | return MSPACK_ERR_READ; | ||
370 | } | ||
371 | cab->base.header_resv = EndGetI16(&buf[cfheadext_HeaderReserved]); | ||
372 | folder_resv = buf[cfheadext_FolderReserved]; | ||
373 | cab->block_resv = buf[cfheadext_DataReserved]; | ||
374 | |||
375 | if (cab->base.header_resv > 60000) { | ||
376 | if (!quiet) sys->message(fh, "WARNING; reserved header > 60000."); | ||
377 | } | ||
378 | |||
379 | /* skip the reserved header */ | ||
380 | if (cab->base.header_resv) { | ||
381 | if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) { | ||
382 | return MSPACK_ERR_SEEK; | ||
383 | } | ||
384 | } | ||
385 | } | ||
386 | else { | ||
387 | cab->base.header_resv = 0; | ||
388 | folder_resv = 0; | ||
389 | cab->block_resv = 0; | ||
390 | } | ||
391 | |||
392 | /* read name and info of preceeding cabinet in set, if present */ | ||
393 | if (cab->base.flags & cfheadPREV_CABINET) { | ||
394 | cab->base.prevname = cabd_read_string(sys, fh, cab, &x); if (x) return x; | ||
395 | cab->base.previnfo = cabd_read_string(sys, fh, cab, &x); if (x) return x; | ||
396 | } | ||
397 | |||
398 | /* read name and info of next cabinet in set, if present */ | ||
399 | if (cab->base.flags & cfheadNEXT_CABINET) { | ||
400 | cab->base.nextname = cabd_read_string(sys, fh, cab, &x); if (x) return x; | ||
401 | cab->base.nextinfo = cabd_read_string(sys, fh, cab, &x); if (x) return x; | ||
402 | } | ||
403 | |||
404 | /* read folders */ | ||
405 | for (i = 0; i < num_folders; i++) { | ||
406 | if (sys->read(fh, &buf[0], cffold_SIZEOF) != cffold_SIZEOF) { | ||
407 | return MSPACK_ERR_READ; | ||
408 | } | ||
409 | if (folder_resv) { | ||
410 | if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) { | ||
411 | return MSPACK_ERR_SEEK; | ||
412 | } | ||
413 | } | ||
414 | |||
415 | if (!(fol = (struct mscabd_folder_p *) sys->alloc(sys, sizeof(struct mscabd_folder_p)))) { | ||
416 | return MSPACK_ERR_NOMEMORY; | ||
417 | } | ||
418 | fol->base.next = NULL; | ||
419 | fol->base.comp_type = EndGetI16(&buf[cffold_CompType]); | ||
420 | fol->base.num_blocks = EndGetI16(&buf[cffold_NumBlocks]); | ||
421 | fol->data.next = NULL; | ||
422 | fol->data.cab = (struct mscabd_cabinet_p *) cab; | ||
423 | fol->data.offset = offset + (off_t) | ||
424 | ( (unsigned int) EndGetI32(&buf[cffold_DataOffset]) ); | ||
425 | fol->merge_prev = NULL; | ||
426 | fol->merge_next = NULL; | ||
427 | |||
428 | /* link folder into list of folders */ | ||
429 | if (!linkfol) cab->base.folders = (struct mscabd_folder *) fol; | ||
430 | else linkfol->base.next = (struct mscabd_folder *) fol; | ||
431 | linkfol = fol; | ||
432 | } | ||
433 | |||
434 | /* read files */ | ||
435 | for (i = 0; i < num_files; i++) { | ||
436 | if (sys->read(fh, &buf[0], cffile_SIZEOF) != cffile_SIZEOF) { | ||
437 | return MSPACK_ERR_READ; | ||
438 | } | ||
439 | |||
440 | if (!(file = (struct mscabd_file *) sys->alloc(sys, sizeof(struct mscabd_file)))) { | ||
441 | return MSPACK_ERR_NOMEMORY; | ||
442 | } | ||
443 | |||
444 | file->next = NULL; | ||
445 | file->length = EndGetI32(&buf[cffile_UncompressedSize]); | ||
446 | file->attribs = EndGetI16(&buf[cffile_Attribs]); | ||
447 | file->offset = EndGetI32(&buf[cffile_FolderOffset]); | ||
448 | |||
449 | /* set folder pointer */ | ||
450 | x = EndGetI16(&buf[cffile_FolderIndex]); | ||
451 | if (x < cffileCONTINUED_FROM_PREV) { | ||
452 | /* normal folder index; count up to the correct folder. the folder | ||
453 | * pointer will be NULL if folder index is invalid */ | ||
454 | struct mscabd_folder *ifol = cab->base.folders; | ||
455 | while (x--) if (ifol) ifol = ifol->next; | ||
456 | file->folder = ifol; | ||
457 | |||
458 | if (!ifol) { | ||
459 | sys->free(file); | ||
460 | D(("invalid folder index")) | ||
461 | return MSPACK_ERR_DATAFORMAT; | ||
462 | } | ||
463 | } | ||
464 | else { | ||
465 | /* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or | ||
466 | * CONTINUED_PREV_AND_NEXT */ | ||
467 | if ((x == cffileCONTINUED_TO_NEXT) || | ||
468 | (x == cffileCONTINUED_PREV_AND_NEXT)) | ||
469 | { | ||
470 | /* get last folder */ | ||
471 | struct mscabd_folder *ifol = cab->base.folders; | ||
472 | while (ifol->next) ifol = ifol->next; | ||
473 | file->folder = ifol; | ||
474 | |||
475 | /* set "merge next" pointer */ | ||
476 | fol = (struct mscabd_folder_p *) ifol; | ||
477 | if (!fol->merge_next) fol->merge_next = file; | ||
478 | } | ||
479 | |||
480 | if ((x == cffileCONTINUED_FROM_PREV) || | ||
481 | (x == cffileCONTINUED_PREV_AND_NEXT)) | ||
482 | { | ||
483 | /* get first folder */ | ||
484 | file->folder = cab->base.folders; | ||
485 | |||
486 | /* set "merge prev" pointer */ | ||
487 | fol = (struct mscabd_folder_p *) file->folder; | ||
488 | if (!fol->merge_prev) fol->merge_prev = file; | ||
489 | } | ||
490 | } | ||
491 | |||
492 | /* get time */ | ||
493 | x = EndGetI16(&buf[cffile_Time]); | ||
494 | file->time_h = x >> 11; | ||
495 | file->time_m = (x >> 5) & 0x3F; | ||
496 | file->time_s = (x << 1) & 0x3E; | ||
497 | |||
498 | /* get date */ | ||
499 | x = EndGetI16(&buf[cffile_Date]); | ||
500 | file->date_d = x & 0x1F; | ||
501 | file->date_m = (x >> 5) & 0xF; | ||
502 | file->date_y = (x >> 9) + 1980; | ||
503 | |||
504 | /* get filename */ | ||
505 | file->filename = cabd_read_string(sys, fh, cab, &x); | ||
506 | if (x) { | ||
507 | sys->free(file); | ||
508 | return x; | ||
509 | } | ||
510 | |||
511 | /* link file entry into file list */ | ||
512 | if (!linkfile) cab->base.files = file; | ||
513 | else linkfile->next = file; | ||
514 | linkfile = file; | ||
515 | } | ||
516 | |||
517 | return MSPACK_ERR_OK; | ||
518 | } | ||
519 | |||
520 | static char *cabd_read_string(struct mspack_system *sys, | ||
521 | struct mspack_file *fh, | ||
522 | struct mscabd_cabinet_p *cab, int *error) | ||
523 | { | ||
524 | off_t base = sys->tell(fh); | ||
525 | char buf[256], *str; | ||
526 | unsigned int len, i, ok; | ||
527 | |||
528 | /* read up to 256 bytes */ | ||
529 | len = sys->read(fh, &buf[0], 256); | ||
530 | |||
531 | /* search for a null terminator in the buffer */ | ||
532 | for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; } | ||
533 | if (!ok) { | ||
534 | *error = MSPACK_ERR_DATAFORMAT; | ||
535 | return NULL; | ||
536 | } | ||
537 | |||
538 | len = i + 1; | ||
539 | |||
540 | /* set the data stream to just after the string and return */ | ||
541 | if (sys->seek(fh, base + (off_t)len, MSPACK_SYS_SEEK_START)) { | ||
542 | *error = MSPACK_ERR_SEEK; | ||
543 | return NULL; | ||
544 | } | ||
545 | |||
546 | if (!(str = (char *) sys->alloc(sys, len))) { | ||
547 | *error = MSPACK_ERR_NOMEMORY; | ||
548 | return NULL; | ||
549 | } | ||
550 | |||
551 | sys->copy(&buf[0], str, len); | ||
552 | *error = MSPACK_ERR_OK; | ||
553 | return str; | ||
554 | } | ||
555 | |||
556 | /*************************************** | ||
557 | * CABD_SEARCH, CABD_FIND | ||
558 | *************************************** | ||
559 | * cabd_search opens a file, finds its extent, allocates a search buffer, | ||
560 | * then reads through the whole file looking for possible cabinet headers. | ||
561 | * if it finds any, it tries to read them as real cabinets. returns a linked | ||
562 | * list of results | ||
563 | * | ||
564 | * cabd_find is the inner loop of cabd_search, to make it easier to | ||
565 | * break out of the loop and be sure that all resources are freed | ||
566 | */ | ||
567 | static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, | ||
568 | const char *filename) | ||
569 | { | ||
570 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
571 | struct mscabd_cabinet_p *cab = NULL; | ||
572 | struct mspack_system *sys; | ||
573 | unsigned char *search_buf; | ||
574 | struct mspack_file *fh; | ||
575 | off_t filelen, firstlen = 0; | ||
576 | |||
577 | if (!base) return NULL; | ||
578 | sys = self->system; | ||
579 | |||
580 | /* allocate a search buffer */ | ||
581 | search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->param[MSCABD_PARAM_SEARCHBUF]); | ||
582 | if (!search_buf) { | ||
583 | self->error = MSPACK_ERR_NOMEMORY; | ||
584 | return NULL; | ||
585 | } | ||
586 | |||
587 | /* open file and get its full file length */ | ||
588 | if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { | ||
589 | if (!(self->error = mspack_sys_filelen(sys, fh, &filelen))) { | ||
590 | self->error = cabd_find(self, search_buf, fh, filename, | ||
591 | filelen, &firstlen, &cab); | ||
592 | } | ||
593 | |||
594 | /* truncated / extraneous data warning: */ | ||
595 | if (firstlen && (firstlen != filelen) && | ||
596 | (!cab || (cab->base.base_offset == 0))) | ||
597 | { | ||
598 | if (firstlen < filelen) { | ||
599 | sys->message(fh, "WARNING; possible %" LD | ||
600 | " extra bytes at end of file.", | ||
601 | filelen - firstlen); | ||
602 | } | ||
603 | else { | ||
604 | sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.", | ||
605 | firstlen - filelen); | ||
606 | } | ||
607 | } | ||
608 | |||
609 | sys->close(fh); | ||
610 | } | ||
611 | else { | ||
612 | self->error = MSPACK_ERR_OPEN; | ||
613 | } | ||
614 | |||
615 | /* free the search buffer */ | ||
616 | sys->free(search_buf); | ||
617 | |||
618 | return (struct mscabd_cabinet *) cab; | ||
619 | } | ||
620 | |||
621 | static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, | ||
622 | struct mspack_file *fh, const char *filename, off_t flen, | ||
623 | off_t *firstlen, struct mscabd_cabinet_p **firstcab) | ||
624 | { | ||
625 | struct mscabd_cabinet_p *cab, *link = NULL; | ||
626 | off_t caboff, offset, length; | ||
627 | struct mspack_system *sys = self->system; | ||
628 | unsigned char *p, *pend, state = 0; | ||
629 | unsigned int cablen_u32 = 0, foffset_u32 = 0; | ||
630 | int false_cabs = 0; | ||
631 | |||
632 | #ifndef LARGEFILE_SUPPORT | ||
633 | /* detect 32-bit off_t overflow */ | ||
634 | if (flen < 0) { | ||
635 | sys->message(fh, largefile_msg); | ||
636 | return MSPACK_ERR_OK; | ||
637 | } | ||
638 | #endif | ||
639 | |||
640 | /* search through the full file length */ | ||
641 | for (offset = 0; offset < flen; offset += length) { | ||
642 | /* search length is either the full length of the search buffer, or the | ||
643 | * amount of data remaining to the end of the file, whichever is less. */ | ||
644 | length = flen - offset; | ||
645 | if (length > self->param[MSCABD_PARAM_SEARCHBUF]) { | ||
646 | length = self->param[MSCABD_PARAM_SEARCHBUF]; | ||
647 | } | ||
648 | |||
649 | /* fill the search buffer with data from disk */ | ||
650 | if (sys->read(fh, &buf[0], (int) length) != (int) length) { | ||
651 | return MSPACK_ERR_READ; | ||
652 | } | ||
653 | |||
654 | /* FAQ avoidance strategy */ | ||
655 | if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) { | ||
656 | sys->message(fh, "WARNING; found InstallShield header. " | ||
657 | "This is probably an InstallShield file. " | ||
658 | "Use UNSHIELD from www.synce.org to unpack it."); | ||
659 | } | ||
660 | |||
661 | /* read through the entire buffer. */ | ||
662 | for (p = &buf[0], pend = &buf[length]; p < pend; ) { | ||
663 | switch (state) { | ||
664 | /* starting state */ | ||
665 | case 0: | ||
666 | /* we spend most of our time in this while loop, looking for | ||
667 | * a leading 'M' of the 'MSCF' signature */ | ||
668 | while (p < pend && *p != 0x4D) p++; | ||
669 | /* if we found tht 'M', advance state */ | ||
670 | if (p++ < pend) state = 1; | ||
671 | break; | ||
672 | |||
673 | /* verify that the next 3 bytes are 'S', 'C' and 'F' */ | ||
674 | case 1: state = (*p++ == 0x53) ? 2 : 0; break; | ||
675 | case 2: state = (*p++ == 0x43) ? 3 : 0; break; | ||
676 | case 3: state = (*p++ == 0x46) ? 4 : 0; break; | ||
677 | |||
678 | /* we don't care about bytes 4-7 (see default: for action) */ | ||
679 | |||
680 | /* bytes 8-11 are the overall length of the cabinet */ | ||
681 | case 8: cablen_u32 = *p++; state++; break; | ||
682 | case 9: cablen_u32 |= *p++ << 8; state++; break; | ||
683 | case 10: cablen_u32 |= *p++ << 16; state++; break; | ||
684 | case 11: cablen_u32 |= *p++ << 24; state++; break; | ||
685 | |||
686 | /* we don't care about bytes 12-15 (see default: for action) */ | ||
687 | |||
688 | /* bytes 16-19 are the offset within the cabinet of the filedata */ | ||
689 | case 16: foffset_u32 = *p++; state++; break; | ||
690 | case 17: foffset_u32 |= *p++ << 8; state++; break; | ||
691 | case 18: foffset_u32 |= *p++ << 16; state++; break; | ||
692 | case 19: foffset_u32 |= *p++ << 24; | ||
693 | /* now we have recieved 20 bytes of potential cab header. work out | ||
694 | * the offset in the file of this potential cabinet */ | ||
695 | caboff = offset + (p - &buf[0]) - 20; | ||
696 | |||
697 | /* should reading cabinet fail, restart search just after 'MSCF' */ | ||
698 | offset = caboff + 4; | ||
699 | |||
700 | /* capture the "length of cabinet" field if there is a cabinet at | ||
701 | * offset 0 in the file, regardless of whether the cabinet can be | ||
702 | * read correctly or not */ | ||
703 | if (caboff == 0) *firstlen = (off_t) cablen_u32; | ||
704 | |||
705 | /* check that the files offset is less than the alleged length of | ||
706 | * the cabinet, and that the offset + the alleged length are | ||
707 | * 'roughly' within the end of overall file length */ | ||
708 | if ((foffset_u32 < cablen_u32) && | ||
709 | ((caboff + (off_t) foffset_u32) < (flen + 32)) && | ||
710 | ((caboff + (off_t) cablen_u32) < (flen + 32)) ) | ||
711 | { | ||
712 | /* likely cabinet found -- try reading it */ | ||
713 | if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { | ||
714 | return MSPACK_ERR_NOMEMORY; | ||
715 | } | ||
716 | cab->base.filename = filename; | ||
717 | if (cabd_read_headers(sys, fh, cab, caboff, 1)) { | ||
718 | /* destroy the failed cabinet */ | ||
719 | cabd_close((struct mscab_decompressor *) self, | ||
720 | (struct mscabd_cabinet *) cab); | ||
721 | false_cabs++; | ||
722 | } | ||
723 | else { | ||
724 | /* cabinet read correctly! */ | ||
725 | |||
726 | /* link the cab into the list */ | ||
727 | if (!link) *firstcab = cab; | ||
728 | else link->base.next = (struct mscabd_cabinet *) cab; | ||
729 | link = cab; | ||
730 | |||
731 | /* cause the search to restart after this cab's data. */ | ||
732 | offset = caboff + (off_t) cablen_u32; | ||
733 | |||
734 | #ifndef LARGEFILE_SUPPORT | ||
735 | /* detect 32-bit off_t overflow */ | ||
736 | if (offset < caboff) { | ||
737 | sys->message(fh, largefile_msg); | ||
738 | return MSPACK_ERR_OK; | ||
739 | } | ||
740 | #endif | ||
741 | } | ||
742 | } | ||
743 | |||
744 | /* restart search */ | ||
745 | if (offset >= flen) return MSPACK_ERR_OK; | ||
746 | if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { | ||
747 | return MSPACK_ERR_SEEK; | ||
748 | } | ||
749 | length = 0; | ||
750 | p = pend; | ||
751 | state = 0; | ||
752 | break; | ||
753 | |||
754 | /* for bytes 4-7 and 12-15, just advance state/pointer */ | ||
755 | default: | ||
756 | p++, state++; | ||
757 | } /* switch(state) */ | ||
758 | } /* for (... p < pend ...) */ | ||
759 | } /* for (... offset < length ...) */ | ||
760 | |||
761 | if (false_cabs) { | ||
762 | D(("%d false cabinets found", false_cabs)) | ||
763 | } | ||
764 | |||
765 | return MSPACK_ERR_OK; | ||
766 | } | ||
767 | |||
768 | /*************************************** | ||
769 | * CABD_MERGE, CABD_PREPEND, CABD_APPEND | ||
770 | *************************************** | ||
771 | * joins cabinets together, also merges split folders between these two | ||
772 | * cabinets only. This includes freeing the duplicate folder and file(s) | ||
773 | * and allocating a further mscabd_folder_data structure to append to the | ||
774 | * merged folder's data parts list. | ||
775 | */ | ||
776 | static int cabd_prepend(struct mscab_decompressor *base, | ||
777 | struct mscabd_cabinet *cab, | ||
778 | struct mscabd_cabinet *prevcab) | ||
779 | { | ||
780 | return cabd_merge(base, prevcab, cab); | ||
781 | } | ||
782 | |||
783 | static int cabd_append(struct mscab_decompressor *base, | ||
784 | struct mscabd_cabinet *cab, | ||
785 | struct mscabd_cabinet *nextcab) | ||
786 | { | ||
787 | return cabd_merge(base, cab, nextcab); | ||
788 | } | ||
789 | |||
790 | static int cabd_merge(struct mscab_decompressor *base, | ||
791 | struct mscabd_cabinet *lcab, | ||
792 | struct mscabd_cabinet *rcab) | ||
793 | { | ||
794 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
795 | struct mscabd_folder_data *data, *ndata; | ||
796 | struct mscabd_folder_p *lfol, *rfol; | ||
797 | struct mscabd_file *fi, *rfi, *lfi; | ||
798 | struct mscabd_cabinet *cab; | ||
799 | struct mspack_system *sys; | ||
800 | |||
801 | if (!self) return MSPACK_ERR_ARGS; | ||
802 | sys = self->system; | ||
803 | |||
804 | /* basic args check */ | ||
805 | if (!lcab || !rcab || (lcab == rcab)) { | ||
806 | D(("lcab NULL, rcab NULL or lcab = rcab")) | ||
807 | return self->error = MSPACK_ERR_ARGS; | ||
808 | } | ||
809 | |||
810 | /* check there's not already a cabinet attached */ | ||
811 | if (lcab->nextcab || rcab->prevcab) { | ||
812 | D(("cabs already joined")) | ||
813 | return self->error = MSPACK_ERR_ARGS; | ||
814 | } | ||
815 | |||
816 | /* do not create circular cabinet chains */ | ||
817 | for (cab = lcab->prevcab; cab; cab = cab->prevcab) { | ||
818 | if (cab == rcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;} | ||
819 | } | ||
820 | for (cab = rcab->nextcab; cab; cab = cab->nextcab) { | ||
821 | if (cab == lcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;} | ||
822 | } | ||
823 | |||
824 | /* warn about odd set IDs or indices */ | ||
825 | if (lcab->set_id != rcab->set_id) { | ||
826 | sys->message(NULL, "WARNING; merged cabinets with differing Set IDs."); | ||
827 | } | ||
828 | |||
829 | if (lcab->set_index > rcab->set_index) { | ||
830 | sys->message(NULL, "WARNING; merged cabinets with odd order."); | ||
831 | } | ||
832 | |||
833 | /* merging the last folder in lcab with the first folder in rcab */ | ||
834 | lfol = (struct mscabd_folder_p *) lcab->folders; | ||
835 | rfol = (struct mscabd_folder_p *) rcab->folders; | ||
836 | while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; | ||
837 | |||
838 | /* do we need to merge folders? */ | ||
839 | if (!lfol->merge_next && !rfol->merge_prev) { | ||
840 | /* no, at least one of the folders is not for merging */ | ||
841 | |||
842 | /* attach cabs */ | ||
843 | lcab->nextcab = rcab; | ||
844 | rcab->prevcab = lcab; | ||
845 | |||
846 | /* attach folders */ | ||
847 | lfol->base.next = (struct mscabd_folder *) rfol; | ||
848 | |||
849 | /* attach files */ | ||
850 | fi = lcab->files; | ||
851 | while (fi->next) fi = fi->next; | ||
852 | fi->next = rcab->files; | ||
853 | } | ||
854 | else { | ||
855 | /* folder merge required - do the files match? */ | ||
856 | if (! cabd_can_merge_folders(sys, lfol, rfol)) { | ||
857 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
858 | } | ||
859 | |||
860 | /* allocate a new folder data structure */ | ||
861 | if (!(data = (struct mscabd_folder_data *) sys->alloc(sys, sizeof(struct mscabd_folder_data)))) { | ||
862 | return self->error = MSPACK_ERR_NOMEMORY; | ||
863 | } | ||
864 | |||
865 | /* attach cabs */ | ||
866 | lcab->nextcab = rcab; | ||
867 | rcab->prevcab = lcab; | ||
868 | |||
869 | /* append rfol's data to lfol */ | ||
870 | ndata = &lfol->data; | ||
871 | while (ndata->next) ndata = ndata->next; | ||
872 | ndata->next = data; | ||
873 | *data = rfol->data; | ||
874 | rfol->data.next = NULL; | ||
875 | |||
876 | /* lfol becomes rfol. | ||
877 | * NOTE: special case, don't merge if rfol is merge prev and next, | ||
878 | * rfol->merge_next is going to be deleted, so keep lfol's version | ||
879 | * instead */ | ||
880 | lfol->base.num_blocks += rfol->base.num_blocks - 1; | ||
881 | if ((rfol->merge_next == NULL) || | ||
882 | (rfol->merge_next->folder != (struct mscabd_folder *) rfol)) | ||
883 | { | ||
884 | lfol->merge_next = rfol->merge_next; | ||
885 | } | ||
886 | |||
887 | /* attach the rfol's folder (except the merge folder) */ | ||
888 | while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; | ||
889 | lfol->base.next = rfol->base.next; | ||
890 | |||
891 | /* free disused merge folder */ | ||
892 | sys->free(rfol); | ||
893 | |||
894 | /* attach rfol's files */ | ||
895 | fi = lcab->files; | ||
896 | while (fi->next) fi = fi->next; | ||
897 | fi->next = rcab->files; | ||
898 | |||
899 | /* delete all files from rfol's merge folder */ | ||
900 | lfi = NULL; | ||
901 | for (fi = lcab->files; fi ; fi = rfi) { | ||
902 | rfi = fi->next; | ||
903 | /* if file's folder matches the merge folder, unlink and free it */ | ||
904 | if (fi->folder == (struct mscabd_folder *) rfol) { | ||
905 | if (lfi) lfi->next = rfi; else lcab->files = rfi; | ||
906 | sys->free(fi->filename); | ||
907 | sys->free(fi); | ||
908 | } | ||
909 | else lfi = fi; | ||
910 | } | ||
911 | } | ||
912 | |||
913 | /* all done! fix files and folders pointers in all cabs so they all | ||
914 | * point to the same list */ | ||
915 | for (cab = lcab->prevcab; cab; cab = cab->prevcab) { | ||
916 | cab->files = lcab->files; | ||
917 | cab->folders = lcab->folders; | ||
918 | } | ||
919 | |||
920 | for (cab = lcab->nextcab; cab; cab = cab->nextcab) { | ||
921 | cab->files = lcab->files; | ||
922 | cab->folders = lcab->folders; | ||
923 | } | ||
924 | |||
925 | return self->error = MSPACK_ERR_OK; | ||
926 | } | ||
927 | |||
928 | /* decides if two folders are OK to merge */ | ||
929 | static int cabd_can_merge_folders(struct mspack_system *sys, | ||
930 | struct mscabd_folder_p *lfol, | ||
931 | struct mscabd_folder_p *rfol) | ||
932 | { | ||
933 | struct mscabd_file *lfi, *rfi, *l, *r; | ||
934 | int matching = 1; | ||
935 | |||
936 | /* check that both folders use the same compression method/settings */ | ||
937 | if (lfol->base.comp_type != rfol->base.comp_type) { | ||
938 | D(("folder merge: compression type mismatch")) | ||
939 | return 0; | ||
940 | } | ||
941 | |||
942 | if (!(lfi = lfol->merge_next) || !(rfi = rfol->merge_prev)) { | ||
943 | D(("folder merge: one cabinet has no files to merge")) | ||
944 | return 0; | ||
945 | } | ||
946 | |||
947 | /* for all files in lfol (which is the last folder in whichever cab and | ||
948 | * only has files to merge), compare them to the files from rfol. They | ||
949 | * should be identical in number and order. to verify this, check the | ||
950 | * offset and length of each file. */ | ||
951 | for (l=lfi, r=rfi; l; l=l->next, r=r->next) { | ||
952 | if (!r || (l->offset != r->offset) || (l->length != r->length)) { | ||
953 | matching = 0; | ||
954 | break; | ||
955 | } | ||
956 | } | ||
957 | |||
958 | if (matching) return 1; | ||
959 | |||
960 | /* if rfol does not begin with an identical copy of the files in lfol, make | ||
961 | * make a judgement call; if at least ONE file from lfol is in rfol, allow | ||
962 | * the merge with a warning about missing files. */ | ||
963 | matching = 0; | ||
964 | for (l = lfi; l; l = l->next) { | ||
965 | for (r = rfi; r; r = r->next) { | ||
966 | if (l->offset == r->offset && l->length == r->length) break; | ||
967 | } | ||
968 | if (r) matching = 1; else sys->message(NULL, | ||
969 | "WARNING; merged file %s not listed in both cabinets", l->filename); | ||
970 | } | ||
971 | return matching; | ||
972 | } | ||
973 | |||
974 | |||
975 | /*************************************** | ||
976 | * CABD_EXTRACT | ||
977 | *************************************** | ||
978 | * extracts a file from a cabinet | ||
979 | */ | ||
980 | static int cabd_extract(struct mscab_decompressor *base, | ||
981 | struct mscabd_file *file, const char *filename) | ||
982 | { | ||
983 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
984 | struct mscabd_folder_p *fol; | ||
985 | struct mspack_system *sys; | ||
986 | struct mspack_file *fh; | ||
987 | |||
988 | if (!self) return MSPACK_ERR_ARGS; | ||
989 | if (!file) return self->error = MSPACK_ERR_ARGS; | ||
990 | |||
991 | sys = self->system; | ||
992 | fol = (struct mscabd_folder_p *) file->folder; | ||
993 | |||
994 | /* check if file can be extracted */ | ||
995 | if ((!fol) || (fol->merge_prev) || | ||
996 | (((file->offset + file->length) / CAB_BLOCKMAX) > fol->base.num_blocks)) | ||
997 | { | ||
998 | sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " | ||
999 | "cabinet set is incomplete.", file->filename); | ||
1000 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1001 | } | ||
1002 | |||
1003 | /* allocate generic decompression state */ | ||
1004 | if (!self->d) { | ||
1005 | self->d = (struct mscabd_decompress_state *) sys->alloc(sys, sizeof(struct mscabd_decompress_state)); | ||
1006 | if (!self->d) return self->error = MSPACK_ERR_NOMEMORY; | ||
1007 | self->d->folder = NULL; | ||
1008 | self->d->data = NULL; | ||
1009 | self->d->sys = *sys; | ||
1010 | self->d->sys.read = &cabd_sys_read; | ||
1011 | self->d->sys.write = &cabd_sys_write; | ||
1012 | self->d->state = NULL; | ||
1013 | self->d->infh = NULL; | ||
1014 | self->d->incab = NULL; | ||
1015 | } | ||
1016 | |||
1017 | /* do we need to change folder or reset the current folder? */ | ||
1018 | if ((self->d->folder != fol) || (self->d->offset > file->offset)) { | ||
1019 | /* do we need to open a new cab file? */ | ||
1020 | if (!self->d->infh || (fol->data.cab != self->d->incab)) { | ||
1021 | /* close previous file handle if from a different cab */ | ||
1022 | if (self->d->infh) sys->close(self->d->infh); | ||
1023 | self->d->incab = fol->data.cab; | ||
1024 | self->d->infh = sys->open(sys, fol->data.cab->base.filename, | ||
1025 | MSPACK_SYS_OPEN_READ); | ||
1026 | if (!self->d->infh) return self->error = MSPACK_ERR_OPEN; | ||
1027 | } | ||
1028 | /* seek to start of data blocks */ | ||
1029 | if (sys->seek(self->d->infh, fol->data.offset, MSPACK_SYS_SEEK_START)) { | ||
1030 | return self->error = MSPACK_ERR_SEEK; | ||
1031 | } | ||
1032 | |||
1033 | /* set up decompressor */ | ||
1034 | if (cabd_init_decomp(self, (unsigned int) fol->base.comp_type)) { | ||
1035 | return self->error; | ||
1036 | } | ||
1037 | |||
1038 | /* initialise new folder state */ | ||
1039 | self->d->folder = fol; | ||
1040 | self->d->data = &fol->data; | ||
1041 | self->d->offset = 0; | ||
1042 | self->d->block = 0; | ||
1043 | self->d->i_ptr = self->d->i_end = &self->d->input[0]; | ||
1044 | |||
1045 | /* read_error lasts for the lifetime of a decompressor */ | ||
1046 | self->read_error = MSPACK_ERR_OK; | ||
1047 | } | ||
1048 | |||
1049 | /* open file for output */ | ||
1050 | if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { | ||
1051 | return self->error = MSPACK_ERR_OPEN; | ||
1052 | } | ||
1053 | |||
1054 | self->error = MSPACK_ERR_OK; | ||
1055 | |||
1056 | /* if file has more than 0 bytes */ | ||
1057 | if (file->length) { | ||
1058 | off_t bytes; | ||
1059 | int error; | ||
1060 | /* get to correct offset. | ||
1061 | * - use NULL fh to say 'no writing' to cabd_sys_write() | ||
1062 | * - if cabd_sys_read() has an error, it will set self->read_error | ||
1063 | * and pass back MSPACK_ERR_READ | ||
1064 | */ | ||
1065 | self->d->outfh = NULL; | ||
1066 | if ((bytes = file->offset - self->d->offset)) { | ||
1067 | error = self->d->decompress(self->d->state, bytes); | ||
1068 | self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; | ||
1069 | } | ||
1070 | |||
1071 | /* if getting to the correct offset was error free, unpack file */ | ||
1072 | if (!self->error) { | ||
1073 | self->d->outfh = fh; | ||
1074 | error = self->d->decompress(self->d->state, (off_t) file->length); | ||
1075 | self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; | ||
1076 | } | ||
1077 | } | ||
1078 | |||
1079 | /* close output file */ | ||
1080 | sys->close(fh); | ||
1081 | self->d->outfh = NULL; | ||
1082 | |||
1083 | return self->error; | ||
1084 | } | ||
1085 | |||
1086 | /*************************************** | ||
1087 | * CABD_INIT_DECOMP, CABD_FREE_DECOMP | ||
1088 | *************************************** | ||
1089 | * cabd_init_decomp initialises decompression state, according to which | ||
1090 | * decompression method was used. relies on self->d->folder being the same | ||
1091 | * as when initialised. | ||
1092 | * | ||
1093 | * cabd_free_decomp frees decompression state, according to which method | ||
1094 | * was used. | ||
1095 | */ | ||
1096 | static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct) | ||
1097 | { | ||
1098 | struct mspack_file *fh = (struct mspack_file *) self; | ||
1099 | |||
1100 | assert(self && self->d); | ||
1101 | |||
1102 | /* free any existing decompressor */ | ||
1103 | cabd_free_decomp(self); | ||
1104 | |||
1105 | self->d->comp_type = ct; | ||
1106 | |||
1107 | switch (ct & cffoldCOMPTYPE_MASK) { | ||
1108 | case cffoldCOMPTYPE_NONE: | ||
1109 | self->d->decompress = (int (*)(void *, off_t)) &noned_decompress; | ||
1110 | self->d->state = noned_init(&self->d->sys, fh, fh, | ||
1111 | self->param[MSCABD_PARAM_DECOMPBUF]); | ||
1112 | break; | ||
1113 | case cffoldCOMPTYPE_MSZIP: | ||
1114 | self->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress; | ||
1115 | self->d->state = mszipd_init(&self->d->sys, fh, fh, | ||
1116 | self->param[MSCABD_PARAM_DECOMPBUF], | ||
1117 | self->param[MSCABD_PARAM_FIXMSZIP]); | ||
1118 | break; | ||
1119 | case cffoldCOMPTYPE_QUANTUM: | ||
1120 | self->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress; | ||
1121 | self->d->state = qtmd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, | ||
1122 | self->param[MSCABD_PARAM_DECOMPBUF]); | ||
1123 | break; | ||
1124 | case cffoldCOMPTYPE_LZX: | ||
1125 | self->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress; | ||
1126 | self->d->state = lzxd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, 0, | ||
1127 | self->param[MSCABD_PARAM_DECOMPBUF], (off_t) 0); | ||
1128 | break; | ||
1129 | default: | ||
1130 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1131 | } | ||
1132 | return self->error = (self->d->state) ? MSPACK_ERR_OK : MSPACK_ERR_NOMEMORY; | ||
1133 | } | ||
1134 | |||
1135 | static void cabd_free_decomp(struct mscab_decompressor_p *self) { | ||
1136 | if (!self || !self->d || !self->d->folder || !self->d->state) return; | ||
1137 | |||
1138 | switch (self->d->comp_type & cffoldCOMPTYPE_MASK) { | ||
1139 | case cffoldCOMPTYPE_NONE: noned_free((struct noned_state *) self->d->state); break; | ||
1140 | case cffoldCOMPTYPE_MSZIP: mszipd_free((struct mszipd_stream *) self->d->state); break; | ||
1141 | case cffoldCOMPTYPE_QUANTUM: qtmd_free((struct qtmd_stream *) self->d->state); break; | ||
1142 | case cffoldCOMPTYPE_LZX: lzxd_free((struct lzxd_stream *) self->d->state); break; | ||
1143 | } | ||
1144 | self->d->decompress = NULL; | ||
1145 | self->d->state = NULL; | ||
1146 | } | ||
1147 | |||
1148 | /*************************************** | ||
1149 | * CABD_SYS_READ, CABD_SYS_WRITE | ||
1150 | *************************************** | ||
1151 | * cabd_sys_read is the internal reader function which the decompressors | ||
1152 | * use. will read data blocks (and merge split blocks) from the cabinet | ||
1153 | * and serve the read bytes to the decompressors | ||
1154 | * | ||
1155 | * cabd_sys_write is the internal writer function which the decompressors | ||
1156 | * use. it either writes data to disk (self->d->outfh) with the real | ||
1157 | * sys->write() function, or does nothing with the data when | ||
1158 | * self->d->outfh == NULL. advances self->d->offset | ||
1159 | */ | ||
1160 | static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) { | ||
1161 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; | ||
1162 | unsigned char *buf = (unsigned char *) buffer; | ||
1163 | struct mspack_system *sys = self->system; | ||
1164 | int avail, todo, outlen, ignore_cksum; | ||
1165 | |||
1166 | ignore_cksum = self->param[MSCABD_PARAM_FIXMSZIP] && | ||
1167 | ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP); | ||
1168 | |||
1169 | todo = bytes; | ||
1170 | while (todo > 0) { | ||
1171 | avail = self->d->i_end - self->d->i_ptr; | ||
1172 | |||
1173 | /* if out of input data, read a new block */ | ||
1174 | if (avail) { | ||
1175 | /* copy as many input bytes available as possible */ | ||
1176 | if (avail > todo) avail = todo; | ||
1177 | sys->copy(self->d->i_ptr, buf, (size_t) avail); | ||
1178 | self->d->i_ptr += avail; | ||
1179 | buf += avail; | ||
1180 | todo -= avail; | ||
1181 | } | ||
1182 | else { | ||
1183 | /* out of data, read a new block */ | ||
1184 | |||
1185 | /* check if we're out of input blocks, advance block counter */ | ||
1186 | if (self->d->block++ >= self->d->folder->base.num_blocks) { | ||
1187 | self->read_error = MSPACK_ERR_DATAFORMAT; | ||
1188 | break; | ||
1189 | } | ||
1190 | |||
1191 | /* read a block */ | ||
1192 | self->read_error = cabd_sys_read_block(sys, self->d, &outlen, ignore_cksum); | ||
1193 | if (self->read_error) return -1; | ||
1194 | |||
1195 | /* special Quantum hack -- trailer byte to allow the decompressor | ||
1196 | * to realign itself. CAB Quantum blocks, unlike LZX blocks, can have | ||
1197 | * anything from 0 to 4 trailing null bytes. */ | ||
1198 | if ((self->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) { | ||
1199 | *self->d->i_end++ = 0xFF; | ||
1200 | } | ||
1201 | |||
1202 | /* is this the last block? */ | ||
1203 | if (self->d->block >= self->d->folder->base.num_blocks) { | ||
1204 | /* last block */ | ||
1205 | if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) { | ||
1206 | /* special LZX hack -- on the last block, inform LZX of the | ||
1207 | * size of the output data stream. */ | ||
1208 | lzxd_set_output_length((struct lzxd_stream *) self->d->state, (off_t) | ||
1209 | ((self->d->block-1) * CAB_BLOCKMAX + outlen)); | ||
1210 | } | ||
1211 | } | ||
1212 | else { | ||
1213 | /* not the last block */ | ||
1214 | if (outlen != CAB_BLOCKMAX) { | ||
1215 | self->system->message(self->d->infh, | ||
1216 | "WARNING; non-maximal data block"); | ||
1217 | } | ||
1218 | } | ||
1219 | } /* if (avail) */ | ||
1220 | } /* while (todo > 0) */ | ||
1221 | return bytes - todo; | ||
1222 | } | ||
1223 | |||
1224 | static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) { | ||
1225 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; | ||
1226 | self->d->offset += bytes; | ||
1227 | if (self->d->outfh) { | ||
1228 | return self->system->write(self->d->outfh, buffer, bytes); | ||
1229 | } | ||
1230 | return bytes; | ||
1231 | } | ||
1232 | |||
1233 | /*************************************** | ||
1234 | * CABD_SYS_READ_BLOCK | ||
1235 | *************************************** | ||
1236 | * reads a whole data block from a cab file. the block may span more than | ||
1237 | * one cab file, if it does then the fragments will be reassembled | ||
1238 | */ | ||
1239 | static int cabd_sys_read_block(struct mspack_system *sys, | ||
1240 | struct mscabd_decompress_state *d, | ||
1241 | int *out, int ignore_cksum) | ||
1242 | { | ||
1243 | unsigned char hdr[cfdata_SIZEOF]; | ||
1244 | unsigned int cksum; | ||
1245 | int len; | ||
1246 | |||
1247 | /* reset the input block pointer and end of block pointer */ | ||
1248 | d->i_ptr = d->i_end = &d->input[0]; | ||
1249 | |||
1250 | do { | ||
1251 | /* read the block header */ | ||
1252 | if (sys->read(d->infh, &hdr[0], cfdata_SIZEOF) != cfdata_SIZEOF) { | ||
1253 | return MSPACK_ERR_READ; | ||
1254 | } | ||
1255 | |||
1256 | /* skip any reserved block headers */ | ||
1257 | if (d->data->cab->block_resv && | ||
1258 | sys->seek(d->infh, (off_t) d->data->cab->block_resv, | ||
1259 | MSPACK_SYS_SEEK_CUR)) | ||
1260 | { | ||
1261 | return MSPACK_ERR_SEEK; | ||
1262 | } | ||
1263 | |||
1264 | /* blocks must not be over CAB_INPUTMAX in size */ | ||
1265 | len = EndGetI16(&hdr[cfdata_CompressedSize]); | ||
1266 | if (((d->i_end - d->i_ptr) + len) > CAB_INPUTMAX) { | ||
1267 | D(("block size > CAB_INPUTMAX (%ld + %d)", d->i_end - d->i_ptr, len)) | ||
1268 | return MSPACK_ERR_DATAFORMAT; | ||
1269 | } | ||
1270 | |||
1271 | /* blocks must not expand to more than CAB_BLOCKMAX */ | ||
1272 | if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) { | ||
1273 | D(("block size > CAB_BLOCKMAX")) | ||
1274 | return MSPACK_ERR_DATAFORMAT; | ||
1275 | } | ||
1276 | |||
1277 | /* read the block data */ | ||
1278 | if (sys->read(d->infh, d->i_end, len) != len) { | ||
1279 | return MSPACK_ERR_READ; | ||
1280 | } | ||
1281 | |||
1282 | /* perform checksum test on the block (if one is stored) */ | ||
1283 | if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) { | ||
1284 | unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0); | ||
1285 | if (cabd_checksum(&hdr[4], 4, sum2) != cksum) { | ||
1286 | if (!ignore_cksum) return MSPACK_ERR_CHECKSUM; | ||
1287 | sys->message(d->infh, "WARNING; bad block checksum found"); | ||
1288 | } | ||
1289 | } | ||
1290 | |||
1291 | /* advance end of block pointer to include newly read data */ | ||
1292 | d->i_end += len; | ||
1293 | |||
1294 | /* uncompressed size == 0 means this block was part of a split block | ||
1295 | * and it continues as the first block of the next cabinet in the set. | ||
1296 | * otherwise, this is the last part of the block, and no more block | ||
1297 | * reading needs to be done. | ||
1298 | */ | ||
1299 | /* EXIT POINT OF LOOP -- uncompressed size != 0 */ | ||
1300 | if ((*out = EndGetI16(&hdr[cfdata_UncompressedSize]))) { | ||
1301 | return MSPACK_ERR_OK; | ||
1302 | } | ||
1303 | |||
1304 | /* otherwise, advance to next cabinet */ | ||
1305 | |||
1306 | /* close current file handle */ | ||
1307 | sys->close(d->infh); | ||
1308 | d->infh = NULL; | ||
1309 | |||
1310 | /* advance to next member in the cabinet set */ | ||
1311 | if (!(d->data = d->data->next)) { | ||
1312 | D(("ran out of splits in cabinet set")) | ||
1313 | return MSPACK_ERR_DATAFORMAT; | ||
1314 | } | ||
1315 | |||
1316 | /* open next cab file */ | ||
1317 | d->incab = d->data->cab; | ||
1318 | if (!(d->infh = sys->open(sys, d->incab->base.filename, | ||
1319 | MSPACK_SYS_OPEN_READ))) | ||
1320 | { | ||
1321 | return MSPACK_ERR_OPEN; | ||
1322 | } | ||
1323 | |||
1324 | /* seek to start of data blocks */ | ||
1325 | if (sys->seek(d->infh, d->data->offset, MSPACK_SYS_SEEK_START)) { | ||
1326 | return MSPACK_ERR_SEEK; | ||
1327 | } | ||
1328 | } while (1); | ||
1329 | |||
1330 | /* not reached */ | ||
1331 | return MSPACK_ERR_OK; | ||
1332 | } | ||
1333 | |||
1334 | static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes, | ||
1335 | unsigned int cksum) | ||
1336 | { | ||
1337 | unsigned int len, ul = 0; | ||
1338 | |||
1339 | for (len = bytes >> 2; len--; data += 4) { | ||
1340 | cksum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24)); | ||
1341 | } | ||
1342 | |||
1343 | switch (bytes & 3) { | ||
1344 | case 3: ul |= *data++ << 16; | ||
1345 | case 2: ul |= *data++ << 8; | ||
1346 | case 1: ul |= *data; | ||
1347 | } | ||
1348 | cksum ^= ul; | ||
1349 | |||
1350 | return cksum; | ||
1351 | } | ||
1352 | |||
1353 | /*************************************** | ||
1354 | * NONED_INIT, NONED_DECOMPRESS, NONED_FREE | ||
1355 | *************************************** | ||
1356 | * the "not compressed" method decompressor | ||
1357 | */ | ||
1358 | struct noned_state { | ||
1359 | struct mspack_system *sys; | ||
1360 | struct mspack_file *i; | ||
1361 | struct mspack_file *o; | ||
1362 | unsigned char *buf; | ||
1363 | int bufsize; | ||
1364 | }; | ||
1365 | |||
1366 | static struct noned_state *noned_init(struct mspack_system *sys, | ||
1367 | struct mspack_file *in, | ||
1368 | struct mspack_file *out, | ||
1369 | int bufsize) | ||
1370 | { | ||
1371 | struct noned_state *state = (struct noned_state *) sys->alloc(sys, sizeof(struct noned_state)); | ||
1372 | unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) bufsize); | ||
1373 | if (state && buf) { | ||
1374 | state->sys = sys; | ||
1375 | state->i = in; | ||
1376 | state->o = out; | ||
1377 | state->buf = buf; | ||
1378 | state->bufsize = bufsize; | ||
1379 | } | ||
1380 | else { | ||
1381 | sys->free(buf); | ||
1382 | sys->free(state); | ||
1383 | state = NULL; | ||
1384 | } | ||
1385 | return state; | ||
1386 | } | ||
1387 | |||
1388 | static int noned_decompress(struct noned_state *s, off_t bytes) { | ||
1389 | int run; | ||
1390 | while (bytes > 0) { | ||
1391 | run = (bytes > s->bufsize) ? s->bufsize : (int) bytes; | ||
1392 | if (s->sys->read(s->i, &s->buf[0], run) != run) return MSPACK_ERR_READ; | ||
1393 | if (s->sys->write(s->o, &s->buf[0], run) != run) return MSPACK_ERR_WRITE; | ||
1394 | bytes -= run; | ||
1395 | } | ||
1396 | return MSPACK_ERR_OK; | ||
1397 | } | ||
1398 | |||
1399 | static void noned_free(struct noned_state *state) { | ||
1400 | struct mspack_system *sys; | ||
1401 | if (state) { | ||
1402 | sys = state->sys; | ||
1403 | sys->free(state->buf); | ||
1404 | sys->free(state); | ||
1405 | } | ||
1406 | } | ||
1407 | |||
1408 | |||
1409 | /*************************************** | ||
1410 | * CABD_PARAM | ||
1411 | *************************************** | ||
1412 | * allows a parameter to be set | ||
1413 | */ | ||
1414 | static int cabd_param(struct mscab_decompressor *base, int param, int value) { | ||
1415 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
1416 | if (!self) return MSPACK_ERR_ARGS; | ||
1417 | |||
1418 | switch (param) { | ||
1419 | case MSCABD_PARAM_SEARCHBUF: | ||
1420 | if (value < 4) return MSPACK_ERR_ARGS; | ||
1421 | self->param[MSCABD_PARAM_SEARCHBUF] = value; | ||
1422 | break; | ||
1423 | case MSCABD_PARAM_FIXMSZIP: | ||
1424 | self->param[MSCABD_PARAM_FIXMSZIP] = value; | ||
1425 | break; | ||
1426 | case MSCABD_PARAM_DECOMPBUF: | ||
1427 | if (value < 4) return MSPACK_ERR_ARGS; | ||
1428 | self->param[MSCABD_PARAM_DECOMPBUF] = value; | ||
1429 | break; | ||
1430 | default: | ||
1431 | return MSPACK_ERR_ARGS; | ||
1432 | } | ||
1433 | return MSPACK_ERR_OK; | ||
1434 | } | ||
1435 | |||
1436 | /*************************************** | ||
1437 | * CABD_ERROR | ||
1438 | *************************************** | ||
1439 | * returns the last error that occurred | ||
1440 | */ | ||
1441 | static int cabd_error(struct mscab_decompressor *base) { | ||
1442 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
1443 | return (self) ? self->error : MSPACK_ERR_ARGS; | ||
1444 | } | ||
diff --git a/rbutil/rbutilqt/mspack/chm.h b/rbutil/rbutilqt/mspack/chm.h new file mode 100644 index 0000000000..a85d2e1731 --- /dev/null +++ b/rbutil/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 | |||
96 | struct 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 */ | ||
105 | struct 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 | |||
115 | struct 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/rbutil/rbutilqt/mspack/chmc.c b/rbutil/rbutilqt/mspack/chmc.c new file mode 100644 index 0000000000..72f6c5b389 --- /dev/null +++ b/rbutil/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.h> | ||
13 | #include <chm.h> | ||
14 | |||
15 | struct mschm_compressor * | ||
16 | mspack_create_chm_compressor(struct mspack_system *sys) | ||
17 | { | ||
18 | /* todo */ | ||
19 | return NULL; | ||
20 | } | ||
21 | |||
22 | void mspack_destroy_chm_compressor(struct mschm_compressor *self) { | ||
23 | /* todo */ | ||
24 | } | ||
diff --git a/rbutil/rbutilqt/mspack/chmd.c b/rbutil/rbutilqt/mspack/chmd.c new file mode 100644 index 0000000000..6e4a1bf24f --- /dev/null +++ b/rbutil/rbutilqt/mspack/chmd.c | |||
@@ -0,0 +1,1346 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2011 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.h> | ||
13 | #include <chm.h> | ||
14 | |||
15 | /* prototypes */ | ||
16 | static struct mschmd_header * chmd_open( | ||
17 | struct mschm_decompressor *base, const char *filename); | ||
18 | static struct mschmd_header * chmd_fast_open( | ||
19 | struct mschm_decompressor *base, const char *filename); | ||
20 | static struct mschmd_header *chmd_real_open( | ||
21 | struct mschm_decompressor *base, const char *filename, int entire); | ||
22 | static void chmd_close( | ||
23 | struct mschm_decompressor *base, struct mschmd_header *chm); | ||
24 | static int chmd_read_headers( | ||
25 | struct mspack_system *sys, struct mspack_file *fh, | ||
26 | struct mschmd_header *chm, int entire); | ||
27 | static 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); | ||
30 | static unsigned char *read_chunk( | ||
31 | struct mschm_decompressor_p *self, struct mschmd_header *chm, | ||
32 | struct mspack_file *fh, unsigned int chunk); | ||
33 | static 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); | ||
36 | static inline int compare( | ||
37 | const char *s1, const char *s2, int l1, int l2); | ||
38 | static int chmd_extract( | ||
39 | struct mschm_decompressor *base, struct mschmd_file *file, | ||
40 | const char *filename); | ||
41 | static int chmd_sys_write( | ||
42 | struct mspack_file *file, void *buffer, int bytes); | ||
43 | static int chmd_init_decomp( | ||
44 | struct mschm_decompressor_p *self, struct mschmd_file *file); | ||
45 | static int read_reset_table( | ||
46 | struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec, | ||
47 | int entry, off_t *length_ptr, off_t *offset_ptr); | ||
48 | static int read_spaninfo( | ||
49 | struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec, | ||
50 | off_t *length_ptr); | ||
51 | static int find_sys_file( | ||
52 | struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec, | ||
53 | struct mschmd_file **f_ptr, const char *name); | ||
54 | static unsigned char *read_sys_file( | ||
55 | struct mschm_decompressor_p *self, struct mschmd_file *file); | ||
56 | static int chmd_error( | ||
57 | struct mschm_decompressor *base); | ||
58 | static 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 | */ | ||
66 | static const char *content_name = "::DataSpace/Storage/MSCompressed/Content"; | ||
67 | static const char *control_name = "::DataSpace/Storage/MSCompressed/ControlData"; | ||
68 | static const char *spaninfo_name = "::DataSpace/Storage/MSCompressed/SpanInfo"; | ||
69 | static 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 | */ | ||
77 | struct 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 | */ | ||
104 | void 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 | */ | ||
123 | static 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 | */ | ||
135 | static 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 | */ | ||
148 | static 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 | */ | ||
194 | static 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 */ | ||
243 | static 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 | |||
262 | static 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 (mspack_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 | /* ensure chunk size is large enough for signature and num_entries */ | ||
360 | if (chm->chunk_size < (pmgl_Entries + 2)) { | ||
361 | return MSPACK_ERR_DATAFORMAT; | ||
362 | } | ||
363 | |||
364 | /* if we are doing a quick read, stop here! */ | ||
365 | if (!entire) { | ||
366 | return MSPACK_ERR_OK; | ||
367 | } | ||
368 | |||
369 | /* seek to the first PMGL chunk, and reduce the number of chunks to read */ | ||
370 | if ((x = chm->first_pmgl) != 0) { | ||
371 | if (sys->seek(fh,(off_t) (x * chm->chunk_size), MSPACK_SYS_SEEK_CUR)) { | ||
372 | return MSPACK_ERR_SEEK; | ||
373 | } | ||
374 | } | ||
375 | num_chunks = chm->last_pmgl - x + 1; | ||
376 | |||
377 | if (!(chunk = (unsigned char *) sys->alloc(sys, (size_t)chm->chunk_size))) { | ||
378 | return MSPACK_ERR_NOMEMORY; | ||
379 | } | ||
380 | |||
381 | /* read and process all chunks from FirstPMGL to LastPMGL */ | ||
382 | errors = 0; | ||
383 | while (num_chunks--) { | ||
384 | /* read next chunk */ | ||
385 | if (sys->read(fh, chunk, (int)chm->chunk_size) != (int)chm->chunk_size) { | ||
386 | sys->free(chunk); | ||
387 | return MSPACK_ERR_READ; | ||
388 | } | ||
389 | |||
390 | /* process only directory (PMGL) chunks */ | ||
391 | if (EndGetI32(&chunk[pmgl_Signature]) != 0x4C474D50) continue; | ||
392 | |||
393 | if (EndGetI32(&chunk[pmgl_QuickRefSize]) < 2) { | ||
394 | sys->message(fh, "WARNING; PMGL quickref area is too small"); | ||
395 | } | ||
396 | if (EndGetI32(&chunk[pmgl_QuickRefSize]) > | ||
397 | ((int)chm->chunk_size - pmgl_Entries)) | ||
398 | { | ||
399 | sys->message(fh, "WARNING; PMGL quickref area is too large"); | ||
400 | } | ||
401 | |||
402 | p = &chunk[pmgl_Entries]; | ||
403 | end = &chunk[chm->chunk_size - 2]; | ||
404 | num_entries = EndGetI16(end); | ||
405 | |||
406 | while (num_entries--) { | ||
407 | READ_ENCINT(name_len); name = p; p += name_len; | ||
408 | READ_ENCINT(section); | ||
409 | READ_ENCINT(offset); | ||
410 | READ_ENCINT(length); | ||
411 | |||
412 | /* empty files and directory names are stored as a file entry at | ||
413 | * offset 0 with length 0. We want to keep empty files, but not | ||
414 | * directory names, which end with a "/" */ | ||
415 | if ((offset == 0) && (length == 0)) { | ||
416 | if ((name_len > 0) && (name[name_len-1] == '/')) continue; | ||
417 | } | ||
418 | |||
419 | if (section > 1) { | ||
420 | sys->message(fh, "invalid section number '%u'.", section); | ||
421 | continue; | ||
422 | } | ||
423 | |||
424 | if (!(fi = (struct mschmd_file *) sys->alloc(sys, sizeof(struct mschmd_file) + name_len + 1))) { | ||
425 | sys->free(chunk); | ||
426 | return MSPACK_ERR_NOMEMORY; | ||
427 | } | ||
428 | |||
429 | fi->next = NULL; | ||
430 | fi->filename = (char *) &fi[1]; | ||
431 | fi->section = ((section == 0) ? (struct mschmd_section *) (&chm->sec0) | ||
432 | : (struct mschmd_section *) (&chm->sec1)); | ||
433 | fi->offset = offset; | ||
434 | fi->length = length; | ||
435 | sys->copy(name, fi->filename, (size_t) name_len); | ||
436 | fi->filename[name_len] = '\0'; | ||
437 | |||
438 | if (name[0] == ':' && name[1] == ':') { | ||
439 | /* system file */ | ||
440 | if (mspack_memcmp(&name[2], &content_name[2], 31L) == 0) { | ||
441 | if (mspack_memcmp(&name[33], &content_name[33], 8L) == 0) { | ||
442 | chm->sec1.content = fi; | ||
443 | } | ||
444 | else if (mspack_memcmp(&name[33], &control_name[33], 11L) == 0) { | ||
445 | chm->sec1.control = fi; | ||
446 | } | ||
447 | else if (mspack_memcmp(&name[33], &spaninfo_name[33], 8L) == 0) { | ||
448 | chm->sec1.spaninfo = fi; | ||
449 | } | ||
450 | else if (mspack_memcmp(&name[33], &rtable_name[33], 72L) == 0) { | ||
451 | chm->sec1.rtable = fi; | ||
452 | } | ||
453 | } | ||
454 | fi->next = chm->sysfiles; | ||
455 | chm->sysfiles = fi; | ||
456 | } | ||
457 | else { | ||
458 | /* normal file */ | ||
459 | if (link) link->next = fi; else chm->files = fi; | ||
460 | link = fi; | ||
461 | } | ||
462 | } | ||
463 | |||
464 | /* this is reached either when num_entries runs out, or if | ||
465 | * reading data from the chunk reached a premature end of chunk */ | ||
466 | chunk_end: | ||
467 | if (num_entries >= 0) { | ||
468 | D(("chunk ended before all entries could be read")) | ||
469 | errors++; | ||
470 | } | ||
471 | |||
472 | } | ||
473 | sys->free(chunk); | ||
474 | return (errors > 0) ? MSPACK_ERR_DATAFORMAT : MSPACK_ERR_OK; | ||
475 | } | ||
476 | |||
477 | /*************************************** | ||
478 | * CHMD_FAST_FIND | ||
479 | *************************************** | ||
480 | * uses PMGI index chunks and quickref data to quickly locate a file | ||
481 | * directly from the on-disk index. | ||
482 | * | ||
483 | * TODO: protect against infinite loops in chunks (where pgml_NextChunk | ||
484 | * or a PGMI index entry point to an already visited chunk) | ||
485 | */ | ||
486 | static int chmd_fast_find(struct mschm_decompressor *base, | ||
487 | struct mschmd_header *chm, const char *filename, | ||
488 | struct mschmd_file *f_ptr, int f_size) | ||
489 | { | ||
490 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; | ||
491 | struct mspack_system *sys; | ||
492 | struct mspack_file *fh; | ||
493 | const unsigned char *chunk, *p, *end; | ||
494 | int err = MSPACK_ERR_OK, result = -1; | ||
495 | unsigned int n, sec; | ||
496 | |||
497 | if (!self || !chm || !f_ptr || (f_size != sizeof(struct mschmd_file))) { | ||
498 | return MSPACK_ERR_ARGS; | ||
499 | } | ||
500 | sys = self->system; | ||
501 | |||
502 | /* clear the results structure */ | ||
503 | memset(f_ptr, 0, f_size); | ||
504 | |||
505 | if (!(fh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ))) { | ||
506 | return MSPACK_ERR_OPEN; | ||
507 | } | ||
508 | |||
509 | /* go through PMGI chunk hierarchy to reach PMGL chunk */ | ||
510 | if (chm->index_root < chm->num_chunks) { | ||
511 | n = chm->index_root; | ||
512 | for (;;) { | ||
513 | if (!(chunk = read_chunk(self, chm, fh, n))) { | ||
514 | sys->close(fh); | ||
515 | return self->error; | ||
516 | } | ||
517 | |||
518 | /* search PMGI/PMGL chunk. exit early if no entry found */ | ||
519 | if ((result = search_chunk(chm, chunk, filename, &p, &end)) <= 0) { | ||
520 | break; | ||
521 | } | ||
522 | |||
523 | /* found result. loop around for next chunk if this is PMGI */ | ||
524 | if (chunk[3] == 0x4C) break; else READ_ENCINT(n); | ||
525 | } | ||
526 | } | ||
527 | else { | ||
528 | /* PMGL chunks only, search from first_pmgl to last_pmgl */ | ||
529 | for (n = chm->first_pmgl; n <= chm->last_pmgl; | ||
530 | n = EndGetI32(&chunk[pmgl_NextChunk])) | ||
531 | { | ||
532 | if (!(chunk = read_chunk(self, chm, fh, n))) { | ||
533 | err = self->error; | ||
534 | break; | ||
535 | } | ||
536 | |||
537 | /* search PMGL chunk. exit if file found */ | ||
538 | if ((result = search_chunk(chm, chunk, filename, &p, &end)) > 0) { | ||
539 | break; | ||
540 | } | ||
541 | } | ||
542 | } | ||
543 | |||
544 | /* if we found a file, read it */ | ||
545 | if (result > 0) { | ||
546 | READ_ENCINT(sec); | ||
547 | f_ptr->section = (sec == 0) ? (struct mschmd_section *) &chm->sec0 | ||
548 | : (struct mschmd_section *) &chm->sec1; | ||
549 | READ_ENCINT(f_ptr->offset); | ||
550 | READ_ENCINT(f_ptr->length); | ||
551 | } | ||
552 | else if (result < 0) { | ||
553 | err = MSPACK_ERR_DATAFORMAT; | ||
554 | } | ||
555 | |||
556 | sys->close(fh); | ||
557 | return self->error = err; | ||
558 | |||
559 | chunk_end: | ||
560 | D(("read beyond end of chunk entries")) | ||
561 | sys->close(fh); | ||
562 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
563 | } | ||
564 | |||
565 | /* reads the given chunk into memory, storing it in a chunk cache | ||
566 | * so it doesn't need to be read from disk more than once | ||
567 | */ | ||
568 | static unsigned char *read_chunk(struct mschm_decompressor_p *self, | ||
569 | struct mschmd_header *chm, | ||
570 | struct mspack_file *fh, | ||
571 | unsigned int chunk_num) | ||
572 | { | ||
573 | struct mspack_system *sys = self->system; | ||
574 | unsigned char *buf; | ||
575 | |||
576 | /* check arguments - most are already checked by chmd_fast_find */ | ||
577 | if (chunk_num > chm->num_chunks) return NULL; | ||
578 | |||
579 | /* ensure chunk cache is available */ | ||
580 | if (!chm->chunk_cache) { | ||
581 | size_t size = sizeof(unsigned char *) * chm->num_chunks; | ||
582 | if (!(chm->chunk_cache = (unsigned char **) sys->alloc(sys, size))) { | ||
583 | self->error = MSPACK_ERR_NOMEMORY; | ||
584 | return NULL; | ||
585 | } | ||
586 | memset(chm->chunk_cache, 0, size); | ||
587 | } | ||
588 | |||
589 | /* try to answer out of chunk cache */ | ||
590 | if (chm->chunk_cache[chunk_num]) return chm->chunk_cache[chunk_num]; | ||
591 | |||
592 | /* need to read chunk - allocate memory for it */ | ||
593 | if (!(buf = (unsigned char *) sys->alloc(sys, chm->chunk_size))) { | ||
594 | self->error = MSPACK_ERR_NOMEMORY; | ||
595 | return NULL; | ||
596 | } | ||
597 | |||
598 | /* seek to block and read it */ | ||
599 | if (sys->seek(fh, (off_t) (chm->dir_offset + (chunk_num * chm->chunk_size)), | ||
600 | MSPACK_SYS_SEEK_START)) | ||
601 | { | ||
602 | self->error = MSPACK_ERR_SEEK; | ||
603 | sys->free(buf); | ||
604 | return NULL; | ||
605 | } | ||
606 | if (sys->read(fh, buf, (int)chm->chunk_size) != (int)chm->chunk_size) { | ||
607 | self->error = MSPACK_ERR_READ; | ||
608 | sys->free(buf); | ||
609 | return NULL; | ||
610 | } | ||
611 | |||
612 | /* check the signature. Is is PMGL or PMGI? */ | ||
613 | if (!((buf[0] == 0x50) && (buf[1] == 0x4D) && (buf[2] == 0x47) && | ||
614 | ((buf[3] == 0x4C) || (buf[3] == 0x49)))) | ||
615 | { | ||
616 | self->error = MSPACK_ERR_SEEK; | ||
617 | sys->free(buf); | ||
618 | return NULL; | ||
619 | } | ||
620 | |||
621 | /* all OK. Store chunk in cache and return it */ | ||
622 | return chm->chunk_cache[chunk_num] = buf; | ||
623 | } | ||
624 | |||
625 | /* searches a PMGI/PMGL chunk for a given filename entry. Returns -1 on | ||
626 | * data format error, 0 if entry definitely not found, 1 if entry | ||
627 | * found. In the latter case, *result and *result_end are set pointing | ||
628 | * to that entry's data (either the "next chunk" ENCINT for a PMGI or | ||
629 | * the section, offset and length ENCINTs for a PMGL). | ||
630 | * | ||
631 | * In the case of PMGL chunks, the entry has definitely been | ||
632 | * found. In the case of PMGI chunks, the entry which points to the | ||
633 | * chunk that may eventually contain that entry has been found. | ||
634 | */ | ||
635 | static int search_chunk(struct mschmd_header *chm, | ||
636 | const unsigned char *chunk, | ||
637 | const char *filename, | ||
638 | const unsigned char **result, | ||
639 | const unsigned char **result_end) | ||
640 | { | ||
641 | const unsigned char *start, *end, *p; | ||
642 | unsigned int qr_size, num_entries, qr_entries, qr_density, name_len; | ||
643 | unsigned int L, R, M, sec, fname_len, entries_off, is_pmgl; | ||
644 | int cmp; | ||
645 | |||
646 | fname_len = strlen(filename); | ||
647 | |||
648 | /* PMGL chunk or PMGI chunk? (note: read_chunk() has already | ||
649 | * checked the rest of the characters in the chunk signature) */ | ||
650 | if (chunk[3] == 0x4C) { | ||
651 | is_pmgl = 1; | ||
652 | entries_off = pmgl_Entries; | ||
653 | } | ||
654 | else { | ||
655 | is_pmgl = 0; | ||
656 | entries_off = pmgi_Entries; | ||
657 | } | ||
658 | |||
659 | /* Step 1: binary search first filename of each QR entry | ||
660 | * - target filename == entry | ||
661 | * found file | ||
662 | * - target filename < all entries | ||
663 | * file not found | ||
664 | * - target filename > all entries | ||
665 | * proceed to step 2 using final entry | ||
666 | * - target filename between two searched entries | ||
667 | * proceed to step 2 | ||
668 | */ | ||
669 | qr_size = EndGetI32(&chunk[pmgl_QuickRefSize]); | ||
670 | start = &chunk[chm->chunk_size - 2]; | ||
671 | end = &chunk[chm->chunk_size - qr_size]; | ||
672 | num_entries = EndGetI16(start); | ||
673 | qr_density = 1 + (1 << chm->density); | ||
674 | qr_entries = (num_entries + qr_density-1) / qr_density; | ||
675 | |||
676 | if (num_entries == 0) { | ||
677 | D(("chunk has no entries")) | ||
678 | return -1; | ||
679 | } | ||
680 | |||
681 | if (qr_size > chm->chunk_size) { | ||
682 | D(("quickref size > chunk size")) | ||
683 | return -1; | ||
684 | } | ||
685 | |||
686 | *result_end = end; | ||
687 | |||
688 | if (((int)qr_entries * 2) > (start - end)) { | ||
689 | D(("WARNING; more quickrefs than quickref space")) | ||
690 | qr_entries = 0; /* but we can live with it */ | ||
691 | } | ||
692 | |||
693 | if (qr_entries > 0) { | ||
694 | L = 0; | ||
695 | R = qr_entries - 1; | ||
696 | do { | ||
697 | /* pick new midpoint */ | ||
698 | M = (L + R) >> 1; | ||
699 | |||
700 | /* compare filename with entry QR points to */ | ||
701 | p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; | ||
702 | READ_ENCINT(name_len); | ||
703 | if (p + name_len > end) goto chunk_end; | ||
704 | cmp = compare(filename, (char *)p, fname_len, name_len); | ||
705 | |||
706 | if (cmp == 0) break; | ||
707 | else if (cmp < 0) { if (M) R = M - 1; else return 0; } | ||
708 | else if (cmp > 0) L = M + 1; | ||
709 | } while (L <= R); | ||
710 | M = (L + R) >> 1; | ||
711 | |||
712 | if (cmp == 0) { | ||
713 | /* exact match! */ | ||
714 | p += name_len; | ||
715 | *result = p; | ||
716 | return 1; | ||
717 | } | ||
718 | |||
719 | /* otherwise, read the group of entries for QR entry M */ | ||
720 | p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; | ||
721 | num_entries -= (M * qr_density); | ||
722 | if (num_entries > qr_density) num_entries = qr_density; | ||
723 | } | ||
724 | else { | ||
725 | p = &chunk[entries_off]; | ||
726 | } | ||
727 | |||
728 | /* Step 2: linear search through the set of entries reached in step 1. | ||
729 | * - filename == any entry | ||
730 | * found entry | ||
731 | * - filename < all entries (PMGI) or any entry (PMGL) | ||
732 | * entry not found, stop now | ||
733 | * - filename > all entries | ||
734 | * entry not found (PMGL) / maybe found (PMGI) | ||
735 | * - | ||
736 | */ | ||
737 | *result = NULL; | ||
738 | while (num_entries-- > 0) { | ||
739 | READ_ENCINT(name_len); | ||
740 | if (p + name_len > end) goto chunk_end; | ||
741 | cmp = compare(filename, (char *)p, fname_len, name_len); | ||
742 | p += name_len; | ||
743 | |||
744 | if (cmp == 0) { | ||
745 | /* entry found */ | ||
746 | *result = p; | ||
747 | return 1; | ||
748 | } | ||
749 | |||
750 | if (cmp < 0) { | ||
751 | /* entry not found (PMGL) / maybe found (PMGI) */ | ||
752 | break; | ||
753 | } | ||
754 | |||
755 | /* read and ignore the rest of this entry */ | ||
756 | if (is_pmgl) { | ||
757 | READ_ENCINT(R); /* skip section */ | ||
758 | READ_ENCINT(R); /* skip offset */ | ||
759 | READ_ENCINT(R); /* skip length */ | ||
760 | } | ||
761 | else { | ||
762 | *result = p; /* store potential final result */ | ||
763 | READ_ENCINT(R); /* skip chunk number */ | ||
764 | } | ||
765 | } | ||
766 | |||
767 | /* PMGL? not found. PMGI? maybe found */ | ||
768 | return (is_pmgl) ? 0 : (*result ? 1 : 0); | ||
769 | |||
770 | chunk_end: | ||
771 | D(("reached end of chunk data while searching")) | ||
772 | return -1; | ||
773 | } | ||
774 | |||
775 | #if HAVE_TOWLOWER | ||
776 | # if HAVE_WCTYPE_H | ||
777 | # include <wctype.h> | ||
778 | # endif | ||
779 | # define TOLOWER(x) towlower(x) | ||
780 | #elif HAVE_TOLOWER | ||
781 | # if HAVE_CTYPE_H | ||
782 | # include <ctype.h> | ||
783 | # endif | ||
784 | # define TOLOWER(x) tolower(x) | ||
785 | #else | ||
786 | # define TOLOWER(x) (((x)<0||(x)>256)?(x):mspack_tolower_map[(x)]) | ||
787 | /* Map of char -> lowercase char for the first 256 chars. Generated with: | ||
788 | * LC_CTYPE=en_GB.utf-8 perl -Mlocale -le 'print map{ord(lc chr).","} 0..255' | ||
789 | */ | ||
790 | static const unsigned char mspack_tolower_map[256] = { | ||
791 | 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27, | ||
792 | 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52, | ||
793 | 53,54,55,56,57,58,59,60,61,62,63,64,97,98,99,100,101,102,103,104,105,106, | ||
794 | 107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,92,93,94, | ||
795 | 95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114, | ||
796 | 115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133, | ||
797 | 134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152, | ||
798 | 153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171, | ||
799 | 172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190, | ||
800 | 191,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241, | ||
801 | 242,243,244,245,246,215,248,249,250,251,252,253,254,223,224,225,226,227,228, | ||
802 | 229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, | ||
803 | 248,249,250,251,252,253,254,255 | ||
804 | }; | ||
805 | #endif | ||
806 | |||
807 | /* decodes a UTF-8 character from s[] into c. Will not read past e. */ | ||
808 | #define GET_UTF8_CHAR(s, e, c) do { \ | ||
809 | unsigned char x = *s++; \ | ||
810 | if (x < 0x80) c = x; \ | ||
811 | else if (x < 0xC0) c = -1; \ | ||
812 | else if (x < 0xE0) { \ | ||
813 | c = (s >= e) ? -1 : ((x & 0x1F) << 6) | (*s++ & 0x3F); \ | ||
814 | } \ | ||
815 | else if (x < 0xF0) { \ | ||
816 | c = (s+2 > e) ? -1 : ((x & 0x0F) << 12) | ((s[0] & 0x3F) << 6) \ | ||
817 | | (s[1] & 0x3F); \ | ||
818 | s += 2; \ | ||
819 | } \ | ||
820 | else if (x < 0xF8) { \ | ||
821 | c = (s+3 > e) ? -1 : ((x & 0x07) << 18) | ((s[0] & 0x3F) << 12) \ | ||
822 | | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F); \ | ||
823 | s += 3; \ | ||
824 | } \ | ||
825 | else if (x < 0xFC) { \ | ||
826 | c = (s+4 > e) ? -1 : ((x & 0x03) << 24) | ((s[0] & 0x3F) << 18) \ | ||
827 | | ((s[1] & 0x3F) << 12)|((s[2] & 0x3F) << 6)|(s[3] & 0x3F); \ | ||
828 | s += 4; \ | ||
829 | } \ | ||
830 | else if (x < 0xFE) { \ | ||
831 | c = (s+5>e)?-1:((x&1)<<30)|((s[0]&0x3F)<<24)|((s[1]&0x3F)<<18)| \ | ||
832 | ((s[2] & 0x3F) << 12) | ((s[3] & 0x3F) << 6)|(s[4] & 0x3F); \ | ||
833 | s += 5; \ | ||
834 | } \ | ||
835 | else c = -1; \ | ||
836 | } while (0) | ||
837 | |||
838 | /* case-insensitively compares two UTF8 encoded strings. String length for | ||
839 | * both strings must be provided, null bytes are not terminators */ | ||
840 | static inline int compare(const char *s1, const char *s2, int l1, int l2) { | ||
841 | register const unsigned char *p1 = (const unsigned char *) s1; | ||
842 | register const unsigned char *p2 = (const unsigned char *) s2; | ||
843 | register const unsigned char *e1 = p1 + l1, *e2 = p2 + l2; | ||
844 | int c1, c2; | ||
845 | |||
846 | while (p1 < e1 && p2 < e2) { | ||
847 | GET_UTF8_CHAR(p1, e1, c1); | ||
848 | GET_UTF8_CHAR(p2, e2, c2); | ||
849 | if (c1 == c2) continue; | ||
850 | c1 = TOLOWER(c1); | ||
851 | c2 = TOLOWER(c2); | ||
852 | if (c1 != c2) return c1 - c2; | ||
853 | } | ||
854 | return l1 - l2; | ||
855 | } | ||
856 | |||
857 | |||
858 | /*************************************** | ||
859 | * CHMD_EXTRACT | ||
860 | *************************************** | ||
861 | * extracts a file from a CHM helpfile | ||
862 | */ | ||
863 | static int chmd_extract(struct mschm_decompressor *base, | ||
864 | struct mschmd_file *file, const char *filename) | ||
865 | { | ||
866 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; | ||
867 | struct mspack_system *sys; | ||
868 | struct mschmd_header *chm; | ||
869 | struct mspack_file *fh; | ||
870 | off_t bytes; | ||
871 | |||
872 | if (!self) return MSPACK_ERR_ARGS; | ||
873 | if (!file || !file->section) return self->error = MSPACK_ERR_ARGS; | ||
874 | sys = self->system; | ||
875 | chm = file->section->chm; | ||
876 | |||
877 | /* create decompression state if it doesn't exist */ | ||
878 | if (!self->d) { | ||
879 | self->d = (struct mschmd_decompress_state *) sys->alloc(sys, sizeof(struct mschmd_decompress_state)); | ||
880 | if (!self->d) return self->error = MSPACK_ERR_NOMEMORY; | ||
881 | self->d->chm = chm; | ||
882 | self->d->offset = 0; | ||
883 | self->d->state = NULL; | ||
884 | self->d->sys = *sys; | ||
885 | self->d->sys.write = &chmd_sys_write; | ||
886 | self->d->infh = NULL; | ||
887 | self->d->outfh = NULL; | ||
888 | } | ||
889 | |||
890 | /* open input chm file if not open, or the open one is a different chm */ | ||
891 | if (!self->d->infh || (self->d->chm != chm)) { | ||
892 | if (self->d->infh) sys->close(self->d->infh); | ||
893 | if (self->d->state) lzxd_free(self->d->state); | ||
894 | self->d->chm = chm; | ||
895 | self->d->offset = 0; | ||
896 | self->d->state = NULL; | ||
897 | self->d->infh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ); | ||
898 | if (!self->d->infh) return self->error = MSPACK_ERR_OPEN; | ||
899 | } | ||
900 | |||
901 | /* open file for output */ | ||
902 | if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { | ||
903 | return self->error = MSPACK_ERR_OPEN; | ||
904 | } | ||
905 | |||
906 | /* if file is empty, simply creating it is enough */ | ||
907 | if (!file->length) { | ||
908 | sys->close(fh); | ||
909 | return self->error = MSPACK_ERR_OK; | ||
910 | } | ||
911 | |||
912 | self->error = MSPACK_ERR_OK; | ||
913 | |||
914 | switch (file->section->id) { | ||
915 | case 0: /* Uncompressed section file */ | ||
916 | /* simple seek + copy */ | ||
917 | if (sys->seek(self->d->infh, file->section->chm->sec0.offset | ||
918 | + file->offset, MSPACK_SYS_SEEK_START)) | ||
919 | { | ||
920 | self->error = MSPACK_ERR_SEEK; | ||
921 | } | ||
922 | else { | ||
923 | unsigned char buf[512]; | ||
924 | off_t length = file->length; | ||
925 | while (length > 0) { | ||
926 | int run = sizeof(buf); | ||
927 | if ((off_t)run > length) run = (int)length; | ||
928 | if (sys->read(self->d->infh, &buf[0], run) != run) { | ||
929 | self->error = MSPACK_ERR_READ; | ||
930 | break; | ||
931 | } | ||
932 | if (sys->write(fh, &buf[0], run) != run) { | ||
933 | self->error = MSPACK_ERR_WRITE; | ||
934 | break; | ||
935 | } | ||
936 | length -= run; | ||
937 | } | ||
938 | } | ||
939 | break; | ||
940 | |||
941 | case 1: /* MSCompressed section file */ | ||
942 | /* (re)initialise compression state if we it is not yet initialised, | ||
943 | * or we have advanced too far and have to backtrack | ||
944 | */ | ||
945 | if (!self->d->state || (file->offset < self->d->offset)) { | ||
946 | if (self->d->state) { | ||
947 | lzxd_free(self->d->state); | ||
948 | self->d->state = NULL; | ||
949 | } | ||
950 | if (chmd_init_decomp(self, file)) break; | ||
951 | } | ||
952 | |||
953 | /* seek to input data */ | ||
954 | if (sys->seek(self->d->infh, self->d->inoffset, MSPACK_SYS_SEEK_START)) { | ||
955 | self->error = MSPACK_ERR_SEEK; | ||
956 | break; | ||
957 | } | ||
958 | |||
959 | /* get to correct offset. */ | ||
960 | self->d->outfh = NULL; | ||
961 | if ((bytes = file->offset - self->d->offset)) { | ||
962 | self->error = lzxd_decompress(self->d->state, bytes); | ||
963 | } | ||
964 | |||
965 | /* if getting to the correct offset was error free, unpack file */ | ||
966 | if (!self->error) { | ||
967 | self->d->outfh = fh; | ||
968 | self->error = lzxd_decompress(self->d->state, file->length); | ||
969 | } | ||
970 | |||
971 | /* save offset in input source stream, in case there is a section 0 | ||
972 | * file between now and the next section 1 file extracted */ | ||
973 | self->d->inoffset = sys->tell(self->d->infh); | ||
974 | |||
975 | /* if an LZX error occured, the LZX decompressor is now useless */ | ||
976 | if (self->error) { | ||
977 | if (self->d->state) lzxd_free(self->d->state); | ||
978 | self->d->state = NULL; | ||
979 | } | ||
980 | break; | ||
981 | } | ||
982 | |||
983 | sys->close(fh); | ||
984 | return self->error; | ||
985 | } | ||
986 | |||
987 | /*************************************** | ||
988 | * CHMD_SYS_WRITE | ||
989 | *************************************** | ||
990 | * chmd_sys_write is the internal writer function which the decompressor | ||
991 | * uses. If either writes data to disk (self->d->outfh) with the real | ||
992 | * sys->write() function, or does nothing with the data when | ||
993 | * self->d->outfh == NULL. advances self->d->offset. | ||
994 | */ | ||
995 | static int chmd_sys_write(struct mspack_file *file, void *buffer, int bytes) { | ||
996 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) file; | ||
997 | self->d->offset += bytes; | ||
998 | if (self->d->outfh) { | ||
999 | return self->system->write(self->d->outfh, buffer, bytes); | ||
1000 | } | ||
1001 | return bytes; | ||
1002 | } | ||
1003 | |||
1004 | /*************************************** | ||
1005 | * CHMD_INIT_DECOMP | ||
1006 | *************************************** | ||
1007 | * Initialises the LZX decompressor to decompress the compressed stream, | ||
1008 | * from the nearest reset offset and length that is needed for the given | ||
1009 | * file. | ||
1010 | */ | ||
1011 | static int chmd_init_decomp(struct mschm_decompressor_p *self, | ||
1012 | struct mschmd_file *file) | ||
1013 | { | ||
1014 | int window_size, window_bits, reset_interval, entry, err; | ||
1015 | struct mspack_system *sys = self->system; | ||
1016 | struct mschmd_sec_mscompressed *sec; | ||
1017 | unsigned char *data; | ||
1018 | off_t length, offset; | ||
1019 | |||
1020 | sec = (struct mschmd_sec_mscompressed *) file->section; | ||
1021 | |||
1022 | /* ensure we have a mscompressed content section */ | ||
1023 | err = find_sys_file(self, sec, &sec->content, content_name); | ||
1024 | if (err) return self->error = err; | ||
1025 | |||
1026 | /* ensure we have a ControlData file */ | ||
1027 | err = find_sys_file(self, sec, &sec->control, control_name); | ||
1028 | if (err) return self->error = err; | ||
1029 | |||
1030 | /* read ControlData */ | ||
1031 | if (sec->control->length < lzxcd_SIZEOF) { | ||
1032 | D(("ControlData file is too short")) | ||
1033 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1034 | } | ||
1035 | if (!(data = read_sys_file(self, sec->control))) { | ||
1036 | D(("can't read mscompressed control data file")) | ||
1037 | return self->error; | ||
1038 | } | ||
1039 | |||
1040 | /* check LZXC signature */ | ||
1041 | if (EndGetI32(&data[lzxcd_Signature]) != 0x43585A4C) { | ||
1042 | sys->free(data); | ||
1043 | return self->error = MSPACK_ERR_SIGNATURE; | ||
1044 | } | ||
1045 | |||
1046 | /* read reset_interval and window_size and validate version number */ | ||
1047 | switch (EndGetI32(&data[lzxcd_Version])) { | ||
1048 | case 1: | ||
1049 | reset_interval = EndGetI32(&data[lzxcd_ResetInterval]); | ||
1050 | window_size = EndGetI32(&data[lzxcd_WindowSize]); | ||
1051 | break; | ||
1052 | case 2: | ||
1053 | reset_interval = EndGetI32(&data[lzxcd_ResetInterval]) * LZX_FRAME_SIZE; | ||
1054 | window_size = EndGetI32(&data[lzxcd_WindowSize]) * LZX_FRAME_SIZE; | ||
1055 | break; | ||
1056 | default: | ||
1057 | D(("bad controldata version")) | ||
1058 | sys->free(data); | ||
1059 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1060 | } | ||
1061 | |||
1062 | /* free ControlData */ | ||
1063 | sys->free(data); | ||
1064 | |||
1065 | /* find window_bits from window_size */ | ||
1066 | switch (window_size) { | ||
1067 | case 0x008000: window_bits = 15; break; | ||
1068 | case 0x010000: window_bits = 16; break; | ||
1069 | case 0x020000: window_bits = 17; break; | ||
1070 | case 0x040000: window_bits = 18; break; | ||
1071 | case 0x080000: window_bits = 19; break; | ||
1072 | case 0x100000: window_bits = 20; break; | ||
1073 | case 0x200000: window_bits = 21; break; | ||
1074 | default: | ||
1075 | D(("bad controldata window size")) | ||
1076 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1077 | } | ||
1078 | |||
1079 | /* validate reset_interval */ | ||
1080 | if (reset_interval % LZX_FRAME_SIZE) { | ||
1081 | D(("bad controldata reset interval")) | ||
1082 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1083 | } | ||
1084 | |||
1085 | /* which reset table entry would we like? */ | ||
1086 | entry = file->offset / reset_interval; | ||
1087 | /* convert from reset interval multiple (usually 64k) to 32k frames */ | ||
1088 | entry *= reset_interval / LZX_FRAME_SIZE; | ||
1089 | |||
1090 | /* read the reset table entry */ | ||
1091 | if (read_reset_table(self, sec, entry, &length, &offset)) { | ||
1092 | /* the uncompressed length given in the reset table is dishonest. | ||
1093 | * the uncompressed data is always padded out from the given | ||
1094 | * uncompressed length up to the next reset interval */ | ||
1095 | length += reset_interval - 1; | ||
1096 | length &= -reset_interval; | ||
1097 | } | ||
1098 | else { | ||
1099 | /* if we can't read the reset table entry, just start from | ||
1100 | * the beginning. Use spaninfo to get the uncompressed length */ | ||
1101 | entry = 0; | ||
1102 | offset = 0; | ||
1103 | err = read_spaninfo(self, sec, &length); | ||
1104 | } | ||
1105 | if (err) return self->error = err; | ||
1106 | |||
1107 | /* get offset of compressed data stream: | ||
1108 | * = offset of uncompressed section from start of file | ||
1109 | * + offset of compressed stream from start of uncompressed section | ||
1110 | * + offset of chosen reset interval from start of compressed stream */ | ||
1111 | self->d->inoffset = file->section->chm->sec0.offset + sec->content->offset + offset; | ||
1112 | |||
1113 | /* set start offset and overall remaining stream length */ | ||
1114 | self->d->offset = entry * LZX_FRAME_SIZE; | ||
1115 | length -= self->d->offset; | ||
1116 | |||
1117 | /* initialise LZX stream */ | ||
1118 | self->d->state = lzxd_init(&self->d->sys, self->d->infh, | ||
1119 | (struct mspack_file *) self, window_bits, | ||
1120 | reset_interval / LZX_FRAME_SIZE, | ||
1121 | 4096, length); | ||
1122 | if (!self->d->state) self->error = MSPACK_ERR_NOMEMORY; | ||
1123 | return self->error; | ||
1124 | } | ||
1125 | |||
1126 | /*************************************** | ||
1127 | * READ_RESET_TABLE | ||
1128 | *************************************** | ||
1129 | * Reads one entry out of the reset table. Also reads the uncompressed | ||
1130 | * data length. Writes these to offset_ptr and length_ptr respectively. | ||
1131 | * Returns non-zero for success, zero for failure. | ||
1132 | */ | ||
1133 | static int read_reset_table(struct mschm_decompressor_p *self, | ||
1134 | struct mschmd_sec_mscompressed *sec, | ||
1135 | int entry, off_t *length_ptr, off_t *offset_ptr) | ||
1136 | { | ||
1137 | struct mspack_system *sys = self->system; | ||
1138 | unsigned char *data; | ||
1139 | int pos, entrysize; | ||
1140 | |||
1141 | /* do we have a ResetTable file? */ | ||
1142 | int err = find_sys_file(self, sec, &sec->rtable, rtable_name); | ||
1143 | if (err) return 0; | ||
1144 | |||
1145 | /* read ResetTable file */ | ||
1146 | if (sec->rtable->length < lzxrt_headerSIZEOF) { | ||
1147 | D(("ResetTable file is too short")) | ||
1148 | return 0; | ||
1149 | } | ||
1150 | if (!(data = read_sys_file(self, sec->rtable))) { | ||
1151 | D(("can't read reset table")) | ||
1152 | return 0; | ||
1153 | } | ||
1154 | |||
1155 | /* check sanity of reset table */ | ||
1156 | if (EndGetI32(&data[lzxrt_FrameLen]) != LZX_FRAME_SIZE) { | ||
1157 | D(("bad reset table frame length")) | ||
1158 | sys->free(data); | ||
1159 | return 0; | ||
1160 | } | ||
1161 | |||
1162 | /* get the uncompressed length of the LZX stream */ | ||
1163 | if (read_off64(length_ptr, data, sys, self->d->infh)) { | ||
1164 | sys->free(data); | ||
1165 | return 0; | ||
1166 | } | ||
1167 | |||
1168 | entrysize = EndGetI32(&data[lzxrt_EntrySize]); | ||
1169 | pos = EndGetI32(&data[lzxrt_TableOffset]) + (entry * entrysize); | ||
1170 | |||
1171 | /* ensure reset table entry for this offset exists */ | ||
1172 | if (entry < EndGetI32(&data[lzxrt_NumEntries]) && | ||
1173 | ((pos + entrysize) <= sec->rtable->length)) | ||
1174 | { | ||
1175 | switch (entrysize) { | ||
1176 | case 4: | ||
1177 | *offset_ptr = EndGetI32(&data[pos]); | ||
1178 | err = 0; | ||
1179 | break; | ||
1180 | case 8: | ||
1181 | err = read_off64(offset_ptr, &data[pos], sys, self->d->infh); | ||
1182 | break; | ||
1183 | default: | ||
1184 | D(("reset table entry size neither 4 nor 8")) | ||
1185 | err = 1; | ||
1186 | break; | ||
1187 | } | ||
1188 | } | ||
1189 | else { | ||
1190 | D(("bad reset interval")) | ||
1191 | err = 1; | ||
1192 | } | ||
1193 | |||
1194 | /* free the reset table */ | ||
1195 | sys->free(data); | ||
1196 | |||
1197 | /* return success */ | ||
1198 | return (err == 0); | ||
1199 | } | ||
1200 | |||
1201 | /*************************************** | ||
1202 | * READ_SPANINFO | ||
1203 | *************************************** | ||
1204 | * Reads the uncompressed data length from the spaninfo file. | ||
1205 | * Returns zero for success or a non-zero error code for failure. | ||
1206 | */ | ||
1207 | static int read_spaninfo(struct mschm_decompressor_p *self, | ||
1208 | struct mschmd_sec_mscompressed *sec, | ||
1209 | off_t *length_ptr) | ||
1210 | { | ||
1211 | struct mspack_system *sys = self->system; | ||
1212 | unsigned char *data; | ||
1213 | |||
1214 | /* find SpanInfo file */ | ||
1215 | int err = find_sys_file(self, sec, &sec->spaninfo, spaninfo_name); | ||
1216 | if (err) return MSPACK_ERR_DATAFORMAT; | ||
1217 | |||
1218 | /* check it's large enough */ | ||
1219 | if (sec->spaninfo->length != 8) { | ||
1220 | D(("SpanInfo file is wrong size")) | ||
1221 | return MSPACK_ERR_DATAFORMAT; | ||
1222 | } | ||
1223 | |||
1224 | /* read the SpanInfo file */ | ||
1225 | if (!(data = read_sys_file(self, sec->spaninfo))) { | ||
1226 | D(("can't read SpanInfo file")) | ||
1227 | return self->error; | ||
1228 | } | ||
1229 | |||
1230 | /* get the uncompressed length of the LZX stream */ | ||
1231 | err = read_off64(length_ptr, data, sys, self->d->infh); | ||
1232 | |||
1233 | sys->free(data); | ||
1234 | return (err) ? MSPACK_ERR_DATAFORMAT : MSPACK_ERR_OK; | ||
1235 | } | ||
1236 | |||
1237 | /*************************************** | ||
1238 | * FIND_SYS_FILE | ||
1239 | *************************************** | ||
1240 | * Uses chmd_fast_find to locate a system file, and fills out that system | ||
1241 | * file's entry and links it into the list of system files. Returns zero | ||
1242 | * for success, non-zero for both failure and the file not existing. | ||
1243 | */ | ||
1244 | static int find_sys_file(struct mschm_decompressor_p *self, | ||
1245 | struct mschmd_sec_mscompressed *sec, | ||
1246 | struct mschmd_file **f_ptr, const char *name) | ||
1247 | { | ||
1248 | struct mspack_system *sys = self->system; | ||
1249 | struct mschmd_file result; | ||
1250 | |||
1251 | /* already loaded */ | ||
1252 | if (*f_ptr) return MSPACK_ERR_OK; | ||
1253 | |||
1254 | /* try using fast_find to find the file - return DATAFORMAT error if | ||
1255 | * it fails, or successfully doesn't find the file */ | ||
1256 | if (chmd_fast_find((struct mschm_decompressor *) self, sec->base.chm, | ||
1257 | name, &result, (int)sizeof(result)) || !result.section) | ||
1258 | { | ||
1259 | return MSPACK_ERR_DATAFORMAT; | ||
1260 | } | ||
1261 | |||
1262 | if (!(*f_ptr = (struct mschmd_file *) sys->alloc(sys, sizeof(result)))) { | ||
1263 | return MSPACK_ERR_NOMEMORY; | ||
1264 | } | ||
1265 | |||
1266 | /* copy result */ | ||
1267 | *(*f_ptr) = result; | ||
1268 | (*f_ptr)->filename = (char *) name; | ||
1269 | |||
1270 | /* link file into sysfiles list */ | ||
1271 | (*f_ptr)->next = sec->base.chm->sysfiles; | ||
1272 | sec->base.chm->sysfiles = *f_ptr; | ||
1273 | return MSPACK_ERR_OK; | ||
1274 | } | ||
1275 | |||
1276 | /*************************************** | ||
1277 | * READ_SYS_FILE | ||
1278 | *************************************** | ||
1279 | * Allocates memory for a section 0 (uncompressed) file and reads it into | ||
1280 | * memory. | ||
1281 | */ | ||
1282 | static unsigned char *read_sys_file(struct mschm_decompressor_p *self, | ||
1283 | struct mschmd_file *file) | ||
1284 | { | ||
1285 | struct mspack_system *sys = self->system; | ||
1286 | unsigned char *data = NULL; | ||
1287 | int len; | ||
1288 | |||
1289 | if (!file || !file->section || (file->section->id != 0)) { | ||
1290 | self->error = MSPACK_ERR_DATAFORMAT; | ||
1291 | return NULL; | ||
1292 | } | ||
1293 | |||
1294 | len = (int) file->length; | ||
1295 | |||
1296 | if (!(data = (unsigned char *) sys->alloc(sys, (size_t) len))) { | ||
1297 | self->error = MSPACK_ERR_NOMEMORY; | ||
1298 | return NULL; | ||
1299 | } | ||
1300 | if (sys->seek(self->d->infh, file->section->chm->sec0.offset | ||
1301 | + file->offset, MSPACK_SYS_SEEK_START)) | ||
1302 | { | ||
1303 | self->error = MSPACK_ERR_SEEK; | ||
1304 | sys->free(data); | ||
1305 | return NULL; | ||
1306 | } | ||
1307 | if (sys->read(self->d->infh, data, len) != len) { | ||
1308 | self->error = MSPACK_ERR_READ; | ||
1309 | sys->free(data); | ||
1310 | return NULL; | ||
1311 | } | ||
1312 | return data; | ||
1313 | } | ||
1314 | |||
1315 | /*************************************** | ||
1316 | * CHMD_ERROR | ||
1317 | *************************************** | ||
1318 | * returns the last error that occurred | ||
1319 | */ | ||
1320 | static int chmd_error(struct mschm_decompressor *base) { | ||
1321 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; | ||
1322 | return (self) ? self->error : MSPACK_ERR_ARGS; | ||
1323 | } | ||
1324 | |||
1325 | /*************************************** | ||
1326 | * READ_OFF64 | ||
1327 | *************************************** | ||
1328 | * Reads a 64-bit signed integer from memory in Intel byte order. | ||
1329 | * If running on a system with a 64-bit off_t, this is simply done. | ||
1330 | * If running on a system with a 32-bit off_t, offsets up to 0x7FFFFFFF | ||
1331 | * are accepted, offsets beyond that cause an error message. | ||
1332 | */ | ||
1333 | static int read_off64(off_t *var, unsigned char *mem, | ||
1334 | struct mspack_system *sys, struct mspack_file *fh) | ||
1335 | { | ||
1336 | #ifdef LARGEFILE_SUPPORT | ||
1337 | *var = EndGetI64(mem); | ||
1338 | #else | ||
1339 | *var = EndGetI32(mem); | ||
1340 | if ((*var & 0x80000000) || EndGetI32(mem+4)) { | ||
1341 | sys->message(fh, (char *)largefile_msg); | ||
1342 | return 1; | ||
1343 | } | ||
1344 | #endif | ||
1345 | return 0; | ||
1346 | } | ||
diff --git a/rbutil/rbutilqt/mspack/des.h b/rbutil/rbutilqt/mspack/des.h new file mode 100644 index 0000000000..64a1ed27e4 --- /dev/null +++ b/rbutil/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/rbutil/rbutilqt/mspack/hlp.h b/rbutil/rbutilqt/mspack/hlp.h new file mode 100644 index 0000000000..b7486fa160 --- /dev/null +++ b/rbutil/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 | |||
19 | struct mshlp_compressor_p { | ||
20 | struct mshlp_compressor base; | ||
21 | struct mspack_system *system; | ||
22 | /* todo */ | ||
23 | }; | ||
24 | |||
25 | /* HLP decompression definitions */ | ||
26 | |||
27 | struct mshlp_decompressor_p { | ||
28 | struct mshlp_decompressor base; | ||
29 | struct mspack_system *system; | ||
30 | /* todo */ | ||
31 | }; | ||
32 | |||
33 | #endif | ||
diff --git a/rbutil/rbutilqt/mspack/hlpc.c b/rbutil/rbutilqt/mspack/hlpc.c new file mode 100644 index 0000000000..60eabfe207 --- /dev/null +++ b/rbutil/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.h> | ||
13 | #include <hlp.h> | ||
14 | |||
15 | struct mshlp_compressor * | ||
16 | mspack_create_hlp_compressor(struct mspack_system *sys) | ||
17 | { | ||
18 | /* todo */ | ||
19 | return NULL; | ||
20 | } | ||
21 | |||
22 | void mspack_destroy_hlp_compressor(struct mshlp_compressor *self) { | ||
23 | /* todo */ | ||
24 | } | ||
diff --git a/rbutil/rbutilqt/mspack/hlpd.c b/rbutil/rbutilqt/mspack/hlpd.c new file mode 100644 index 0000000000..43354f008f --- /dev/null +++ b/rbutil/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.h> | ||
13 | #include <hlp.h> | ||
14 | |||
15 | struct mshlp_decompressor * | ||
16 | mspack_create_hlp_decompressor(struct mspack_system *sys) | ||
17 | { | ||
18 | /* todo */ | ||
19 | return NULL; | ||
20 | } | ||
21 | |||
22 | void mspack_destroy_hlp_decompressor(struct mshlp_decompressor *self) { | ||
23 | /* todo */ | ||
24 | } | ||
diff --git a/rbutil/rbutilqt/mspack/kwaj.h b/rbutil/rbutilqt/mspack/kwaj.h new file mode 100644 index 0000000000..09673c0779 --- /dev/null +++ b/rbutil/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 | |||
25 | struct 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 | |||
35 | struct mskwaj_decompressor_p { | ||
36 | struct mskwaj_decompressor base; | ||
37 | struct mspack_system *system; | ||
38 | int error; | ||
39 | }; | ||
40 | |||
41 | struct 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 | |||
87 | struct 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/rbutil/rbutilqt/mspack/kwajc.c b/rbutil/rbutilqt/mspack/kwajc.c new file mode 100644 index 0000000000..b88ed7690a --- /dev/null +++ b/rbutil/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.h> | ||
13 | #include <kwaj.h> | ||
14 | |||
15 | struct mskwaj_compressor * | ||
16 | mspack_create_kwaj_compressor(struct mspack_system *sys) | ||
17 | { | ||
18 | /* todo */ | ||
19 | return NULL; | ||
20 | } | ||
21 | |||
22 | void mspack_destroy_kwaj_compressor(struct mskwaj_compressor *self) { | ||
23 | /* todo */ | ||
24 | } | ||
diff --git a/rbutil/rbutilqt/mspack/kwajd.c b/rbutil/rbutilqt/mspack/kwajd.c new file mode 100644 index 0000000000..d891b6a7e3 --- /dev/null +++ b/rbutil/rbutilqt/mspack/kwajd.c | |||
@@ -0,0 +1,555 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2010 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.h> | ||
16 | #include <kwaj.h> | ||
17 | |||
18 | /* prototypes */ | ||
19 | static struct mskwajd_header *kwajd_open( | ||
20 | struct mskwaj_decompressor *base, const char *filename); | ||
21 | static void kwajd_close( | ||
22 | struct mskwaj_decompressor *base, struct mskwajd_header *hdr); | ||
23 | static int kwajd_read_headers( | ||
24 | struct mspack_system *sys, struct mspack_file *fh, | ||
25 | struct mskwajd_header *hdr); | ||
26 | static int kwajd_extract( | ||
27 | struct mskwaj_decompressor *base, struct mskwajd_header *hdr, | ||
28 | const char *filename); | ||
29 | static int kwajd_decompress( | ||
30 | struct mskwaj_decompressor *base, const char *input, const char *output); | ||
31 | static int kwajd_error( | ||
32 | struct mskwaj_decompressor *base); | ||
33 | |||
34 | static struct kwajd_stream *lzh_init( | ||
35 | struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out); | ||
36 | static int lzh_decompress( | ||
37 | struct kwajd_stream *kwaj); | ||
38 | static void lzh_free( | ||
39 | struct kwajd_stream *kwaj); | ||
40 | static int lzh_read_lens( | ||
41 | struct kwajd_stream *kwaj, | ||
42 | unsigned int type, unsigned int numsyms, | ||
43 | unsigned char *lens, unsigned short *table); | ||
44 | static int lzh_read_input( | ||
45 | struct kwajd_stream *kwaj); | ||
46 | |||
47 | |||
48 | /*************************************** | ||
49 | * MSPACK_CREATE_KWAJ_DECOMPRESSOR | ||
50 | *************************************** | ||
51 | * constructor | ||
52 | */ | ||
53 | struct mskwaj_decompressor * | ||
54 | mspack_create_kwaj_decompressor(struct mspack_system *sys) | ||
55 | { | ||
56 | struct mskwaj_decompressor_p *self = NULL; | ||
57 | |||
58 | if (!sys) sys = mspack_default_system; | ||
59 | if (!mspack_valid_system(sys)) return NULL; | ||
60 | |||
61 | if ((self = (struct mskwaj_decompressor_p *) sys->alloc(sys, sizeof(struct mskwaj_decompressor_p)))) { | ||
62 | self->base.open = &kwajd_open; | ||
63 | self->base.close = &kwajd_close; | ||
64 | self->base.extract = &kwajd_extract; | ||
65 | self->base.decompress = &kwajd_decompress; | ||
66 | self->base.last_error = &kwajd_error; | ||
67 | self->system = sys; | ||
68 | self->error = MSPACK_ERR_OK; | ||
69 | } | ||
70 | return (struct mskwaj_decompressor *) self; | ||
71 | } | ||
72 | |||
73 | /*************************************** | ||
74 | * MSPACK_DESTROY_KWAJ_DECOMPRESSOR | ||
75 | *************************************** | ||
76 | * destructor | ||
77 | */ | ||
78 | void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *base) | ||
79 | { | ||
80 | struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; | ||
81 | if (self) { | ||
82 | struct mspack_system *sys = self->system; | ||
83 | sys->free(self); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | /*************************************** | ||
88 | * KWAJD_OPEN | ||
89 | *************************************** | ||
90 | * opens a KWAJ file without decompressing, reads header | ||
91 | */ | ||
92 | static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base, | ||
93 | const char *filename) | ||
94 | { | ||
95 | struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; | ||
96 | struct mskwajd_header *hdr; | ||
97 | struct mspack_system *sys; | ||
98 | struct mspack_file *fh; | ||
99 | |||
100 | if (!self) return NULL; | ||
101 | sys = self->system; | ||
102 | |||
103 | fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ); | ||
104 | hdr = (struct mskwajd_header *) sys->alloc(sys, sizeof(struct mskwajd_header_p)); | ||
105 | if (fh && hdr) { | ||
106 | ((struct mskwajd_header_p *) hdr)->fh = fh; | ||
107 | self->error = kwajd_read_headers(sys, fh, hdr); | ||
108 | } | ||
109 | else { | ||
110 | if (!fh) self->error = MSPACK_ERR_OPEN; | ||
111 | if (!hdr) self->error = MSPACK_ERR_NOMEMORY; | ||
112 | } | ||
113 | |||
114 | if (self->error) { | ||
115 | if (fh) sys->close(fh); | ||
116 | if (hdr) sys->free(hdr); | ||
117 | hdr = NULL; | ||
118 | } | ||
119 | |||
120 | return hdr; | ||
121 | } | ||
122 | |||
123 | /*************************************** | ||
124 | * KWAJD_CLOSE | ||
125 | *************************************** | ||
126 | * closes a KWAJ file | ||
127 | */ | ||
128 | static void kwajd_close(struct mskwaj_decompressor *base, | ||
129 | struct mskwajd_header *hdr) | ||
130 | { | ||
131 | struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; | ||
132 | struct mskwajd_header_p *hdr_p = (struct mskwajd_header_p *) hdr; | ||
133 | |||
134 | if (!self || !self->system) return; | ||
135 | |||
136 | /* close the file handle associated */ | ||
137 | self->system->close(hdr_p->fh); | ||
138 | |||
139 | /* free the memory associated */ | ||
140 | self->system->free(hdr); | ||
141 | |||
142 | self->error = MSPACK_ERR_OK; | ||
143 | } | ||
144 | |||
145 | /*************************************** | ||
146 | * KWAJD_READ_HEADERS | ||
147 | *************************************** | ||
148 | * reads the headers of a KWAJ format file | ||
149 | */ | ||
150 | static int kwajd_read_headers(struct mspack_system *sys, | ||
151 | struct mspack_file *fh, | ||
152 | struct mskwajd_header *hdr) | ||
153 | { | ||
154 | unsigned char buf[16]; | ||
155 | int i; | ||
156 | |||
157 | /* read in the header */ | ||
158 | if (sys->read(fh, &buf[0], kwajh_SIZEOF) != kwajh_SIZEOF) { | ||
159 | return MSPACK_ERR_READ; | ||
160 | } | ||
161 | |||
162 | /* check for "KWAJ" signature */ | ||
163 | if (((unsigned int) EndGetI32(&buf[kwajh_Signature1]) != 0x4A41574B) || | ||
164 | ((unsigned int) EndGetI32(&buf[kwajh_Signature2]) != 0xD127F088)) | ||
165 | { | ||
166 | return MSPACK_ERR_SIGNATURE; | ||
167 | } | ||
168 | |||
169 | /* basic header fields */ | ||
170 | hdr->comp_type = EndGetI16(&buf[kwajh_CompMethod]); | ||
171 | hdr->data_offset = EndGetI16(&buf[kwajh_DataOffset]); | ||
172 | hdr->headers = EndGetI16(&buf[kwajh_Flags]); | ||
173 | hdr->length = 0; | ||
174 | hdr->filename = NULL; | ||
175 | hdr->extra = NULL; | ||
176 | hdr->extra_length = 0; | ||
177 | |||
178 | /* optional headers */ | ||
179 | |||
180 | /* 4 bytes: length of unpacked file */ | ||
181 | if (hdr->headers & MSKWAJ_HDR_HASLENGTH) { | ||
182 | if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ; | ||
183 | hdr->length = EndGetI32(&buf[0]); | ||
184 | } | ||
185 | |||
186 | /* 2 bytes: unknown purpose */ | ||
187 | if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN1) { | ||
188 | if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; | ||
189 | } | ||
190 | |||
191 | /* 2 bytes: length of section, then [length] bytes: unknown purpose */ | ||
192 | if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN2) { | ||
193 | if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; | ||
194 | i = EndGetI16(&buf[0]); | ||
195 | if (sys->seek(fh, (off_t)i, MSPACK_SYS_SEEK_CUR)) return MSPACK_ERR_SEEK; | ||
196 | } | ||
197 | |||
198 | /* filename and extension */ | ||
199 | if (hdr->headers & (MSKWAJ_HDR_HASFILENAME | MSKWAJ_HDR_HASFILEEXT)) { | ||
200 | off_t pos = sys->tell(fh); | ||
201 | char *fn = (char *) sys->alloc(sys, (size_t) 13); | ||
202 | |||
203 | /* allocate memory for maximum length filename */ | ||
204 | if (! fn) return MSPACK_ERR_NOMEMORY; | ||
205 | hdr->filename = fn; | ||
206 | |||
207 | /* copy filename if present */ | ||
208 | if (hdr->headers & MSKWAJ_HDR_HASFILENAME) { | ||
209 | if (sys->read(fh, &buf[0], 9) != 9) return MSPACK_ERR_READ; | ||
210 | for (i = 0; i < 9; i++, fn++) if (!(*fn = buf[i])) break; | ||
211 | pos += (i < 9) ? i+1 : 9; | ||
212 | if (sys->seek(fh, pos, MSPACK_SYS_SEEK_START)) | ||
213 | return MSPACK_ERR_SEEK; | ||
214 | } | ||
215 | |||
216 | /* copy extension if present */ | ||
217 | if (hdr->headers & MSKWAJ_HDR_HASFILEEXT) { | ||
218 | *fn++ = '.'; | ||
219 | if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ; | ||
220 | for (i = 0; i < 4; i++, fn++) if (!(*fn = buf[i])) break; | ||
221 | pos += (i < 4) ? i+1 : 4; | ||
222 | if (sys->seek(fh, pos, MSPACK_SYS_SEEK_START)) | ||
223 | return MSPACK_ERR_SEEK; | ||
224 | } | ||
225 | *fn = '\0'; | ||
226 | } | ||
227 | |||
228 | /* 2 bytes: extra text length then [length] bytes of extra text data */ | ||
229 | if (hdr->headers & MSKWAJ_HDR_HASEXTRATEXT) { | ||
230 | if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; | ||
231 | i = EndGetI16(&buf[0]); | ||
232 | hdr->extra = (char *) sys->alloc(sys, (size_t)i+1); | ||
233 | if (! hdr->extra) return MSPACK_ERR_NOMEMORY; | ||
234 | if (sys->read(fh, hdr->extra, i) != i) return MSPACK_ERR_READ; | ||
235 | hdr->extra[i] = '\0'; | ||
236 | hdr->extra_length = i; | ||
237 | } | ||
238 | return MSPACK_ERR_OK; | ||
239 | } | ||
240 | |||
241 | /*************************************** | ||
242 | * KWAJD_EXTRACT | ||
243 | *************************************** | ||
244 | * decompresses a KWAJ file | ||
245 | */ | ||
246 | static int kwajd_extract(struct mskwaj_decompressor *base, | ||
247 | struct mskwajd_header *hdr, const char *filename) | ||
248 | { | ||
249 | struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; | ||
250 | struct mspack_system *sys; | ||
251 | struct mspack_file *fh, *outfh; | ||
252 | |||
253 | if (!self) return MSPACK_ERR_ARGS; | ||
254 | if (!hdr) return self->error = MSPACK_ERR_ARGS; | ||
255 | |||
256 | sys = self->system; | ||
257 | fh = ((struct mskwajd_header_p *) hdr)->fh; | ||
258 | |||
259 | /* seek to the compressed data */ | ||
260 | if (sys->seek(fh, hdr->data_offset, MSPACK_SYS_SEEK_START)) { | ||
261 | return self->error = MSPACK_ERR_SEEK; | ||
262 | } | ||
263 | |||
264 | /* open file for output */ | ||
265 | if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { | ||
266 | return self->error = MSPACK_ERR_OPEN; | ||
267 | } | ||
268 | |||
269 | self->error = MSPACK_ERR_OK; | ||
270 | |||
271 | /* decompress based on format */ | ||
272 | if (hdr->comp_type == MSKWAJ_COMP_NONE || | ||
273 | hdr->comp_type == MSKWAJ_COMP_XOR) | ||
274 | { | ||
275 | /* NONE is a straight copy. XOR is a copy xored with 0xFF */ | ||
276 | unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) KWAJ_INPUT_SIZE); | ||
277 | if (buf) { | ||
278 | int read, i; | ||
279 | while ((read = sys->read(fh, buf, KWAJ_INPUT_SIZE)) > 0) { | ||
280 | if (hdr->comp_type == MSKWAJ_COMP_XOR) { | ||
281 | for (i = 0; i < read; i++) buf[i] ^= 0xFF; | ||
282 | } | ||
283 | if (sys->write(outfh, buf, read) != read) { | ||
284 | self->error = MSPACK_ERR_WRITE; | ||
285 | break; | ||
286 | } | ||
287 | } | ||
288 | if (read < 0) self->error = MSPACK_ERR_READ; | ||
289 | sys->free(buf); | ||
290 | } | ||
291 | else { | ||
292 | self->error = MSPACK_ERR_NOMEMORY; | ||
293 | } | ||
294 | } | ||
295 | else if (hdr->comp_type == MSKWAJ_COMP_SZDD) { | ||
296 | self->error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE, | ||
297 | LZSS_MODE_EXPAND); | ||
298 | } | ||
299 | else if (hdr->comp_type == MSKWAJ_COMP_LZH) { | ||
300 | struct kwajd_stream *lzh = lzh_init(sys, fh, outfh); | ||
301 | self->error = (lzh) ? lzh_decompress(lzh) : MSPACK_ERR_NOMEMORY; | ||
302 | lzh_free(lzh); | ||
303 | } | ||
304 | else { | ||
305 | self->error = MSPACK_ERR_DATAFORMAT; | ||
306 | } | ||
307 | |||
308 | /* close output file */ | ||
309 | sys->close(outfh); | ||
310 | |||
311 | return self->error; | ||
312 | } | ||
313 | |||
314 | /*************************************** | ||
315 | * KWAJD_DECOMPRESS | ||
316 | *************************************** | ||
317 | * unpacks directly from input to output | ||
318 | */ | ||
319 | static int kwajd_decompress(struct mskwaj_decompressor *base, | ||
320 | const char *input, const char *output) | ||
321 | { | ||
322 | struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; | ||
323 | struct mskwajd_header *hdr; | ||
324 | int error; | ||
325 | |||
326 | if (!self) return MSPACK_ERR_ARGS; | ||
327 | |||
328 | if (!(hdr = kwajd_open(base, input))) return self->error; | ||
329 | error = kwajd_extract(base, hdr, output); | ||
330 | kwajd_close(base, hdr); | ||
331 | return self->error = error; | ||
332 | } | ||
333 | |||
334 | /*************************************** | ||
335 | * KWAJD_ERROR | ||
336 | *************************************** | ||
337 | * returns the last error that occurred | ||
338 | */ | ||
339 | static int kwajd_error(struct mskwaj_decompressor *base) | ||
340 | { | ||
341 | struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; | ||
342 | return (self) ? self->error : MSPACK_ERR_ARGS; | ||
343 | } | ||
344 | |||
345 | /*************************************** | ||
346 | * LZH_INIT, LZH_DECOMPRESS, LZH_FREE | ||
347 | *************************************** | ||
348 | * unpacks KWAJ method 3 files | ||
349 | */ | ||
350 | |||
351 | /* import bit-reading macros and code */ | ||
352 | #define BITS_TYPE struct kwajd_stream | ||
353 | #define BITS_VAR lzh | ||
354 | #define BITS_ORDER_MSB | ||
355 | #define BITS_NO_READ_INPUT | ||
356 | #define READ_BYTES do { \ | ||
357 | if (i_ptr >= i_end) { \ | ||
358 | if ((err = lzh_read_input(lzh))) return err; \ | ||
359 | i_ptr = lzh->i_ptr; \ | ||
360 | i_end = lzh->i_end; \ | ||
361 | } \ | ||
362 | INJECT_BITS(*i_ptr++, 8); \ | ||
363 | } while (0) | ||
364 | #include <readbits.h> | ||
365 | |||
366 | /* import huffman-reading macros and code */ | ||
367 | #define TABLEBITS(tbl) KWAJ_TABLEBITS | ||
368 | #define MAXSYMBOLS(tbl) KWAJ_##tbl##_SYMS | ||
369 | #define HUFF_TABLE(tbl,idx) lzh->tbl##_table[idx] | ||
370 | #define HUFF_LEN(tbl,idx) lzh->tbl##_len[idx] | ||
371 | #define HUFF_ERROR return MSPACK_ERR_DATAFORMAT | ||
372 | #include <readhuff.h> | ||
373 | |||
374 | /* In the KWAJ LZH format, there is no special 'eof' marker, it just | ||
375 | * ends. Depending on how many bits are left in the final byte when | ||
376 | * the stream ends, that might be enough to start another literal or | ||
377 | * match. The only easy way to detect that we've come to an end is to | ||
378 | * guard all bit-reading. We allow fake bits to be read once we reach | ||
379 | * the end of the stream, but we check if we then consumed any of | ||
380 | * those fake bits, after doing the READ_BITS / READ_HUFFSYM. This | ||
381 | * isn't how the default readbits.h read_input() works (it simply lets | ||
382 | * 2 fake bytes in then stops), so we implement our own. | ||
383 | */ | ||
384 | #define READ_BITS_SAFE(val, n) do { \ | ||
385 | READ_BITS(val, n); \ | ||
386 | if (lzh->input_end && bits_left < lzh->input_end) \ | ||
387 | return MSPACK_ERR_OK; \ | ||
388 | } while (0) | ||
389 | |||
390 | #define READ_HUFFSYM_SAFE(tbl, val) do { \ | ||
391 | READ_HUFFSYM(tbl, val); \ | ||
392 | if (lzh->input_end && bits_left < lzh->input_end) \ | ||
393 | return MSPACK_ERR_OK; \ | ||
394 | } while (0) | ||
395 | |||
396 | #define BUILD_TREE(tbl, type) \ | ||
397 | STORE_BITS; \ | ||
398 | err = lzh_read_lens(lzh, type, MAXSYMBOLS(tbl), \ | ||
399 | &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0)); \ | ||
400 | if (err) return err; \ | ||
401 | RESTORE_BITS; \ | ||
402 | if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ | ||
403 | &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ | ||
404 | return MSPACK_ERR_DATAFORMAT; | ||
405 | |||
406 | #define WRITE_BYTE do { \ | ||
407 | if (lzh->sys->write(lzh->output, &lzh->window[pos], 1) != 1) \ | ||
408 | return MSPACK_ERR_WRITE; \ | ||
409 | } while (0) | ||
410 | |||
411 | static struct kwajd_stream *lzh_init(struct mspack_system *sys, | ||
412 | struct mspack_file *in, struct mspack_file *out) | ||
413 | { | ||
414 | struct kwajd_stream *lzh; | ||
415 | |||
416 | if (!sys || !in || !out) return NULL; | ||
417 | if (!(lzh = (struct kwajd_stream *) sys->alloc(sys, sizeof(struct kwajd_stream)))) return NULL; | ||
418 | |||
419 | lzh->sys = sys; | ||
420 | lzh->input = in; | ||
421 | lzh->output = out; | ||
422 | return lzh; | ||
423 | } | ||
424 | |||
425 | static int lzh_decompress(struct kwajd_stream *lzh) | ||
426 | { | ||
427 | register unsigned int bit_buffer; | ||
428 | register int bits_left, i; | ||
429 | register unsigned short sym; | ||
430 | unsigned char *i_ptr, *i_end, lit_run = 0; | ||
431 | int j, pos = 0, len, offset, err; | ||
432 | unsigned int types[6]; | ||
433 | |||
434 | /* reset global state */ | ||
435 | INIT_BITS; | ||
436 | RESTORE_BITS; | ||
437 | memset(&lzh->window[0], LZSS_WINDOW_FILL, (size_t) LZSS_WINDOW_SIZE); | ||
438 | |||
439 | /* read 6 encoding types (for byte alignment) but only 5 are needed */ | ||
440 | for (i = 0; i < 6; i++) READ_BITS_SAFE(types[i], 4); | ||
441 | |||
442 | /* read huffman table symbol lengths and build huffman trees */ | ||
443 | BUILD_TREE(MATCHLEN1, types[0]); | ||
444 | BUILD_TREE(MATCHLEN2, types[1]); | ||
445 | BUILD_TREE(LITLEN, types[2]); | ||
446 | BUILD_TREE(OFFSET, types[3]); | ||
447 | BUILD_TREE(LITERAL, types[4]); | ||
448 | |||
449 | while (!lzh->input_end) { | ||
450 | if (lit_run) READ_HUFFSYM_SAFE(MATCHLEN2, len); | ||
451 | else READ_HUFFSYM_SAFE(MATCHLEN1, len); | ||
452 | |||
453 | if (len > 0) { | ||
454 | len += 2; | ||
455 | lit_run = 0; /* not the end of a literal run */ | ||
456 | READ_HUFFSYM_SAFE(OFFSET, j); offset = j << 6; | ||
457 | READ_BITS_SAFE(j, 6); offset |= j; | ||
458 | |||
459 | /* copy match as output and into the ring buffer */ | ||
460 | while (len-- > 0) { | ||
461 | lzh->window[pos] = lzh->window[(pos+4096-offset) & 4095]; | ||
462 | WRITE_BYTE; | ||
463 | pos++; pos &= 4095; | ||
464 | } | ||
465 | } | ||
466 | else { | ||
467 | READ_HUFFSYM_SAFE(LITLEN, len); len++; | ||
468 | lit_run = (len == 32) ? 0 : 1; /* end of a literal run? */ | ||
469 | while (len-- > 0) { | ||
470 | READ_HUFFSYM_SAFE(LITERAL, j); | ||
471 | /* copy as output and into the ring buffer */ | ||
472 | lzh->window[pos] = j; | ||
473 | WRITE_BYTE; | ||
474 | pos++; pos &= 4095; | ||
475 | } | ||
476 | } | ||
477 | } | ||
478 | return MSPACK_ERR_OK; | ||
479 | } | ||
480 | |||
481 | static void lzh_free(struct kwajd_stream *lzh) | ||
482 | { | ||
483 | struct mspack_system *sys; | ||
484 | if (!lzh || !lzh->sys) return; | ||
485 | sys = lzh->sys; | ||
486 | sys->free(lzh); | ||
487 | } | ||
488 | |||
489 | static int lzh_read_lens(struct kwajd_stream *lzh, | ||
490 | unsigned int type, unsigned int numsyms, | ||
491 | unsigned char *lens, unsigned short *table) | ||
492 | { | ||
493 | register unsigned int bit_buffer; | ||
494 | register int bits_left; | ||
495 | unsigned char *i_ptr, *i_end; | ||
496 | unsigned int i, c, sel; | ||
497 | int err; | ||
498 | |||
499 | RESTORE_BITS; | ||
500 | switch (type) { | ||
501 | case 0: | ||
502 | i = numsyms; c = (i==16)?4: (i==32)?5: (i==64)?6: (i==256)?8 :0; | ||
503 | for (i = 0; i < numsyms; i++) lens[i] = c; | ||
504 | break; | ||
505 | |||
506 | case 1: | ||
507 | READ_BITS_SAFE(c, 4); lens[0] = c; | ||
508 | for (i = 1; i < numsyms; i++) { | ||
509 | READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = c; | ||
510 | else { READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = ++c; | ||
511 | else { READ_BITS_SAFE(c, 4); lens[i] = c; }} | ||
512 | } | ||
513 | break; | ||
514 | |||
515 | case 2: | ||
516 | READ_BITS_SAFE(c, 4); lens[0] = c; | ||
517 | for (i = 1; i < numsyms; i++) { | ||
518 | READ_BITS_SAFE(sel, 2); | ||
519 | if (sel == 3) READ_BITS_SAFE(c, 4); else c += (char) sel-1; | ||
520 | lens[i] = c; | ||
521 | } | ||
522 | break; | ||
523 | |||
524 | case 3: | ||
525 | for (i = 0; i < numsyms; i++) { | ||
526 | READ_BITS_SAFE(c, 4); lens[i] = c; | ||
527 | } | ||
528 | break; | ||
529 | } | ||
530 | STORE_BITS; | ||
531 | return MSPACK_ERR_OK; | ||
532 | } | ||
533 | |||
534 | static int lzh_read_input(struct kwajd_stream *lzh) { | ||
535 | int read; | ||
536 | if (lzh->input_end) { | ||
537 | lzh->input_end += 8; | ||
538 | lzh->inbuf[0] = 0; | ||
539 | read = 1; | ||
540 | } | ||
541 | else { | ||
542 | read = lzh->sys->read(lzh->input, &lzh->inbuf[0], KWAJ_INPUT_SIZE); | ||
543 | if (read < 0) return MSPACK_ERR_READ; | ||
544 | if (read == 0) { | ||
545 | lzh->input_end = 8; | ||
546 | lzh->inbuf[0] = 0; | ||
547 | read = 1; | ||
548 | } | ||
549 | } | ||
550 | |||
551 | /* update i_ptr and i_end */ | ||
552 | lzh->i_ptr = &lzh->inbuf[0]; | ||
553 | lzh->i_end = &lzh->inbuf[read]; | ||
554 | return MSPACK_ERR_OK; | ||
555 | } | ||
diff --git a/rbutil/rbutilqt/mspack/lit.h b/rbutil/rbutilqt/mspack/lit.h new file mode 100644 index 0000000000..79ba44d877 --- /dev/null +++ b/rbutil/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 | |||
21 | struct mslit_compressor_p { | ||
22 | struct mslit_compressor base; | ||
23 | struct mspack_system *system; | ||
24 | /* todo */ | ||
25 | }; | ||
26 | |||
27 | /* LIT decompression definitions */ | ||
28 | |||
29 | struct mslit_decompressor_p { | ||
30 | struct mslit_decompressor base; | ||
31 | struct mspack_system *system; | ||
32 | /* todo */ | ||
33 | }; | ||
34 | |||
35 | #endif | ||
diff --git a/rbutil/rbutilqt/mspack/litc.c b/rbutil/rbutilqt/mspack/litc.c new file mode 100644 index 0000000000..a8a709af07 --- /dev/null +++ b/rbutil/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.h> | ||
13 | #include <lit.h> | ||
14 | |||
15 | struct mslit_compressor * | ||
16 | mspack_create_lit_compressor(struct mspack_system *sys) | ||
17 | { | ||
18 | /* todo */ | ||
19 | return NULL; | ||
20 | } | ||
21 | |||
22 | void mspack_destroy_lit_compressor(struct mslit_compressor *self) { | ||
23 | /* todo */ | ||
24 | } | ||
diff --git a/rbutil/rbutilqt/mspack/litd.c b/rbutil/rbutilqt/mspack/litd.c new file mode 100644 index 0000000000..6e0dc9af27 --- /dev/null +++ b/rbutil/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.h> | ||
13 | #include <lit.h> | ||
14 | |||
15 | struct mslit_decompressor * | ||
16 | mspack_create_lit_decompressor(struct mspack_system *sys) | ||
17 | { | ||
18 | /* todo */ | ||
19 | return NULL; | ||
20 | } | ||
21 | |||
22 | void mspack_destroy_lit_decompressor(struct mslit_decompressor *self) { | ||
23 | /* todo */ | ||
24 | } | ||
diff --git a/rbutil/rbutilqt/mspack/lzss.h b/rbutil/rbutilqt/mspack/lzss.h new file mode 100644 index 0000000000..55e761b5bf --- /dev/null +++ b/rbutil/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 | ||
14 | extern "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 | */ | ||
56 | extern 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/rbutil/rbutilqt/mspack/lzssd.c b/rbutil/rbutilqt/mspack/lzssd.c new file mode 100644 index 0000000000..df294bc132 --- /dev/null +++ b/rbutil/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.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 | |||
37 | int 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/rbutil/rbutilqt/mspack/lzx.h b/rbutil/rbutilqt/mspack/lzx.h new file mode 100644 index 0000000000..fc69928c96 --- /dev/null +++ b/rbutil/rbutilqt/mspack/lzx.h | |||
@@ -0,0 +1,194 @@ | |||
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 | #ifndef MSPACK_LZX_H | ||
14 | #define MSPACK_LZX_H 1 | ||
15 | |||
16 | #ifdef __cplusplus | ||
17 | extern "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 + 50*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 | |||
48 | struct 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 window_posn; /* decompression offset within window */ | ||
59 | unsigned int frame_posn; /* current frame offset within in window */ | ||
60 | unsigned int frame; /* the number of 32kb frames processed */ | ||
61 | unsigned int reset_interval; /* which frame do we reset the compressor? */ | ||
62 | |||
63 | unsigned int R0, R1, R2; /* for the LRU offset system */ | ||
64 | unsigned int block_length; /* uncompressed length of this LZX block */ | ||
65 | unsigned int block_remaining; /* uncompressed bytes still left to decode */ | ||
66 | |||
67 | signed int intel_filesize; /* magic header value used for transform */ | ||
68 | signed int intel_curpos; /* current offset in transform space */ | ||
69 | |||
70 | unsigned char intel_started; /* has intel E8 decoding started? */ | ||
71 | unsigned char block_type; /* type of the current block */ | ||
72 | unsigned char header_read; /* have we started decoding at all yet? */ | ||
73 | unsigned char posn_slots; /* how many posn slots in stream? */ | ||
74 | unsigned char input_end; /* have we reached the end of input? */ | ||
75 | |||
76 | int error; | ||
77 | |||
78 | /* I/O buffering */ | ||
79 | unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end; | ||
80 | unsigned int bit_buffer, bits_left, inbuf_size; | ||
81 | |||
82 | /* huffman code lengths */ | ||
83 | unsigned char PRETREE_len [LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; | ||
84 | unsigned char MAINTREE_len [LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; | ||
85 | unsigned char LENGTH_len [LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; | ||
86 | unsigned char ALIGNED_len [LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; | ||
87 | |||
88 | /* huffman decoding tables */ | ||
89 | unsigned short PRETREE_table [(1 << LZX_PRETREE_TABLEBITS) + | ||
90 | (LZX_PRETREE_MAXSYMBOLS * 2)]; | ||
91 | unsigned short MAINTREE_table[(1 << LZX_MAINTREE_TABLEBITS) + | ||
92 | (LZX_MAINTREE_MAXSYMBOLS * 2)]; | ||
93 | unsigned short LENGTH_table [(1 << LZX_LENGTH_TABLEBITS) + | ||
94 | (LZX_LENGTH_MAXSYMBOLS * 2)]; | ||
95 | unsigned short ALIGNED_table [(1 << LZX_ALIGNED_TABLEBITS) + | ||
96 | (LZX_ALIGNED_MAXSYMBOLS * 2)]; | ||
97 | unsigned char LENGTH_empty; | ||
98 | |||
99 | /* this is used purely for doing the intel E8 transform */ | ||
100 | unsigned char e8_buf[LZX_FRAME_SIZE]; | ||
101 | }; | ||
102 | |||
103 | /** | ||
104 | * Allocates and initialises LZX decompression state for decoding an LZX | ||
105 | * stream. | ||
106 | * | ||
107 | * This routine uses system->alloc() to allocate memory. If memory | ||
108 | * allocation fails, or the parameters to this function are invalid, | ||
109 | * NULL is returned. | ||
110 | * | ||
111 | * @param system an mspack_system structure used to read from | ||
112 | * the input stream and write to the output | ||
113 | * stream, also to allocate and free memory. | ||
114 | * @param input an input stream with the LZX data. | ||
115 | * @param output an output stream to write the decoded data to. | ||
116 | * @param window_bits the size of the decoding window, which must be | ||
117 | * between 15 and 21 inclusive. | ||
118 | * @param reset_interval the interval at which the LZX bitstream is | ||
119 | * reset, in multiples of LZX frames (32678 | ||
120 | * bytes), e.g. a value of 2 indicates the input | ||
121 | * stream resets after every 65536 output bytes. | ||
122 | * A value of 0 indicates that the bistream never | ||
123 | * resets, such as in CAB LZX streams. | ||
124 | * @param input_buffer_size the number of bytes to use as an input | ||
125 | * bitstream buffer. | ||
126 | * @param output_length the length in bytes of the entirely | ||
127 | * decompressed output stream, if known in | ||
128 | * advance. It is used to correctly perform the | ||
129 | * Intel E8 transformation, which must stop 6 | ||
130 | * bytes before the very end of the | ||
131 | * decompressed stream. It is not otherwise used | ||
132 | * or adhered to. If the full decompressed | ||
133 | * length is known in advance, set it here. | ||
134 | * If it is NOT known, use the value 0, and call | ||
135 | * lzxd_set_output_length() once it is | ||
136 | * known. If never set, 4 of the final 6 bytes | ||
137 | * of the output stream may be incorrect. | ||
138 | * @return a pointer to an initialised lzxd_stream structure, or NULL if | ||
139 | * there was not enough memory or parameters to the function were wrong. | ||
140 | */ | ||
141 | extern struct lzxd_stream *lzxd_init(struct mspack_system *system, | ||
142 | struct mspack_file *input, | ||
143 | struct mspack_file *output, | ||
144 | int window_bits, | ||
145 | int reset_interval, | ||
146 | int input_buffer_size, | ||
147 | off_t output_length); | ||
148 | |||
149 | /* see description of output_length in lzxd_init() */ | ||
150 | extern void lzxd_set_output_length(struct lzxd_stream *lzx, | ||
151 | off_t output_length); | ||
152 | |||
153 | /** | ||
154 | * Decompresses entire or partial LZX streams. | ||
155 | * | ||
156 | * The number of bytes of data that should be decompressed is given as the | ||
157 | * out_bytes parameter. If more bytes are decoded than are needed, they | ||
158 | * will be kept over for a later invocation. | ||
159 | * | ||
160 | * The output bytes will be passed to the system->write() function given in | ||
161 | * lzxd_init(), using the output file handle given in lzxd_init(). More than | ||
162 | * one call may be made to system->write(). | ||
163 | |||
164 | * Input bytes will be read in as necessary using the system->read() | ||
165 | * function given in lzxd_init(), using the input file handle given in | ||
166 | * lzxd_init(). This will continue until system->read() returns 0 bytes, | ||
167 | * or an error. Errors will be passed out of the function as | ||
168 | * MSPACK_ERR_READ errors. Input streams should convey an "end of input | ||
169 | * stream" by refusing to supply all the bytes that LZX asks for when they | ||
170 | * reach the end of the stream, rather than return an error code. | ||
171 | * | ||
172 | * If any error code other than MSPACK_ERR_OK is returned, the stream | ||
173 | * should be considered unusable and lzxd_decompress() should not be | ||
174 | * called again on this stream. | ||
175 | * | ||
176 | * @param lzx LZX decompression state, as allocated by lzxd_init(). | ||
177 | * @param out_bytes the number of bytes of data to decompress. | ||
178 | * @return an error code, or MSPACK_ERR_OK if successful | ||
179 | */ | ||
180 | extern int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes); | ||
181 | |||
182 | /** | ||
183 | * Frees all state associated with an LZX data stream. This will call | ||
184 | * system->free() using the system pointer given in lzxd_init(). | ||
185 | * | ||
186 | * @param lzx LZX decompression state to free. | ||
187 | */ | ||
188 | void lzxd_free(struct lzxd_stream *lzx); | ||
189 | |||
190 | #ifdef __cplusplus | ||
191 | } | ||
192 | #endif | ||
193 | |||
194 | #endif | ||
diff --git a/rbutil/rbutilqt/mspack/lzxc.c b/rbutil/rbutilqt/mspack/lzxc.c new file mode 100644 index 0000000000..1207a0d747 --- /dev/null +++ b/rbutil/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.h> | ||
16 | #include <lzx.h> | ||
17 | |||
18 | /* todo */ | ||
diff --git a/rbutil/rbutilqt/mspack/lzxd.c b/rbutil/rbutilqt/mspack/lzxd.c new file mode 100644 index 0000000000..cebc4c23d3 --- /dev/null +++ b/rbutil/rbutilqt/mspack/lzxd.c | |||
@@ -0,0 +1,738 @@ | |||
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 decompression implementation */ | ||
14 | |||
15 | #include <system.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 | * The format now allows for "reference data", supplied by the caller. | ||
74 | * If match offsets go further back than the number of bytes | ||
75 | * decompressed so far, that is them accessing the reference data. | ||
76 | */ | ||
77 | |||
78 | /* import bit-reading macros and code */ | ||
79 | #define BITS_TYPE struct lzxd_stream | ||
80 | #define BITS_VAR lzx | ||
81 | #define BITS_ORDER_MSB | ||
82 | #define READ_BYTES do { \ | ||
83 | unsigned char b0, b1; \ | ||
84 | READ_IF_NEEDED; b0 = *i_ptr++; \ | ||
85 | READ_IF_NEEDED; b1 = *i_ptr++; \ | ||
86 | INJECT_BITS((b1 << 8) | b0, 16); \ | ||
87 | } while (0) | ||
88 | #include <readbits.h> | ||
89 | |||
90 | /* import huffman-reading macros and code */ | ||
91 | #define TABLEBITS(tbl) LZX_##tbl##_TABLEBITS | ||
92 | #define MAXSYMBOLS(tbl) LZX_##tbl##_MAXSYMBOLS | ||
93 | #define HUFF_TABLE(tbl,idx) lzx->tbl##_table[idx] | ||
94 | #define HUFF_LEN(tbl,idx) lzx->tbl##_len[idx] | ||
95 | #define HUFF_ERROR return lzx->error = MSPACK_ERR_DECRUNCH | ||
96 | #include <readhuff.h> | ||
97 | |||
98 | /* BUILD_TABLE(tbl) builds a huffman lookup table from code lengths */ | ||
99 | #define BUILD_TABLE(tbl) \ | ||
100 | if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ | ||
101 | &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ | ||
102 | { \ | ||
103 | D(("failed to build %s table", #tbl)) \ | ||
104 | return lzx->error = MSPACK_ERR_DECRUNCH; \ | ||
105 | } | ||
106 | |||
107 | #define BUILD_TABLE_MAYBE_EMPTY(tbl) do { \ | ||
108 | lzx->tbl##_empty = 0; \ | ||
109 | if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ | ||
110 | &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ | ||
111 | { \ | ||
112 | for (i = 0; i < MAXSYMBOLS(tbl); i++) { \ | ||
113 | if (HUFF_LEN(tbl, i) > 0) { \ | ||
114 | D(("failed to build %s table", #tbl)) \ | ||
115 | return lzx->error = MSPACK_ERR_DECRUNCH; \ | ||
116 | } \ | ||
117 | } \ | ||
118 | /* empty tree - allow it, but don't decode symbols with it */ \ | ||
119 | lzx->tbl##_empty = 1; \ | ||
120 | } \ | ||
121 | } while (0) | ||
122 | |||
123 | /* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols | ||
124 | * first to last in the given table. The code lengths are stored in their | ||
125 | * own special LZX way. | ||
126 | */ | ||
127 | #define READ_LENGTHS(tbl, first, last) do { \ | ||
128 | STORE_BITS; \ | ||
129 | if (lzxd_read_lens(lzx, &HUFF_LEN(tbl, 0), (first), \ | ||
130 | (unsigned int)(last))) return lzx->error; \ | ||
131 | RESTORE_BITS; \ | ||
132 | } while (0) | ||
133 | |||
134 | static int lzxd_read_lens(struct lzxd_stream *lzx, unsigned char *lens, | ||
135 | unsigned int first, unsigned int last) | ||
136 | { | ||
137 | /* bit buffer and huffman symbol decode variables */ | ||
138 | register unsigned int bit_buffer; | ||
139 | register int bits_left, i; | ||
140 | register unsigned short sym; | ||
141 | unsigned char *i_ptr, *i_end; | ||
142 | |||
143 | unsigned int x, y; | ||
144 | int z; | ||
145 | |||
146 | RESTORE_BITS; | ||
147 | |||
148 | /* read lengths for pretree (20 symbols, lengths stored in fixed 4 bits) */ | ||
149 | for (x = 0; x < 20; x++) { | ||
150 | READ_BITS(y, 4); | ||
151 | lzx->PRETREE_len[x] = y; | ||
152 | } | ||
153 | BUILD_TABLE(PRETREE); | ||
154 | |||
155 | for (x = first; x < last; ) { | ||
156 | READ_HUFFSYM(PRETREE, z); | ||
157 | if (z == 17) { | ||
158 | /* code = 17, run of ([read 4 bits]+4) zeros */ | ||
159 | READ_BITS(y, 4); y += 4; | ||
160 | while (y--) lens[x++] = 0; | ||
161 | } | ||
162 | else if (z == 18) { | ||
163 | /* code = 18, run of ([read 5 bits]+20) zeros */ | ||
164 | READ_BITS(y, 5); y += 20; | ||
165 | while (y--) lens[x++] = 0; | ||
166 | } | ||
167 | else if (z == 19) { | ||
168 | /* code = 19, run of ([read 1 bit]+4) [read huffman symbol] */ | ||
169 | READ_BITS(y, 1); y += 4; | ||
170 | READ_HUFFSYM(PRETREE, z); | ||
171 | z = lens[x] - z; if (z < 0) z += 17; | ||
172 | while (y--) lens[x++] = z; | ||
173 | } | ||
174 | else { | ||
175 | /* code = 0 to 16, delta current length entry */ | ||
176 | z = lens[x] - z; if (z < 0) z += 17; | ||
177 | lens[x++] = z; | ||
178 | } | ||
179 | } | ||
180 | |||
181 | STORE_BITS; | ||
182 | |||
183 | return MSPACK_ERR_OK; | ||
184 | } | ||
185 | |||
186 | /* LZX static data tables: | ||
187 | * | ||
188 | * LZX uses 'position slots' to represent match offsets. For every match, | ||
189 | * a small 'position slot' number and a small offset from that slot are | ||
190 | * encoded instead of one large offset. | ||
191 | * | ||
192 | * position_base[] is an index to the position slot bases | ||
193 | * | ||
194 | * extra_bits[] states how many bits of offset-from-base data is needed. | ||
195 | * | ||
196 | * They are generated like so: | ||
197 | * for (i = 0; i < 4; i++) extra_bits[i] = 0; | ||
198 | * for (i = 4, j = 0; i < 36; i+=2) extra_bits[i] = extra_bits[i+1] = j++; | ||
199 | * for (i = 36; i < 51; i++) extra_bits[i] = 17; | ||
200 | * for (i = 0, j = 0; i < 51; j += 1 << extra_bits[i++]) position_base[i] = j; | ||
201 | */ | ||
202 | static const unsigned int position_base[51] = { | ||
203 | 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, | ||
204 | 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, | ||
205 | 16384, 24576, 32768, 49152, 65536, 98304, 131072, 196608, 262144, | ||
206 | 393216, 524288, 655360, 786432, 917504, 1048576, 1179648, 1310720, | ||
207 | 1441792, 1572864, 1703936, 1835008, 1966080, 2097152 | ||
208 | }; | ||
209 | static const unsigned char extra_bits[51] = { | ||
210 | 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, | ||
211 | 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, | ||
212 | 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 | ||
213 | }; | ||
214 | |||
215 | static void lzxd_reset_state(struct lzxd_stream *lzx) { | ||
216 | int i; | ||
217 | |||
218 | lzx->R0 = 1; | ||
219 | lzx->R1 = 1; | ||
220 | lzx->R2 = 1; | ||
221 | lzx->header_read = 0; | ||
222 | lzx->block_remaining = 0; | ||
223 | lzx->block_type = LZX_BLOCKTYPE_INVALID; | ||
224 | |||
225 | /* initialise tables to 0 (because deltas will be applied to them) */ | ||
226 | for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) lzx->MAINTREE_len[i] = 0; | ||
227 | for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) lzx->LENGTH_len[i] = 0; | ||
228 | } | ||
229 | |||
230 | /*-------- main LZX code --------*/ | ||
231 | |||
232 | struct lzxd_stream *lzxd_init(struct mspack_system *system, | ||
233 | struct mspack_file *input, | ||
234 | struct mspack_file *output, | ||
235 | int window_bits, | ||
236 | int reset_interval, | ||
237 | int input_buffer_size, | ||
238 | off_t output_length) | ||
239 | { | ||
240 | unsigned int window_size = 1 << window_bits; | ||
241 | struct lzxd_stream *lzx; | ||
242 | |||
243 | if (!system) return NULL; | ||
244 | |||
245 | /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */ | ||
246 | if (window_bits < 15 || window_bits > 21) return NULL; | ||
247 | |||
248 | input_buffer_size = (input_buffer_size + 1) & -2; | ||
249 | if (!input_buffer_size) return NULL; | ||
250 | |||
251 | /* allocate decompression state */ | ||
252 | if (!(lzx = (struct lzxd_stream *) system->alloc(system, sizeof(struct lzxd_stream)))) { | ||
253 | return NULL; | ||
254 | } | ||
255 | |||
256 | /* allocate decompression window and input buffer */ | ||
257 | lzx->window = (unsigned char *) system->alloc(system, (size_t) window_size); | ||
258 | lzx->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size); | ||
259 | if (!lzx->window || !lzx->inbuf) { | ||
260 | system->free(lzx->window); | ||
261 | system->free(lzx->inbuf); | ||
262 | system->free(lzx); | ||
263 | return NULL; | ||
264 | } | ||
265 | |||
266 | /* initialise decompression state */ | ||
267 | lzx->sys = system; | ||
268 | lzx->input = input; | ||
269 | lzx->output = output; | ||
270 | lzx->offset = 0; | ||
271 | lzx->length = output_length; | ||
272 | |||
273 | lzx->inbuf_size = input_buffer_size; | ||
274 | lzx->window_size = 1 << window_bits; | ||
275 | lzx->window_posn = 0; | ||
276 | lzx->frame_posn = 0; | ||
277 | lzx->frame = 0; | ||
278 | lzx->reset_interval = reset_interval; | ||
279 | lzx->intel_filesize = 0; | ||
280 | lzx->intel_curpos = 0; | ||
281 | lzx->intel_started = 0; | ||
282 | lzx->error = MSPACK_ERR_OK; | ||
283 | |||
284 | /* window bits: 15 16 17 18 19 20 21 | ||
285 | * position slots: 30 32 34 36 38 42 50 */ | ||
286 | lzx->posn_slots = ((window_bits == 21) ? 50 : | ||
287 | ((window_bits == 20) ? 42 : (window_bits << 1))); | ||
288 | |||
289 | lzx->o_ptr = lzx->o_end = &lzx->e8_buf[0]; | ||
290 | lzxd_reset_state(lzx); | ||
291 | INIT_BITS; | ||
292 | return lzx; | ||
293 | } | ||
294 | |||
295 | void lzxd_set_output_length(struct lzxd_stream *lzx, off_t out_bytes) { | ||
296 | if (lzx) lzx->length = out_bytes; | ||
297 | } | ||
298 | |||
299 | int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { | ||
300 | /* bitstream and huffman reading variables */ | ||
301 | register unsigned int bit_buffer; | ||
302 | register int bits_left, i=0; | ||
303 | unsigned char *i_ptr, *i_end; | ||
304 | register unsigned short sym; | ||
305 | |||
306 | int match_length, length_footer, extra, verbatim_bits, bytes_todo; | ||
307 | int this_run, main_element, aligned_bits, j; | ||
308 | unsigned char *window, *runsrc, *rundest, buf[12]; | ||
309 | unsigned int frame_size=0, end_frame, match_offset, window_posn; | ||
310 | unsigned int R0, R1, R2; | ||
311 | |||
312 | /* easy answers */ | ||
313 | if (!lzx || (out_bytes < 0)) return MSPACK_ERR_ARGS; | ||
314 | if (lzx->error) return lzx->error; | ||
315 | |||
316 | /* flush out any stored-up bytes before we begin */ | ||
317 | i = lzx->o_end - lzx->o_ptr; | ||
318 | if ((off_t) i > out_bytes) i = (int) out_bytes; | ||
319 | if (i) { | ||
320 | if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) { | ||
321 | return lzx->error = MSPACK_ERR_WRITE; | ||
322 | } | ||
323 | lzx->o_ptr += i; | ||
324 | lzx->offset += i; | ||
325 | out_bytes -= i; | ||
326 | } | ||
327 | if (out_bytes == 0) return MSPACK_ERR_OK; | ||
328 | |||
329 | /* restore local state */ | ||
330 | RESTORE_BITS; | ||
331 | window = lzx->window; | ||
332 | window_posn = lzx->window_posn; | ||
333 | R0 = lzx->R0; | ||
334 | R1 = lzx->R1; | ||
335 | R2 = lzx->R2; | ||
336 | |||
337 | end_frame = (unsigned int)((lzx->offset + out_bytes) / LZX_FRAME_SIZE) + 1; | ||
338 | |||
339 | while (lzx->frame < end_frame) { | ||
340 | /* have we reached the reset interval? (if there is one?) */ | ||
341 | if (lzx->reset_interval && ((lzx->frame % lzx->reset_interval) == 0)) { | ||
342 | if (lzx->block_remaining) { | ||
343 | D(("%d bytes remaining at reset interval", lzx->block_remaining)) | ||
344 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
345 | } | ||
346 | |||
347 | /* re-read the intel header and reset the huffman lengths */ | ||
348 | lzxd_reset_state(lzx); | ||
349 | R0 = lzx->R0; | ||
350 | R1 = lzx->R1; | ||
351 | R2 = lzx->R2; | ||
352 | } | ||
353 | |||
354 | /* read header if necessary */ | ||
355 | if (!lzx->header_read) { | ||
356 | /* read 1 bit. if bit=0, intel filesize = 0. | ||
357 | * if bit=1, read intel filesize (32 bits) */ | ||
358 | j = 0; READ_BITS(i, 1); if (i) { READ_BITS(i, 16); READ_BITS(j, 16); } | ||
359 | lzx->intel_filesize = (i << 16) | j; | ||
360 | lzx->header_read = 1; | ||
361 | } | ||
362 | |||
363 | /* calculate size of frame: all frames are 32k except the final frame | ||
364 | * which is 32kb or less. this can only be calculated when lzx->length | ||
365 | * has been filled in. */ | ||
366 | frame_size = LZX_FRAME_SIZE; | ||
367 | if (lzx->length && (lzx->length - lzx->offset) < (off_t)frame_size) { | ||
368 | frame_size = lzx->length - lzx->offset; | ||
369 | } | ||
370 | |||
371 | /* decode until one more frame is available */ | ||
372 | bytes_todo = lzx->frame_posn + frame_size - window_posn; | ||
373 | while (bytes_todo > 0) { | ||
374 | /* initialise new block, if one is needed */ | ||
375 | if (lzx->block_remaining == 0) { | ||
376 | /* realign if previous block was an odd-sized UNCOMPRESSED block */ | ||
377 | if ((lzx->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) && | ||
378 | (lzx->block_length & 1)) | ||
379 | { | ||
380 | READ_IF_NEEDED; | ||
381 | i_ptr++; | ||
382 | } | ||
383 | |||
384 | /* read block type (3 bits) and block length (24 bits) */ | ||
385 | READ_BITS(lzx->block_type, 3); | ||
386 | READ_BITS(i, 16); READ_BITS(j, 8); | ||
387 | lzx->block_remaining = lzx->block_length = (i << 8) | j; | ||
388 | /*D(("new block t%d len %u", lzx->block_type, lzx->block_length))*/ | ||
389 | |||
390 | /* read individual block headers */ | ||
391 | switch (lzx->block_type) { | ||
392 | case LZX_BLOCKTYPE_ALIGNED: | ||
393 | /* read lengths of and build aligned huffman decoding tree */ | ||
394 | for (i = 0; i < 8; i++) { READ_BITS(j, 3); lzx->ALIGNED_len[i] = j; } | ||
395 | BUILD_TABLE(ALIGNED); | ||
396 | /* no break -- rest of aligned header is same as verbatim */ | ||
397 | case LZX_BLOCKTYPE_VERBATIM: | ||
398 | /* read lengths of and build main huffman decoding tree */ | ||
399 | READ_LENGTHS(MAINTREE, 0, 256); | ||
400 | READ_LENGTHS(MAINTREE, 256, LZX_NUM_CHARS + (lzx->posn_slots << 3)); | ||
401 | BUILD_TABLE(MAINTREE); | ||
402 | /* if the literal 0xE8 is anywhere in the block... */ | ||
403 | if (lzx->MAINTREE_len[0xE8] != 0) lzx->intel_started = 1; | ||
404 | /* read lengths of and build lengths huffman decoding tree */ | ||
405 | READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS); | ||
406 | BUILD_TABLE_MAYBE_EMPTY(LENGTH); | ||
407 | break; | ||
408 | |||
409 | case LZX_BLOCKTYPE_UNCOMPRESSED: | ||
410 | /* because we can't assume otherwise */ | ||
411 | lzx->intel_started = 1; | ||
412 | |||
413 | /* read 1-16 (not 0-15) bits to align to bytes */ | ||
414 | ENSURE_BITS(16); | ||
415 | if (bits_left > 16) i_ptr -= 2; | ||
416 | bits_left = 0; bit_buffer = 0; | ||
417 | |||
418 | /* read 12 bytes of stored R0 / R1 / R2 values */ | ||
419 | for (rundest = &buf[0], i = 0; i < 12; i++) { | ||
420 | READ_IF_NEEDED; | ||
421 | *rundest++ = *i_ptr++; | ||
422 | } | ||
423 | R0 = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); | ||
424 | R1 = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); | ||
425 | R2 = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24); | ||
426 | break; | ||
427 | |||
428 | default: | ||
429 | D(("bad block type")) | ||
430 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
431 | } | ||
432 | } | ||
433 | |||
434 | /* decode more of the block: | ||
435 | * run = min(what's available, what's needed) */ | ||
436 | this_run = lzx->block_remaining; | ||
437 | if (this_run > bytes_todo) this_run = bytes_todo; | ||
438 | |||
439 | /* assume we decode exactly this_run bytes, for now */ | ||
440 | bytes_todo -= this_run; | ||
441 | lzx->block_remaining -= this_run; | ||
442 | |||
443 | /* decode at least this_run bytes */ | ||
444 | switch (lzx->block_type) { | ||
445 | case LZX_BLOCKTYPE_VERBATIM: | ||
446 | while (this_run > 0) { | ||
447 | READ_HUFFSYM(MAINTREE, main_element); | ||
448 | if (main_element < LZX_NUM_CHARS) { | ||
449 | /* literal: 0 to LZX_NUM_CHARS-1 */ | ||
450 | window[window_posn++] = main_element; | ||
451 | this_run--; | ||
452 | } | ||
453 | else { | ||
454 | /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ | ||
455 | main_element -= LZX_NUM_CHARS; | ||
456 | |||
457 | /* get match length */ | ||
458 | match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; | ||
459 | if (match_length == LZX_NUM_PRIMARY_LENGTHS) { | ||
460 | if (lzx->LENGTH_empty) { | ||
461 | D(("LENGTH symbol needed but tree is empty")) | ||
462 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
463 | } | ||
464 | READ_HUFFSYM(LENGTH, length_footer); | ||
465 | match_length += length_footer; | ||
466 | } | ||
467 | match_length += LZX_MIN_MATCH; | ||
468 | |||
469 | /* get match offset */ | ||
470 | switch ((match_offset = (main_element >> 3))) { | ||
471 | case 0: match_offset = R0; break; | ||
472 | case 1: match_offset = R1; R1=R0; R0 = match_offset; break; | ||
473 | case 2: match_offset = R2; R2=R0; R0 = match_offset; break; | ||
474 | case 3: match_offset = 1; R2=R1; R1=R0; R0 = match_offset; break; | ||
475 | default: | ||
476 | extra = extra_bits[match_offset]; | ||
477 | READ_BITS(verbatim_bits, extra); | ||
478 | match_offset = position_base[match_offset] - 2 + verbatim_bits; | ||
479 | R2 = R1; R1 = R0; R0 = match_offset; | ||
480 | } | ||
481 | |||
482 | if ((window_posn + match_length) > lzx->window_size) { | ||
483 | D(("match ran over window wrap")) | ||
484 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
485 | } | ||
486 | |||
487 | /* copy match */ | ||
488 | rundest = &window[window_posn]; | ||
489 | i = match_length; | ||
490 | /* does match offset wrap the window? */ | ||
491 | if (match_offset > window_posn) { | ||
492 | /* j = length from match offset to end of window */ | ||
493 | j = match_offset - window_posn; | ||
494 | if (j > (int) lzx->window_size) { | ||
495 | D(("match offset beyond window boundaries")) | ||
496 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
497 | } | ||
498 | runsrc = &window[lzx->window_size - j]; | ||
499 | if (j < i) { | ||
500 | /* if match goes over the window edge, do two copy runs */ | ||
501 | i -= j; while (j-- > 0) *rundest++ = *runsrc++; | ||
502 | runsrc = window; | ||
503 | } | ||
504 | while (i-- > 0) *rundest++ = *runsrc++; | ||
505 | } | ||
506 | else { | ||
507 | runsrc = rundest - match_offset; | ||
508 | while (i-- > 0) *rundest++ = *runsrc++; | ||
509 | } | ||
510 | |||
511 | this_run -= match_length; | ||
512 | window_posn += match_length; | ||
513 | } | ||
514 | } /* while (this_run > 0) */ | ||
515 | break; | ||
516 | |||
517 | case LZX_BLOCKTYPE_ALIGNED: | ||
518 | while (this_run > 0) { | ||
519 | READ_HUFFSYM(MAINTREE, main_element); | ||
520 | if (main_element < LZX_NUM_CHARS) { | ||
521 | /* literal: 0 to LZX_NUM_CHARS-1 */ | ||
522 | window[window_posn++] = main_element; | ||
523 | this_run--; | ||
524 | } | ||
525 | else { | ||
526 | /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ | ||
527 | main_element -= LZX_NUM_CHARS; | ||
528 | |||
529 | /* get match length */ | ||
530 | match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; | ||
531 | if (match_length == LZX_NUM_PRIMARY_LENGTHS) { | ||
532 | if (lzx->LENGTH_empty) { | ||
533 | D(("LENGTH symbol needed but tree is empty")) | ||
534 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
535 | } | ||
536 | READ_HUFFSYM(LENGTH, length_footer); | ||
537 | match_length += length_footer; | ||
538 | } | ||
539 | match_length += LZX_MIN_MATCH; | ||
540 | |||
541 | /* get match offset */ | ||
542 | switch ((match_offset = (main_element >> 3))) { | ||
543 | case 0: match_offset = R0; break; | ||
544 | case 1: match_offset = R1; R1 = R0; R0 = match_offset; break; | ||
545 | case 2: match_offset = R2; R2 = R0; R0 = match_offset; break; | ||
546 | default: | ||
547 | extra = extra_bits[match_offset]; | ||
548 | match_offset = position_base[match_offset] - 2; | ||
549 | if (extra > 3) { | ||
550 | /* verbatim and aligned bits */ | ||
551 | extra -= 3; | ||
552 | READ_BITS(verbatim_bits, extra); | ||
553 | match_offset += (verbatim_bits << 3); | ||
554 | READ_HUFFSYM(ALIGNED, aligned_bits); | ||
555 | match_offset += aligned_bits; | ||
556 | } | ||
557 | else if (extra == 3) { | ||
558 | /* aligned bits only */ | ||
559 | READ_HUFFSYM(ALIGNED, aligned_bits); | ||
560 | match_offset += aligned_bits; | ||
561 | } | ||
562 | else if (extra > 0) { /* extra==1, extra==2 */ | ||
563 | /* verbatim bits only */ | ||
564 | READ_BITS(verbatim_bits, extra); | ||
565 | match_offset += verbatim_bits; | ||
566 | } | ||
567 | else /* extra == 0 */ { | ||
568 | /* ??? not defined in LZX specification! */ | ||
569 | match_offset = 1; | ||
570 | } | ||
571 | /* update repeated offset LRU queue */ | ||
572 | R2 = R1; R1 = R0; R0 = match_offset; | ||
573 | } | ||
574 | |||
575 | if ((window_posn + match_length) > lzx->window_size) { | ||
576 | D(("match ran over window wrap")) | ||
577 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
578 | } | ||
579 | |||
580 | /* copy match */ | ||
581 | rundest = &window[window_posn]; | ||
582 | i = match_length; | ||
583 | /* does match offset wrap the window? */ | ||
584 | if (match_offset > window_posn) { | ||
585 | /* j = length from match offset to end of window */ | ||
586 | j = match_offset - window_posn; | ||
587 | if (j > (int) lzx->window_size) { | ||
588 | D(("match offset beyond window boundaries")) | ||
589 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
590 | } | ||
591 | runsrc = &window[lzx->window_size - j]; | ||
592 | if (j < i) { | ||
593 | /* if match goes over the window edge, do two copy runs */ | ||
594 | i -= j; while (j-- > 0) *rundest++ = *runsrc++; | ||
595 | runsrc = window; | ||
596 | } | ||
597 | while (i-- > 0) *rundest++ = *runsrc++; | ||
598 | } | ||
599 | else { | ||
600 | runsrc = rundest - match_offset; | ||
601 | while (i-- > 0) *rundest++ = *runsrc++; | ||
602 | } | ||
603 | |||
604 | this_run -= match_length; | ||
605 | window_posn += match_length; | ||
606 | } | ||
607 | } /* while (this_run > 0) */ | ||
608 | break; | ||
609 | |||
610 | case LZX_BLOCKTYPE_UNCOMPRESSED: | ||
611 | /* as this_run is limited not to wrap a frame, this also means it | ||
612 | * won't wrap the window (as the window is a multiple of 32k) */ | ||
613 | rundest = &window[window_posn]; | ||
614 | window_posn += this_run; | ||
615 | while (this_run > 0) { | ||
616 | if ((i = i_end - i_ptr) == 0) { | ||
617 | READ_IF_NEEDED; | ||
618 | } | ||
619 | else { | ||
620 | if (i > this_run) i = this_run; | ||
621 | lzx->sys->copy(i_ptr, rundest, (size_t) i); | ||
622 | rundest += i; | ||
623 | i_ptr += i; | ||
624 | this_run -= i; | ||
625 | } | ||
626 | } | ||
627 | break; | ||
628 | |||
629 | default: | ||
630 | return lzx->error = MSPACK_ERR_DECRUNCH; /* might as well */ | ||
631 | } | ||
632 | |||
633 | /* did the final match overrun our desired this_run length? */ | ||
634 | if (this_run < 0) { | ||
635 | if ((unsigned int)(-this_run) > lzx->block_remaining) { | ||
636 | D(("overrun went past end of block by %d (%d remaining)", | ||
637 | -this_run, lzx->block_remaining )) | ||
638 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
639 | } | ||
640 | lzx->block_remaining -= -this_run; | ||
641 | } | ||
642 | } /* while (bytes_todo > 0) */ | ||
643 | |||
644 | /* streams don't extend over frame boundaries */ | ||
645 | if ((window_posn - lzx->frame_posn) != frame_size) { | ||
646 | D(("decode beyond output frame limits! %d != %d", | ||
647 | window_posn - lzx->frame_posn, frame_size)) | ||
648 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
649 | } | ||
650 | |||
651 | /* re-align input bitstream */ | ||
652 | if (bits_left > 0) ENSURE_BITS(16); | ||
653 | if (bits_left & 15) REMOVE_BITS(bits_left & 15); | ||
654 | |||
655 | /* check that we've used all of the previous frame first */ | ||
656 | if (lzx->o_ptr != lzx->o_end) { | ||
657 | D(("%ld avail bytes, new %d frame", lzx->o_end-lzx->o_ptr, frame_size)) | ||
658 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
659 | } | ||
660 | |||
661 | /* does this intel block _really_ need decoding? */ | ||
662 | if (lzx->intel_started && lzx->intel_filesize && | ||
663 | (lzx->frame <= 32768) && (frame_size > 10)) | ||
664 | { | ||
665 | unsigned char *data = &lzx->e8_buf[0]; | ||
666 | unsigned char *dataend = &lzx->e8_buf[frame_size - 10]; | ||
667 | signed int curpos = lzx->intel_curpos; | ||
668 | signed int filesize = lzx->intel_filesize; | ||
669 | signed int abs_off, rel_off; | ||
670 | |||
671 | /* copy e8 block to the e8 buffer and tweak if needed */ | ||
672 | lzx->o_ptr = data; | ||
673 | lzx->sys->copy(&lzx->window[lzx->frame_posn], data, frame_size); | ||
674 | |||
675 | while (data < dataend) { | ||
676 | if (*data++ != 0xE8) { curpos++; continue; } | ||
677 | abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); | ||
678 | if ((abs_off >= -curpos) && (abs_off < filesize)) { | ||
679 | rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; | ||
680 | data[0] = (unsigned char) rel_off; | ||
681 | data[1] = (unsigned char) (rel_off >> 8); | ||
682 | data[2] = (unsigned char) (rel_off >> 16); | ||
683 | data[3] = (unsigned char) (rel_off >> 24); | ||
684 | } | ||
685 | data += 4; | ||
686 | curpos += 5; | ||
687 | } | ||
688 | lzx->intel_curpos += frame_size; | ||
689 | } | ||
690 | else { | ||
691 | lzx->o_ptr = &lzx->window[lzx->frame_posn]; | ||
692 | if (lzx->intel_filesize) lzx->intel_curpos += frame_size; | ||
693 | } | ||
694 | lzx->o_end = &lzx->o_ptr[frame_size]; | ||
695 | |||
696 | /* write a frame */ | ||
697 | i = (out_bytes < (off_t)frame_size) ? (unsigned int)out_bytes : frame_size; | ||
698 | if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) { | ||
699 | return lzx->error = MSPACK_ERR_WRITE; | ||
700 | } | ||
701 | lzx->o_ptr += i; | ||
702 | lzx->offset += i; | ||
703 | out_bytes -= i; | ||
704 | |||
705 | /* advance frame start position */ | ||
706 | lzx->frame_posn += frame_size; | ||
707 | lzx->frame++; | ||
708 | |||
709 | /* wrap window / frame position pointers */ | ||
710 | if (window_posn == lzx->window_size) window_posn = 0; | ||
711 | if (lzx->frame_posn == lzx->window_size) lzx->frame_posn = 0; | ||
712 | |||
713 | } /* while (lzx->frame < end_frame) */ | ||
714 | |||
715 | if (out_bytes) { | ||
716 | D(("bytes left to output")) | ||
717 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
718 | } | ||
719 | |||
720 | /* store local state */ | ||
721 | STORE_BITS; | ||
722 | lzx->window_posn = window_posn; | ||
723 | lzx->R0 = R0; | ||
724 | lzx->R1 = R1; | ||
725 | lzx->R2 = R2; | ||
726 | |||
727 | return MSPACK_ERR_OK; | ||
728 | } | ||
729 | |||
730 | void lzxd_free(struct lzxd_stream *lzx) { | ||
731 | struct mspack_system *sys; | ||
732 | if (lzx) { | ||
733 | sys = lzx->sys; | ||
734 | sys->free(lzx->inbuf); | ||
735 | sys->free(lzx->window); | ||
736 | sys->free(lzx); | ||
737 | } | ||
738 | } | ||
diff --git a/rbutil/rbutilqt/mspack/mspack.h b/rbutil/rbutilqt/mspack/mspack.h new file mode 100644 index 0000000000..7f6bdf1465 --- /dev/null +++ b/rbutil/rbutilqt/mspack/mspack.h | |||
@@ -0,0 +1,2203 @@ | |||
1 | /* libmspack -- a library for working with Microsoft compression formats. | ||
2 | * (C) 2003-2011 Stuart Caie <kyzer@4u.net> | ||
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 | * | ||
34 | * To determine the capabilities of the library, and the binary | ||
35 | * compatibility version of any particular compressor or decompressor, use | ||
36 | * the mspack_version() function. The UNIX library interface version is | ||
37 | * defined as the highest-versioned library component. | ||
38 | * | ||
39 | * \section starting Getting started | ||
40 | * | ||
41 | * The macro MSPACK_SYS_SELFTEST() should be used to ensure the library can | ||
42 | * be used. In particular, it checks if the caller is using 32-bit file I/O | ||
43 | * when the library is compiled for 64-bit file I/O and vice versa. | ||
44 | * | ||
45 | * If compiled normally, the library includes basic file I/O and memory | ||
46 | * management functionality using the standard C library. This can be | ||
47 | * customised and replaced entirely by creating a mspack_system structure. | ||
48 | * | ||
49 | * A compressor or decompressor for the required format must be | ||
50 | * instantiated before it can be used. Each construction function takes | ||
51 | * one parameter, which is either a pointer to a custom mspack_system | ||
52 | * structure, or NULL to use the default. The instantiation returned, if | ||
53 | * not NULL, contains function pointers (methods) to work with the given | ||
54 | * file format. | ||
55 | * | ||
56 | * For compression: | ||
57 | * - mspack_create_cab_compressor() creates a mscab_compressor | ||
58 | * - mspack_create_chm_compressor() creates a mschm_compressor | ||
59 | * - mspack_create_lit_compressor() creates a mslit_compressor | ||
60 | * - mspack_create_hlp_compressor() creates a mshlp_compressor | ||
61 | * - mspack_create_szdd_compressor() creates a msszdd_compressor | ||
62 | * - mspack_create_kwaj_compressor() creates a mskwaj_compressor | ||
63 | * | ||
64 | * For decompression: | ||
65 | * - mspack_create_cab_decompressor() creates a mscab_decompressor | ||
66 | * - mspack_create_chm_decompressor() creates a mschm_decompressor | ||
67 | * - mspack_create_lit_decompressor() creates a mslit_decompressor | ||
68 | * - mspack_create_hlp_decompressor() creates a mshlp_decompressor | ||
69 | * - mspack_create_szdd_decompressor() creates a msszdd_decompressor | ||
70 | * - mspack_create_kwaj_decompressor() creates a mskwaj_decompressor | ||
71 | * | ||
72 | * Once finished working with a format, each kind of | ||
73 | * compressor/decompressor has its own specific destructor: | ||
74 | * - mspack_destroy_cab_compressor() | ||
75 | * - mspack_destroy_cab_decompressor() | ||
76 | * - mspack_destroy_chm_compressor() | ||
77 | * - mspack_destroy_chm_decompressor() | ||
78 | * - mspack_destroy_lit_compressor() | ||
79 | * - mspack_destroy_lit_decompressor() | ||
80 | * - mspack_destroy_hlp_compressor() | ||
81 | * - mspack_destroy_hlp_decompressor() | ||
82 | * - mspack_destroy_szdd_compressor() | ||
83 | * - mspack_destroy_szdd_decompressor() | ||
84 | * - mspack_destroy_kwaj_compressor() | ||
85 | * - mspack_destroy_kwaj_decompressor() | ||
86 | * | ||
87 | * Destroying a compressor or decompressor does not destroy any objects, | ||
88 | * structures or handles that have been created using that compressor or | ||
89 | * decompressor. Ensure that everything created or opened is destroyed or | ||
90 | * closed before compressor/decompressor is itself destroyed. | ||
91 | * | ||
92 | * \section errors Error codes | ||
93 | * | ||
94 | * All compressors and decompressors use the same set of error codes. Most | ||
95 | * methods return an error code directly. For methods which do not | ||
96 | * return error codes directly, the error code can be obtained with the | ||
97 | * last_error() method. | ||
98 | * | ||
99 | * - #MSPACK_ERR_OK is used to indicate success. This error code is defined | ||
100 | * as zero, all other code are non-zero. | ||
101 | * - #MSPACK_ERR_ARGS indicates that a method was called with inappropriate | ||
102 | * arguments. | ||
103 | * - #MSPACK_ERR_OPEN indicates that mspack_system::open() failed. | ||
104 | * - #MSPACK_ERR_READ indicates that mspack_system::read() failed. | ||
105 | * - #MSPACK_ERR_WRITE indicates that mspack_system::write() failed. | ||
106 | * - #MSPACK_ERR_SEEK indicates that mspack_system::seek() failed. | ||
107 | * - #MSPACK_ERR_NOMEMORY indicates that mspack_system::alloc() failed. | ||
108 | * - #MSPACK_ERR_SIGNATURE indicates that the file being read does not | ||
109 | * have the correct "signature". It is probably not a valid file for | ||
110 | * whatever format is being read. | ||
111 | * - #MSPACK_ERR_DATAFORMAT indicates that the file being used or read | ||
112 | * is corrupt. | ||
113 | * - #MSPACK_ERR_CHECKSUM indicates that a data checksum has failed. | ||
114 | * - #MSPACK_ERR_CRUNCH indicates an error occured during compression. | ||
115 | * - #MSPACK_ERR_DECRUNCH indicates an error occured during decompression. | ||
116 | * | ||
117 | * \section threading Multi-threading | ||
118 | * | ||
119 | * libmspack methods are reentrant and multithreading-safe when each | ||
120 | * thread has its own compressor or decompressor. | ||
121 | |||
122 | * You should not call multiple methods simultaneously on a single | ||
123 | * compressor or decompressor instance. | ||
124 | * | ||
125 | * If this may happen, you can either use one compressor or | ||
126 | * decompressor per thread, or you can use your preferred lock, | ||
127 | * semaphore or mutex library to ensure no more than one method on a | ||
128 | * compressor/decompressor is called simultaneously. libmspack will | ||
129 | * not do this locking for you. | ||
130 | * | ||
131 | * Example of incorrect behaviour: | ||
132 | * - thread 1 calls mspack_create_cab_decompressor() | ||
133 | * - thread 1 calls open() | ||
134 | * - thread 1 calls extract() for one file | ||
135 | * - thread 2 simultaneously calls extract() for another file | ||
136 | * | ||
137 | * Correct behaviour: | ||
138 | * - thread 1 calls mspack_create_cab_decompressor() | ||
139 | * - thread 2 calls mspack_create_cab_decompressor() | ||
140 | * - thread 1 calls its own open() / extract() | ||
141 | * - thread 2 simultaneously calls its own open() / extract() | ||
142 | * | ||
143 | * Also correct behaviour: | ||
144 | * - thread 1 calls mspack_create_cab_decompressor() | ||
145 | * - thread 1 locks a mutex for with the decompressor before | ||
146 | * calling any methods on it, and unlocks the mutex after each | ||
147 | * method returns. | ||
148 | * - thread 1 can share the results of open() with thread 2, and both | ||
149 | * can call extract(), provided they both guard against simultaneous | ||
150 | * use of extract(), and any other methods, with the mutex | ||
151 | */ | ||
152 | |||
153 | #ifndef LIB_MSPACK_H | ||
154 | #define LIB_MSPACK_H 1 | ||
155 | |||
156 | #ifdef __cplusplus | ||
157 | extern "C" { | ||
158 | #endif | ||
159 | |||
160 | #include <sys/types.h> | ||
161 | #include <stdlib.h> | ||
162 | |||
163 | /** | ||
164 | * System self-test function, to ensure both library and calling program | ||
165 | * can use one another. | ||
166 | * | ||
167 | * A result of MSPACK_ERR_OK means the library and caller are | ||
168 | * compatible. Any other result indicates that the library and caller are | ||
169 | * not compatible and should not be used. In particular, a value of | ||
170 | * MSPACK_ERR_SEEK means the library and caller use different off_t | ||
171 | * datatypes. | ||
172 | * | ||
173 | * It should be used like so: | ||
174 | * | ||
175 | * @code | ||
176 | * int selftest_result; | ||
177 | * MSPACK_SYS_SELFTEST(selftest_result); | ||
178 | * if (selftest_result != MSPACK_ERR_OK) { | ||
179 | * fprintf(stderr, "incompatible with this build of libmspack\n"); | ||
180 | * exit(0); | ||
181 | * } | ||
182 | * @endcode | ||
183 | * | ||
184 | * @param result an int variable to store the result of the self-test | ||
185 | */ | ||
186 | #define MSPACK_SYS_SELFTEST(result) do { \ | ||
187 | (result) = mspack_sys_selftest_internal(sizeof(off_t)); \ | ||
188 | } while (0) | ||
189 | |||
190 | /** Part of the MSPACK_SYS_SELFTEST() macro, must not be used directly. */ | ||
191 | extern int mspack_sys_selftest_internal(int); | ||
192 | |||
193 | /** | ||
194 | * Enquire about the binary compatibility version of a specific interface in | ||
195 | * the library. Currently, the following interfaces are defined: | ||
196 | * | ||
197 | * - #MSPACK_VER_LIBRARY: the overall library | ||
198 | * - #MSPACK_VER_SYSTEM: the mspack_system interface | ||
199 | * - #MSPACK_VER_MSCABD: the mscab_decompressor interface | ||
200 | * - #MSPACK_VER_MSCABC: the mscab_compressor interface | ||
201 | * - #MSPACK_VER_MSCHMD: the mschm_decompressor interface | ||
202 | * - #MSPACK_VER_MSCHMC: the mschm_compressor interface | ||
203 | * - #MSPACK_VER_MSLITD: the mslit_decompressor interface | ||
204 | * - #MSPACK_VER_MSLITC: the mslit_compressor interface | ||
205 | * - #MSPACK_VER_MSHLPD: the mshlp_decompressor interface | ||
206 | * - #MSPACK_VER_MSHLPC: the mshlp_compressor interface | ||
207 | * - #MSPACK_VER_MSSZDDD: the msszdd_decompressor interface | ||
208 | * - #MSPACK_VER_MSSZDDC: the msszdd_compressor interface | ||
209 | * - #MSPACK_VER_MSKWAJD: the mskwaj_decompressor interface | ||
210 | * - #MSPACK_VER_MSKWAJC: the mskwaj_compressor interface | ||
211 | * | ||
212 | * The result of the function should be interpreted as follows: | ||
213 | * - -1: this interface is completely unknown to the library | ||
214 | * - 0: this interface is known, but non-functioning | ||
215 | * - 1: this interface has all basic functionality | ||
216 | * - 2, 3, ...: this interface has additional functionality, clearly marked | ||
217 | * in the documentation as "version 2", "version 3" and so on. | ||
218 | * | ||
219 | * @param entity the interface to request current version of | ||
220 | * @return the version of the requested interface | ||
221 | */ | ||
222 | extern int mspack_version(int entity); | ||
223 | |||
224 | /** Pass to mspack_version() to get the overall library version */ | ||
225 | #define MSPACK_VER_LIBRARY (0) | ||
226 | /** Pass to mspack_version() to get the mspack_system version */ | ||
227 | #define MSPACK_VER_SYSTEM (1) | ||
228 | /** Pass to mspack_version() to get the mscab_decompressor version */ | ||
229 | #define MSPACK_VER_MSCABD (2) | ||
230 | /** Pass to mspack_version() to get the mscab_compressor version */ | ||
231 | #define MSPACK_VER_MSCABC (3) | ||
232 | /** Pass to mspack_version() to get the mschm_decompressor version */ | ||
233 | #define MSPACK_VER_MSCHMD (4) | ||
234 | /** Pass to mspack_version() to get the mschm_compressor version */ | ||
235 | #define MSPACK_VER_MSCHMC (5) | ||
236 | /** Pass to mspack_version() to get the mslit_decompressor version */ | ||
237 | #define MSPACK_VER_MSLITD (6) | ||
238 | /** Pass to mspack_version() to get the mslit_compressor version */ | ||
239 | #define MSPACK_VER_MSLITC (7) | ||
240 | /** Pass to mspack_version() to get the mshlp_decompressor version */ | ||
241 | #define MSPACK_VER_MSHLPD (8) | ||
242 | /** Pass to mspack_version() to get the mshlp_compressor version */ | ||
243 | #define MSPACK_VER_MSHLPC (9) | ||
244 | /** Pass to mspack_version() to get the msszdd_decompressor version */ | ||
245 | #define MSPACK_VER_MSSZDDD (10) | ||
246 | /** Pass to mspack_version() to get the msszdd_compressor version */ | ||
247 | #define MSPACK_VER_MSSZDDC (11) | ||
248 | /** Pass to mspack_version() to get the mskwaj_decompressor version */ | ||
249 | #define MSPACK_VER_MSKWAJD (12) | ||
250 | /** Pass to mspack_version() to get the mskwaj_compressor version */ | ||
251 | #define MSPACK_VER_MSKWAJC (13) | ||
252 | |||
253 | /* --- file I/O abstraction ------------------------------------------------ */ | ||
254 | |||
255 | /** | ||
256 | * A structure which abstracts file I/O and memory management. | ||
257 | * | ||
258 | * The library always uses the mspack_system structure for interaction | ||
259 | * with the file system and to allocate, free and copy all memory. It also | ||
260 | * uses it to send literal messages to the library user. | ||
261 | * | ||
262 | * When the library is compiled normally, passing NULL to a compressor or | ||
263 | * decompressor constructor will result in a default mspack_system being | ||
264 | * used, where all methods are implemented with the standard C library. | ||
265 | * However, all constructors support being given a custom created | ||
266 | * mspack_system structure, with the library user's own methods. This | ||
267 | * allows for more abstract interaction, such as reading and writing files | ||
268 | * directly to memory, or from a network socket or pipe. | ||
269 | * | ||
270 | * Implementors of an mspack_system structure should read all | ||
271 | * documentation entries for every structure member, and write methods | ||
272 | * which conform to those standards. | ||
273 | */ | ||
274 | struct mspack_system { | ||
275 | /** | ||
276 | * Opens a file for reading, writing, appending or updating. | ||
277 | * | ||
278 | * @param self a self-referential pointer to the mspack_system | ||
279 | * structure whose open() method is being called. If | ||
280 | * this pointer is required by close(), read(), write(), | ||
281 | * seek() or tell(), it should be stored in the result | ||
282 | * structure at this time. | ||
283 | * @param filename the file to be opened. It is passed directly from the | ||
284 | * library caller without being modified, so it is up to | ||
285 | * the caller what this parameter actually represents. | ||
286 | * @param mode one of #MSPACK_SYS_OPEN_READ (open an existing file | ||
287 | * for reading), #MSPACK_SYS_OPEN_WRITE (open a new file | ||
288 | * for writing), #MSPACK_SYS_OPEN_UPDATE (open an existing | ||
289 | * file for reading/writing from the start of the file) or | ||
290 | * #MSPACK_SYS_OPEN_APPEND (open an existing file for | ||
291 | * reading/writing from the end of the file) | ||
292 | * @return a pointer to a mspack_file structure. This structure officially | ||
293 | * contains no members, its true contents are up to the | ||
294 | * mspack_system implementor. It should contain whatever is needed | ||
295 | * for other mspack_system methods to operate. Returning the NULL | ||
296 | * pointer indicates an error condition. | ||
297 | * @see close(), read(), write(), seek(), tell(), message() | ||
298 | */ | ||
299 | struct mspack_file * (*open)(struct mspack_system *self, | ||
300 | const char *filename, | ||
301 | int mode); | ||
302 | |||
303 | /** | ||
304 | * Closes a previously opened file. If any memory was allocated for this | ||
305 | * particular file handle, it should be freed at this time. | ||
306 | * | ||
307 | * @param file the file to close | ||
308 | * @see open() | ||
309 | */ | ||
310 | void (*close)(struct mspack_file *file); | ||
311 | |||
312 | /** | ||
313 | * Reads a given number of bytes from an open file. | ||
314 | * | ||
315 | * @param file the file to read from | ||
316 | * @param buffer the location where the read bytes should be stored | ||
317 | * @param bytes the number of bytes to read from the file. | ||
318 | * @return the number of bytes successfully read (this can be less than | ||
319 | * the number requested), zero to mark the end of file, or less | ||
320 | * than zero to indicate an error. | ||
321 | * @see open(), write() | ||
322 | */ | ||
323 | int (*read)(struct mspack_file *file, | ||
324 | void *buffer, | ||
325 | int bytes); | ||
326 | |||
327 | /** | ||
328 | * Writes a given number of bytes to an open file. | ||
329 | * | ||
330 | * @param file the file to write to | ||
331 | * @param buffer the location where the written bytes should be read from | ||
332 | * @param bytes the number of bytes to write to the file. | ||
333 | * @return the number of bytes successfully written, this can be less | ||
334 | * than the number requested. Zero or less can indicate an error | ||
335 | * where no bytes at all could be written. All cases where less | ||
336 | * bytes were written than requested are considered by the library | ||
337 | * to be an error. | ||
338 | * @see open(), read() | ||
339 | */ | ||
340 | int (*write)(struct mspack_file *file, | ||
341 | void *buffer, | ||
342 | int bytes); | ||
343 | |||
344 | /** | ||
345 | * Seeks to a specific file offset within an open file. | ||
346 | * | ||
347 | * Sometimes the library needs to know the length of a file. It does | ||
348 | * this by seeking to the end of the file with seek(file, 0, | ||
349 | * MSPACK_SYS_SEEK_END), then calling tell(). Implementations may want | ||
350 | * to make a special case for this. | ||
351 | * | ||
352 | * Due to the potentially varying 32/64 bit datatype off_t on some | ||
353 | * architectures, the #MSPACK_SYS_SELFTEST macro MUST be used before | ||
354 | * using the library. If not, the error caused by the library passing an | ||
355 | * inappropriate stackframe to seek() is subtle and hard to trace. | ||
356 | * | ||
357 | * @param file the file to be seeked | ||
358 | * @param offset an offset to seek, measured in bytes | ||
359 | * @param mode one of #MSPACK_SYS_SEEK_START (the offset should be | ||
360 | * measured from the start of the file), #MSPACK_SYS_SEEK_CUR | ||
361 | * (the offset should be measured from the current file offset) | ||
362 | * or #MSPACK_SYS_SEEK_END (the offset should be measured from | ||
363 | * the end of the file) | ||
364 | * @return zero for success, non-zero for an error | ||
365 | * @see open(), tell() | ||
366 | */ | ||
367 | int (*seek)(struct mspack_file *file, | ||
368 | off_t offset, | ||
369 | int mode); | ||
370 | |||
371 | /** | ||
372 | * Returns the current file position (in bytes) of the given file. | ||
373 | * | ||
374 | * @param file the file whose file position is wanted | ||
375 | * @return the current file position of the file | ||
376 | * @see open(), seek() | ||
377 | */ | ||
378 | off_t (*tell)(struct mspack_file *file); | ||
379 | |||
380 | /** | ||
381 | * Used to send messages from the library to the user. | ||
382 | * | ||
383 | * Occasionally, the library generates warnings or other messages in | ||
384 | * plain english to inform the human user. These are informational only | ||
385 | * and can be ignored if not wanted. | ||
386 | * | ||
387 | * @param file may be a file handle returned from open() if this message | ||
388 | * pertains to a specific open file, or NULL if not related to | ||
389 | * a specific file. | ||
390 | * @param format a printf() style format string. It does NOT include a | ||
391 | * trailing newline. | ||
392 | * @see open() | ||
393 | */ | ||
394 | void (*message)(struct mspack_file *file, | ||
395 | const char *format, | ||
396 | ...); | ||
397 | |||
398 | /** | ||
399 | * Allocates memory. | ||
400 | * | ||
401 | * @param self a self-referential pointer to the mspack_system | ||
402 | * structure whose alloc() method is being called. | ||
403 | * @param bytes the number of bytes to allocate | ||
404 | * @result a pointer to the requested number of bytes, or NULL if | ||
405 | * not enough memory is available | ||
406 | * @see free() | ||
407 | */ | ||
408 | void * (*alloc)(struct mspack_system *self, | ||
409 | size_t bytes); | ||
410 | |||
411 | /** | ||
412 | * Frees memory. | ||
413 | * | ||
414 | * @param ptr the memory to be freed. | ||
415 | * @see alloc() | ||
416 | */ | ||
417 | void (*free)(void *ptr); | ||
418 | |||
419 | /** | ||
420 | * Copies from one region of memory to another. | ||
421 | * | ||
422 | * The regions of memory are guaranteed not to overlap, are usually less | ||
423 | * than 256 bytes, and may not be aligned. Please note that the source | ||
424 | * parameter comes before the destination parameter, unlike the standard | ||
425 | * C function memcpy(). | ||
426 | * | ||
427 | * @param src the region of memory to copy from | ||
428 | * @param dest the region of memory to copy to | ||
429 | * @param bytes the size of the memory region, in bytes | ||
430 | */ | ||
431 | void (*copy)(void *src, | ||
432 | void *dest, | ||
433 | size_t bytes); | ||
434 | |||
435 | /** | ||
436 | * A null pointer to mark the end of mspack_system. It must equal NULL. | ||
437 | * | ||
438 | * Should the mspack_system structure extend in the future, this NULL | ||
439 | * will be seen, rather than have an invalid method pointer called. | ||
440 | */ | ||
441 | void *null_ptr; | ||
442 | }; | ||
443 | |||
444 | /** mspack_system::open() mode: open existing file for reading. */ | ||
445 | #define MSPACK_SYS_OPEN_READ (0) | ||
446 | /** mspack_system::open() mode: open new file for writing */ | ||
447 | #define MSPACK_SYS_OPEN_WRITE (1) | ||
448 | /** mspack_system::open() mode: open existing file for writing */ | ||
449 | #define MSPACK_SYS_OPEN_UPDATE (2) | ||
450 | /** mspack_system::open() mode: open existing file for writing */ | ||
451 | #define MSPACK_SYS_OPEN_APPEND (3) | ||
452 | |||
453 | /** mspack_system::seek() mode: seek relative to start of file */ | ||
454 | #define MSPACK_SYS_SEEK_START (0) | ||
455 | /** mspack_system::seek() mode: seek relative to current offset */ | ||
456 | #define MSPACK_SYS_SEEK_CUR (1) | ||
457 | /** mspack_system::seek() mode: seek relative to end of file */ | ||
458 | #define MSPACK_SYS_SEEK_END (2) | ||
459 | |||
460 | /** | ||
461 | * A structure which represents an open file handle. The contents of this | ||
462 | * structure are determined by the implementation of the | ||
463 | * mspack_system::open() method. | ||
464 | */ | ||
465 | struct mspack_file { | ||
466 | int dummy; | ||
467 | }; | ||
468 | |||
469 | /* --- error codes --------------------------------------------------------- */ | ||
470 | |||
471 | /** Error code: no error */ | ||
472 | #define MSPACK_ERR_OK (0) | ||
473 | /** Error code: bad arguments to method */ | ||
474 | #define MSPACK_ERR_ARGS (1) | ||
475 | /** Error code: error opening file */ | ||
476 | #define MSPACK_ERR_OPEN (2) | ||
477 | /** Error code: error reading file */ | ||
478 | #define MSPACK_ERR_READ (3) | ||
479 | /** Error code: error writing file */ | ||
480 | #define MSPACK_ERR_WRITE (4) | ||
481 | /** Error code: seek error */ | ||
482 | #define MSPACK_ERR_SEEK (5) | ||
483 | /** Error code: out of memory */ | ||
484 | #define MSPACK_ERR_NOMEMORY (6) | ||
485 | /** Error code: bad "magic id" in file */ | ||
486 | #define MSPACK_ERR_SIGNATURE (7) | ||
487 | /** Error code: bad or corrupt file format */ | ||
488 | #define MSPACK_ERR_DATAFORMAT (8) | ||
489 | /** Error code: bad checksum or CRC */ | ||
490 | #define MSPACK_ERR_CHECKSUM (9) | ||
491 | /** Error code: error during compression */ | ||
492 | #define MSPACK_ERR_CRUNCH (10) | ||
493 | /** Error code: error during decompression */ | ||
494 | #define MSPACK_ERR_DECRUNCH (11) | ||
495 | |||
496 | /* --- functions available in library -------------------------------------- */ | ||
497 | |||
498 | /** Creates a new CAB compressor. | ||
499 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
500 | * @return a #mscab_compressor or NULL | ||
501 | */ | ||
502 | extern struct mscab_compressor * | ||
503 | mspack_create_cab_compressor(struct mspack_system *sys); | ||
504 | |||
505 | /** Creates a new CAB decompressor. | ||
506 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
507 | * @return a #mscab_decompressor or NULL | ||
508 | */ | ||
509 | extern struct mscab_decompressor * | ||
510 | mspack_create_cab_decompressor(struct mspack_system *sys); | ||
511 | |||
512 | /** Destroys an existing CAB compressor. | ||
513 | * @param self the #mscab_compressor to destroy | ||
514 | */ | ||
515 | extern void mspack_destroy_cab_compressor(struct mscab_compressor *self); | ||
516 | |||
517 | /** Destroys an existing CAB decompressor. | ||
518 | * @param self the #mscab_decompressor to destroy | ||
519 | */ | ||
520 | extern void mspack_destroy_cab_decompressor(struct mscab_decompressor *self); | ||
521 | |||
522 | |||
523 | /** Creates a new CHM compressor. | ||
524 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
525 | * @return a #mschm_compressor or NULL | ||
526 | */ | ||
527 | extern struct mschm_compressor * | ||
528 | mspack_create_chm_compressor(struct mspack_system *sys); | ||
529 | |||
530 | /** Creates a new CHM decompressor. | ||
531 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
532 | * @return a #mschm_decompressor or NULL | ||
533 | */ | ||
534 | extern struct mschm_decompressor * | ||
535 | mspack_create_chm_decompressor(struct mspack_system *sys); | ||
536 | |||
537 | /** Destroys an existing CHM compressor. | ||
538 | * @param self the #mschm_compressor to destroy | ||
539 | */ | ||
540 | extern void mspack_destroy_chm_compressor(struct mschm_compressor *self); | ||
541 | |||
542 | /** Destroys an existing CHM decompressor. | ||
543 | * @param self the #mschm_decompressor to destroy | ||
544 | */ | ||
545 | extern void mspack_destroy_chm_decompressor(struct mschm_decompressor *self); | ||
546 | |||
547 | |||
548 | /** Creates a new LIT compressor. | ||
549 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
550 | * @return a #mslit_compressor or NULL | ||
551 | */ | ||
552 | extern struct mslit_compressor * | ||
553 | mspack_create_lit_compressor(struct mspack_system *sys); | ||
554 | |||
555 | /** Creates a new LIT decompressor. | ||
556 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
557 | * @return a #mslit_decompressor or NULL | ||
558 | */ | ||
559 | extern struct mslit_decompressor * | ||
560 | mspack_create_lit_decompressor(struct mspack_system *sys); | ||
561 | |||
562 | /** Destroys an existing LIT compressor. | ||
563 | * @param self the #mslit_compressor to destroy | ||
564 | */ | ||
565 | extern void mspack_destroy_lit_compressor(struct mslit_compressor *self); | ||
566 | |||
567 | /** Destroys an existing LIT decompressor. | ||
568 | * @param self the #mslit_decompressor to destroy | ||
569 | */ | ||
570 | extern void mspack_destroy_lit_decompressor(struct mslit_decompressor *self); | ||
571 | |||
572 | |||
573 | /** Creates a new HLP compressor. | ||
574 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
575 | * @return a #mshlp_compressor or NULL | ||
576 | */ | ||
577 | extern struct mshlp_compressor * | ||
578 | mspack_create_hlp_compressor(struct mspack_system *sys); | ||
579 | |||
580 | /** Creates a new HLP decompressor. | ||
581 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
582 | * @return a #mshlp_decompressor or NULL | ||
583 | */ | ||
584 | extern struct mshlp_decompressor * | ||
585 | mspack_create_hlp_decompressor(struct mspack_system *sys); | ||
586 | |||
587 | /** Destroys an existing hlp compressor. | ||
588 | * @param self the #mshlp_compressor to destroy | ||
589 | */ | ||
590 | extern void mspack_destroy_hlp_compressor(struct mshlp_compressor *self); | ||
591 | |||
592 | /** Destroys an existing hlp decompressor. | ||
593 | * @param self the #mshlp_decompressor to destroy | ||
594 | */ | ||
595 | extern void mspack_destroy_hlp_decompressor(struct mshlp_decompressor *self); | ||
596 | |||
597 | |||
598 | /** Creates a new SZDD compressor. | ||
599 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
600 | * @return a #msszdd_compressor or NULL | ||
601 | */ | ||
602 | extern struct msszdd_compressor * | ||
603 | mspack_create_szdd_compressor(struct mspack_system *sys); | ||
604 | |||
605 | /** Creates a new SZDD decompressor. | ||
606 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
607 | * @return a #msszdd_decompressor or NULL | ||
608 | */ | ||
609 | extern struct msszdd_decompressor * | ||
610 | mspack_create_szdd_decompressor(struct mspack_system *sys); | ||
611 | |||
612 | /** Destroys an existing SZDD compressor. | ||
613 | * @param self the #msszdd_compressor to destroy | ||
614 | */ | ||
615 | extern void mspack_destroy_szdd_compressor(struct msszdd_compressor *self); | ||
616 | |||
617 | /** Destroys an existing SZDD decompressor. | ||
618 | * @param self the #msszdd_decompressor to destroy | ||
619 | */ | ||
620 | extern void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *self); | ||
621 | |||
622 | |||
623 | /** Creates a new KWAJ compressor. | ||
624 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
625 | * @return a #mskwaj_compressor or NULL | ||
626 | */ | ||
627 | extern struct mskwaj_compressor * | ||
628 | mspack_create_kwaj_compressor(struct mspack_system *sys); | ||
629 | |||
630 | /** Creates a new KWAJ decompressor. | ||
631 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
632 | * @return a #mskwaj_decompressor or NULL | ||
633 | */ | ||
634 | extern struct mskwaj_decompressor * | ||
635 | mspack_create_kwaj_decompressor(struct mspack_system *sys); | ||
636 | |||
637 | /** Destroys an existing KWAJ compressor. | ||
638 | * @param self the #mskwaj_compressor to destroy | ||
639 | */ | ||
640 | extern void mspack_destroy_kwaj_compressor(struct mskwaj_compressor *self); | ||
641 | |||
642 | /** Destroys an existing KWAJ decompressor. | ||
643 | * @param self the #mskwaj_decompressor to destroy | ||
644 | */ | ||
645 | extern void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *self); | ||
646 | |||
647 | |||
648 | /* --- support for .CAB (MS Cabinet) file format --------------------------- */ | ||
649 | |||
650 | /** | ||
651 | * A structure which represents a single cabinet file. | ||
652 | * | ||
653 | * All fields are READ ONLY. | ||
654 | * | ||
655 | * If this cabinet is part of a merged cabinet set, the #files and #folders | ||
656 | * fields are common to all cabinets in the set, and will be identical. | ||
657 | * | ||
658 | * @see mscab_decompressor::open(), mscab_decompressor::close(), | ||
659 | * mscab_decompressor::search() | ||
660 | */ | ||
661 | struct mscabd_cabinet { | ||
662 | /** | ||
663 | * The next cabinet in a chained list, if this cabinet was opened with | ||
664 | * mscab_decompressor::search(). May be NULL to mark the end of the | ||
665 | * list. | ||
666 | */ | ||
667 | struct mscabd_cabinet *next; | ||
668 | |||
669 | /** | ||
670 | * The filename of the cabinet. More correctly, the filename of the | ||
671 | * physical file that the cabinet resides in. This is given by the | ||
672 | * library user and may be in any format. | ||
673 | */ | ||
674 | const char *filename; | ||
675 | |||
676 | /** The file offset of cabinet within the physical file it resides in. */ | ||
677 | off_t base_offset; | ||
678 | |||
679 | /** The length of the cabinet file in bytes. */ | ||
680 | unsigned int length; | ||
681 | |||
682 | /** The previous cabinet in a cabinet set, or NULL. */ | ||
683 | struct mscabd_cabinet *prevcab; | ||
684 | |||
685 | /** The next cabinet in a cabinet set, or NULL. */ | ||
686 | struct mscabd_cabinet *nextcab; | ||
687 | |||
688 | /** The filename of the previous cabinet in a cabinet set, or NULL. */ | ||
689 | char *prevname; | ||
690 | |||
691 | /** The filename of the next cabinet in a cabinet set, or NULL. */ | ||
692 | char *nextname; | ||
693 | |||
694 | /** The name of the disk containing the previous cabinet in a cabinet | ||
695 | * set, or NULL. | ||
696 | */ | ||
697 | char *previnfo; | ||
698 | |||
699 | /** The name of the disk containing the next cabinet in a cabinet set, | ||
700 | * or NULL. | ||
701 | */ | ||
702 | char *nextinfo; | ||
703 | |||
704 | /** A list of all files in the cabinet or cabinet set. */ | ||
705 | struct mscabd_file *files; | ||
706 | |||
707 | /** A list of all folders in the cabinet or cabinet set. */ | ||
708 | struct mscabd_folder *folders; | ||
709 | |||
710 | /** | ||
711 | * The set ID of the cabinet. All cabinets in the same set should have | ||
712 | * the same set ID. | ||
713 | */ | ||
714 | unsigned short set_id; | ||
715 | |||
716 | /** | ||
717 | * The index number of the cabinet within the set. Numbering should | ||
718 | * start from 0 for the first cabinet in the set, and increment by 1 for | ||
719 | * each following cabinet. | ||
720 | */ | ||
721 | unsigned short set_index; | ||
722 | |||
723 | /** | ||
724 | * The number of bytes reserved in the header area of the cabinet. | ||
725 | * | ||
726 | * If this is non-zero and flags has MSCAB_HDR_RESV set, this data can | ||
727 | * be read by the calling application. It is of the given length, | ||
728 | * located at offset (base_offset + MSCAB_HDR_RESV_OFFSET) in the | ||
729 | * cabinet file. | ||
730 | * | ||
731 | * @see flags | ||
732 | */ | ||
733 | unsigned short header_resv; | ||
734 | |||
735 | /** | ||
736 | * Header flags. | ||
737 | * | ||
738 | * - MSCAB_HDR_PREVCAB indicates the cabinet is part of a cabinet set, and | ||
739 | * has a predecessor cabinet. | ||
740 | * - MSCAB_HDR_NEXTCAB indicates the cabinet is part of a cabinet set, and | ||
741 | * has a successor cabinet. | ||
742 | * - MSCAB_HDR_RESV indicates the cabinet has reserved header space. | ||
743 | * | ||
744 | * @see prevname, previnfo, nextname, nextinfo, header_resv | ||
745 | */ | ||
746 | int flags; | ||
747 | }; | ||
748 | |||
749 | /** Offset from start of cabinet to the reserved header data (if present). */ | ||
750 | #define MSCAB_HDR_RESV_OFFSET (0x28) | ||
751 | |||
752 | /** Cabinet header flag: cabinet has a predecessor */ | ||
753 | #define MSCAB_HDR_PREVCAB (0x01) | ||
754 | /** Cabinet header flag: cabinet has a successor */ | ||
755 | #define MSCAB_HDR_NEXTCAB (0x02) | ||
756 | /** Cabinet header flag: cabinet has reserved header space */ | ||
757 | #define MSCAB_HDR_RESV (0x04) | ||
758 | |||
759 | /** | ||
760 | * A structure which represents a single folder in a cabinet or cabinet set. | ||
761 | * | ||
762 | * All fields are READ ONLY. | ||
763 | * | ||
764 | * A folder is a single compressed stream of data. When uncompressed, it | ||
765 | * holds the data of one or more files. A folder may be split across more | ||
766 | * than one cabinet. | ||
767 | */ | ||
768 | struct mscabd_folder { | ||
769 | /** | ||
770 | * A pointer to the next folder in this cabinet or cabinet set, or NULL | ||
771 | * if this is the final folder. | ||
772 | */ | ||
773 | struct mscabd_folder *next; | ||
774 | |||
775 | /** | ||
776 | * The compression format used by this folder. | ||
777 | * | ||
778 | * The macro MSCABD_COMP_METHOD() should be used on this field to get | ||
779 | * the algorithm used. The macro MSCABD_COMP_LEVEL() should be used to get | ||
780 | * the "compression level". | ||
781 | * | ||
782 | * @see MSCABD_COMP_METHOD(), MSCABD_COMP_LEVEL() | ||
783 | */ | ||
784 | int comp_type; | ||
785 | |||
786 | /** | ||
787 | * The total number of data blocks used by this folder. This includes | ||
788 | * data blocks present in other files, if this folder spans more than | ||
789 | * one cabinet. | ||
790 | */ | ||
791 | unsigned int num_blocks; | ||
792 | }; | ||
793 | |||
794 | /** | ||
795 | * Returns the compression method used by a folder. | ||
796 | * | ||
797 | * @param comp_type a mscabd_folder::comp_type value | ||
798 | * @return one of #MSCAB_COMP_NONE, #MSCAB_COMP_MSZIP, #MSCAB_COMP_QUANTUM | ||
799 | * or #MSCAB_COMP_LZX | ||
800 | */ | ||
801 | #define MSCABD_COMP_METHOD(comp_type) ((comp_type) & 0x0F) | ||
802 | /** | ||
803 | * Returns the compression level used by a folder. | ||
804 | * | ||
805 | * @param comp_type a mscabd_folder::comp_type value | ||
806 | * @return the compression level. This is only defined by LZX and Quantum | ||
807 | * compression | ||
808 | */ | ||
809 | #define MSCABD_COMP_LEVEL(comp_type) (((comp_type) >> 8) & 0x1F) | ||
810 | |||
811 | /** Compression mode: no compression. */ | ||
812 | #define MSCAB_COMP_NONE (0) | ||
813 | /** Compression mode: MSZIP (deflate) compression. */ | ||
814 | #define MSCAB_COMP_MSZIP (1) | ||
815 | /** Compression mode: Quantum compression */ | ||
816 | #define MSCAB_COMP_QUANTUM (2) | ||
817 | /** Compression mode: LZX compression */ | ||
818 | #define MSCAB_COMP_LZX (3) | ||
819 | |||
820 | /** | ||
821 | * A structure which represents a single file in a cabinet or cabinet set. | ||
822 | * | ||
823 | * All fields are READ ONLY. | ||
824 | */ | ||
825 | struct mscabd_file { | ||
826 | /** | ||
827 | * The next file in the cabinet or cabinet set, or NULL if this is the | ||
828 | * final file. | ||
829 | */ | ||
830 | struct mscabd_file *next; | ||
831 | |||
832 | /** | ||
833 | * The filename of the file. | ||
834 | * | ||
835 | * A null terminated string of up to 255 bytes in length, it may be in | ||
836 | * either ISO-8859-1 or UTF8 format, depending on the file attributes. | ||
837 | * | ||
838 | * @see attribs | ||
839 | */ | ||
840 | char *filename; | ||
841 | |||
842 | /** The uncompressed length of the file, in bytes. */ | ||
843 | unsigned int length; | ||
844 | |||
845 | /** | ||
846 | * File attributes. | ||
847 | * | ||
848 | * The following attributes are defined: | ||
849 | * - #MSCAB_ATTRIB_RDONLY indicates the file is write protected. | ||
850 | * - #MSCAB_ATTRIB_HIDDEN indicates the file is hidden. | ||
851 | * - #MSCAB_ATTRIB_SYSTEM indicates the file is a operating system file. | ||
852 | * - #MSCAB_ATTRIB_ARCH indicates the file is "archived". | ||
853 | * - #MSCAB_ATTRIB_EXEC indicates the file is an executable program. | ||
854 | * - #MSCAB_ATTRIB_UTF_NAME indicates the filename is in UTF8 format rather | ||
855 | * than ISO-8859-1. | ||
856 | */ | ||
857 | int attribs; | ||
858 | |||
859 | /** File's last modified time, hour field. */ | ||
860 | char time_h; | ||
861 | /** File's last modified time, minute field. */ | ||
862 | char time_m; | ||
863 | /** File's last modified time, second field. */ | ||
864 | char time_s; | ||
865 | |||
866 | /** File's last modified date, day field. */ | ||
867 | char date_d; | ||
868 | /** File's last modified date, month field. */ | ||
869 | char date_m; | ||
870 | /** File's last modified date, year field. */ | ||
871 | int date_y; | ||
872 | |||
873 | /** A pointer to the folder that contains this file. */ | ||
874 | struct mscabd_folder *folder; | ||
875 | |||
876 | /** The uncompressed offset of this file in its folder. */ | ||
877 | unsigned int offset; | ||
878 | }; | ||
879 | |||
880 | /** mscabd_file::attribs attribute: file is read-only. */ | ||
881 | #define MSCAB_ATTRIB_RDONLY (0x01) | ||
882 | /** mscabd_file::attribs attribute: file is hidden. */ | ||
883 | #define MSCAB_ATTRIB_HIDDEN (0x02) | ||
884 | /** mscabd_file::attribs attribute: file is an operating system file. */ | ||
885 | #define MSCAB_ATTRIB_SYSTEM (0x04) | ||
886 | /** mscabd_file::attribs attribute: file is "archived". */ | ||
887 | #define MSCAB_ATTRIB_ARCH (0x20) | ||
888 | /** mscabd_file::attribs attribute: file is an executable program. */ | ||
889 | #define MSCAB_ATTRIB_EXEC (0x40) | ||
890 | /** mscabd_file::attribs attribute: filename is UTF8, not ISO-8859-1. */ | ||
891 | #define MSCAB_ATTRIB_UTF_NAME (0x80) | ||
892 | |||
893 | /** mscab_decompressor::set_param() parameter: search buffer size. */ | ||
894 | #define MSCABD_PARAM_SEARCHBUF (0) | ||
895 | /** mscab_decompressor::set_param() parameter: repair MS-ZIP streams? */ | ||
896 | #define MSCABD_PARAM_FIXMSZIP (1) | ||
897 | /** mscab_decompressor::set_param() parameter: size of decompression buffer */ | ||
898 | #define MSCABD_PARAM_DECOMPBUF (2) | ||
899 | |||
900 | /** TODO */ | ||
901 | struct mscab_compressor { | ||
902 | int dummy; | ||
903 | }; | ||
904 | |||
905 | /** | ||
906 | * A decompressor for .CAB (Microsoft Cabinet) files | ||
907 | * | ||
908 | * All fields are READ ONLY. | ||
909 | * | ||
910 | * @see mspack_create_cab_decompressor(), mspack_destroy_cab_decompressor() | ||
911 | */ | ||
912 | struct mscab_decompressor { | ||
913 | /** | ||
914 | * Opens a cabinet file and reads its contents. | ||
915 | * | ||
916 | * If the file opened is a valid cabinet file, all headers will be read | ||
917 | * and a mscabd_cabinet structure will be returned, with a full list of | ||
918 | * folders and files. | ||
919 | * | ||
920 | * In the case of an error occuring, NULL is returned and the error code | ||
921 | * is available from last_error(). | ||
922 | * | ||
923 | * The filename pointer should be considered "in use" until close() is | ||
924 | * called on the cabinet. | ||
925 | * | ||
926 | * @param self a self-referential pointer to the mscab_decompressor | ||
927 | * instance being called | ||
928 | * @param filename the filename of the cabinet file. This is passed | ||
929 | * directly to mspack_system::open(). | ||
930 | * @return a pointer to a mscabd_cabinet structure, or NULL on failure | ||
931 | * @see close(), search(), last_error() | ||
932 | */ | ||
933 | struct mscabd_cabinet * (*open) (struct mscab_decompressor *self, | ||
934 | const char *filename); | ||
935 | |||
936 | /** | ||
937 | * Closes a previously opened cabinet or cabinet set. | ||
938 | * | ||
939 | * This closes a cabinet, all cabinets associated with it via the | ||
940 | * mscabd_cabinet::next, mscabd_cabinet::prevcab and | ||
941 | * mscabd_cabinet::nextcab pointers, and all folders and files. All | ||
942 | * memory used by these entities is freed. | ||
943 | * | ||
944 | * The cabinet pointer is now invalid and cannot be used again. All | ||
945 | * mscabd_folder and mscabd_file pointers from that cabinet or cabinet | ||
946 | * set are also now invalid, and cannot be used again. | ||
947 | * | ||
948 | * If the cabinet pointer given was created using search(), it MUST be | ||
949 | * the cabinet pointer returned by search() and not one of the later | ||
950 | * cabinet pointers further along the mscabd_cabinet::next chain. | ||
951 | |||
952 | * If extra cabinets have been added using append() or prepend(), these | ||
953 | * will all be freed, even if the cabinet pointer given is not the first | ||
954 | * cabinet in the set. Do NOT close() more than one cabinet in the set. | ||
955 | * | ||
956 | * The mscabd_cabinet::filename is not freed by the library, as it is | ||
957 | * not allocated by the library. The caller should free this itself if | ||
958 | * necessary, before it is lost forever. | ||
959 | * | ||
960 | * @param self a self-referential pointer to the mscab_decompressor | ||
961 | * instance being called | ||
962 | * @param cab the cabinet to close | ||
963 | * @see open(), search(), append(), prepend() | ||
964 | */ | ||
965 | void (*close)(struct mscab_decompressor *self, | ||
966 | struct mscabd_cabinet *cab); | ||
967 | |||
968 | /** | ||
969 | * Searches a regular file for embedded cabinets. | ||
970 | * | ||
971 | * This opens a normal file with the given filename and will search the | ||
972 | * entire file for embedded cabinet files | ||
973 | * | ||
974 | * If any cabinets are found, the equivalent of open() is called on each | ||
975 | * potential cabinet file at the offset it was found. All successfully | ||
976 | * open()ed cabinets are kept in a list. | ||
977 | * | ||
978 | * The first cabinet found will be returned directly as the result of | ||
979 | * this method. Any further cabinets found will be chained in a list | ||
980 | * using the mscabd_cabinet::next field. | ||
981 | * | ||
982 | * In the case of an error occuring anywhere other than the simulated | ||
983 | * open(), NULL is returned and the error code is available from | ||
984 | * last_error(). | ||
985 | * | ||
986 | * If no error occurs, but no cabinets can be found in the file, NULL is | ||
987 | * returned and last_error() returns MSPACK_ERR_OK. | ||
988 | * | ||
989 | * The filename pointer should be considered in use until close() is | ||
990 | * called on the cabinet. | ||
991 | * | ||
992 | * close() should only be called on the result of search(), not on any | ||
993 | * subsequent cabinets in the mscabd_cabinet::next chain. | ||
994 | * | ||
995 | * @param self a self-referential pointer to the mscab_decompressor | ||
996 | * instance being called | ||
997 | * @param filename the filename of the file to search for cabinets. This | ||
998 | * is passed directly to mspack_system::open(). | ||
999 | * @return a pointer to a mscabd_cabinet structure, or NULL | ||
1000 | * @see close(), open(), last_error() | ||
1001 | */ | ||
1002 | struct mscabd_cabinet * (*search) (struct mscab_decompressor *self, | ||
1003 | const char *filename); | ||
1004 | |||
1005 | /** | ||
1006 | * Appends one mscabd_cabinet to another, forming or extending a cabinet | ||
1007 | * set. | ||
1008 | * | ||
1009 | * This will attempt to append one cabinet to another such that | ||
1010 | * <tt>(cab->nextcab == nextcab) && (nextcab->prevcab == cab)</tt> and | ||
1011 | * any folders split between the two cabinets are merged. | ||
1012 | * | ||
1013 | * The cabinets MUST be part of a cabinet set -- a cabinet set is a | ||
1014 | * cabinet that spans more than one physical cabinet file on disk -- and | ||
1015 | * must be appropriately matched. | ||
1016 | * | ||
1017 | * It can be determined if a cabinet has further parts to load by | ||
1018 | * examining the mscabd_cabinet::flags field: | ||
1019 | * | ||
1020 | * - if <tt>(flags & MSCAB_HDR_PREVCAB)</tt> is non-zero, there is a | ||
1021 | * predecessor cabinet to open() and prepend(). Its MS-DOS | ||
1022 | * case-insensitive filename is mscabd_cabinet::prevname | ||
1023 | * - if <tt>(flags & MSCAB_HDR_NEXTCAB)</tt> is non-zero, there is a | ||
1024 | * successor cabinet to open() and append(). Its MS-DOS case-insensitive | ||
1025 | * filename is mscabd_cabinet::nextname | ||
1026 | * | ||
1027 | * If the cabinets do not match, an error code will be returned. Neither | ||
1028 | * cabinet has been altered, and both should be closed seperately. | ||
1029 | * | ||
1030 | * Files and folders in a cabinet set are a single entity. All cabinets | ||
1031 | * in a set use the same file list, which is updated as cabinets in the | ||
1032 | * set are added. All pointers to mscabd_folder and mscabd_file | ||
1033 | * structures in either cabinet must be discarded and re-obtained after | ||
1034 | * merging. | ||
1035 | * | ||
1036 | * @param self a self-referential pointer to the mscab_decompressor | ||
1037 | * instance being called | ||
1038 | * @param cab the cabinet which will be appended to, | ||
1039 | * predecessor of nextcab | ||
1040 | * @param nextcab the cabinet which will be appended, | ||
1041 | * successor of cab | ||
1042 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1043 | * @see prepend(), open(), close() | ||
1044 | */ | ||
1045 | int (*append) (struct mscab_decompressor *self, | ||
1046 | struct mscabd_cabinet *cab, | ||
1047 | struct mscabd_cabinet *nextcab); | ||
1048 | |||
1049 | /** | ||
1050 | * Prepends one mscabd_cabinet to another, forming or extending a | ||
1051 | * cabinet set. | ||
1052 | * | ||
1053 | * This will attempt to prepend one cabinet to another, such that | ||
1054 | * <tt>(cab->prevcab == prevcab) && (prevcab->nextcab == cab)</tt>. In | ||
1055 | * all other respects, it is identical to append(). See append() for the | ||
1056 | * full documentation. | ||
1057 | * | ||
1058 | * @param self a self-referential pointer to the mscab_decompressor | ||
1059 | * instance being called | ||
1060 | * @param cab the cabinet which will be prepended to, | ||
1061 | * successor of prevcab | ||
1062 | * @param prevcab the cabinet which will be prepended, | ||
1063 | * predecessor of cab | ||
1064 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1065 | * @see append(), open(), close() | ||
1066 | */ | ||
1067 | int (*prepend) (struct mscab_decompressor *self, | ||
1068 | struct mscabd_cabinet *cab, | ||
1069 | struct mscabd_cabinet *prevcab); | ||
1070 | |||
1071 | /** | ||
1072 | * Extracts a file from a cabinet or cabinet set. | ||
1073 | * | ||
1074 | * This extracts a compressed file in a cabinet and writes it to the given | ||
1075 | * filename. | ||
1076 | * | ||
1077 | * The MS-DOS filename of the file, mscabd_file::filename, is NOT USED | ||
1078 | * by extract(). The caller must examine this MS-DOS filename, copy and | ||
1079 | * change it as necessary, create directories as necessary, and provide | ||
1080 | * the correct filename as a parameter, which will be passed unchanged | ||
1081 | * to the decompressor's mspack_system::open() | ||
1082 | * | ||
1083 | * If the file belongs to a split folder in a multi-part cabinet set, | ||
1084 | * and not enough parts of the cabinet set have been loaded and appended | ||
1085 | * or prepended, an error will be returned immediately. | ||
1086 | * | ||
1087 | * @param self a self-referential pointer to the mscab_decompressor | ||
1088 | * instance being called | ||
1089 | * @param file the file to be decompressed | ||
1090 | * @param filename the filename of the file being written to | ||
1091 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1092 | */ | ||
1093 | int (*extract)(struct mscab_decompressor *self, | ||
1094 | struct mscabd_file *file, | ||
1095 | const char *filename); | ||
1096 | |||
1097 | /** | ||
1098 | * Sets a CAB decompression engine parameter. | ||
1099 | * | ||
1100 | * The following parameters are defined: | ||
1101 | * - #MSCABD_PARAM_SEARCHBUF: How many bytes should be allocated as a | ||
1102 | * buffer when using search()? The minimum value is 4. The default | ||
1103 | * value is 32768. | ||
1104 | * - #MSCABD_PARAM_FIXMSZIP: If non-zero, extract() will ignore bad | ||
1105 | * checksums and recover from decompression errors in MS-ZIP | ||
1106 | * compressed folders. The default value is 0 (don't recover). | ||
1107 | * - #MSCABD_PARAM_DECOMPBUF: How many bytes should be used as an input | ||
1108 | * bit buffer by decompressors? The minimum value is 4. The default | ||
1109 | * value is 4096. | ||
1110 | * | ||
1111 | * @param self a self-referential pointer to the mscab_decompressor | ||
1112 | * instance being called | ||
1113 | * @param param the parameter to set | ||
1114 | * @param value the value to set the parameter to | ||
1115 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there | ||
1116 | * is a problem with either parameter or value. | ||
1117 | * @see search(), extract() | ||
1118 | */ | ||
1119 | int (*set_param)(struct mscab_decompressor *self, | ||
1120 | int param, | ||
1121 | int value); | ||
1122 | |||
1123 | /** | ||
1124 | * Returns the error code set by the most recently called method. | ||
1125 | * | ||
1126 | * This is useful for open() and search(), which do not return an error | ||
1127 | * code directly. | ||
1128 | * | ||
1129 | * @param self a self-referential pointer to the mscab_decompressor | ||
1130 | * instance being called | ||
1131 | * @return the most recent error code | ||
1132 | * @see open(), search() | ||
1133 | */ | ||
1134 | int (*last_error)(struct mscab_decompressor *self); | ||
1135 | }; | ||
1136 | |||
1137 | /* --- support for .CHM (HTMLHelp) file format ----------------------------- */ | ||
1138 | |||
1139 | /** | ||
1140 | * A structure which represents a file to be placed in a CHM helpfile. | ||
1141 | * | ||
1142 | * A contiguous array of these structures should be passed to | ||
1143 | * mschm_compressor::generate(). The array list is terminated with an | ||
1144 | * entry whose mschmc_file::section field is set to #MSCHMC_ENDLIST, the | ||
1145 | * other fields in this entry are ignored. | ||
1146 | */ | ||
1147 | struct mschmc_file { | ||
1148 | /** One of #MSCHMC_ENDLIST, #MSCHMC_UNCOMP or #MSCHMC_MSCOMP. */ | ||
1149 | int section; | ||
1150 | |||
1151 | /** The filename of the source file that will be added to the CHM. This | ||
1152 | * is passed directly to mspack_system::open(). */ | ||
1153 | const char *filename; | ||
1154 | |||
1155 | /** The full path and filename of the file within the CHM helpfile, a | ||
1156 | * UTF-1 encoded null-terminated string. */ | ||
1157 | char *chm_filename; | ||
1158 | |||
1159 | /** The length of the file, in bytes. This will be adhered to strictly | ||
1160 | * and a read error will be issued if this many bytes cannot be read | ||
1161 | * from the real file at CHM generation time. */ | ||
1162 | off_t length; | ||
1163 | }; | ||
1164 | |||
1165 | /** | ||
1166 | * A structure which represents a section of a CHM helpfile. | ||
1167 | * | ||
1168 | * All fields are READ ONLY. | ||
1169 | * | ||
1170 | * Not used directly, but used as a generic base type for | ||
1171 | * mschmd_sec_uncompressed and mschmd_sec_mscompressed. | ||
1172 | */ | ||
1173 | struct mschmd_section { | ||
1174 | /** A pointer to the CHM helpfile that contains this section. */ | ||
1175 | struct mschmd_header *chm; | ||
1176 | |||
1177 | /** | ||
1178 | * The section ID. Either 0 for the uncompressed section | ||
1179 | * mschmd_sec_uncompressed, or 1 for the LZX compressed section | ||
1180 | * mschmd_sec_mscompressed. No other section IDs are known. | ||
1181 | */ | ||
1182 | unsigned int id; | ||
1183 | }; | ||
1184 | |||
1185 | /** | ||
1186 | * A structure which represents the uncompressed section of a CHM helpfile. | ||
1187 | * | ||
1188 | * All fields are READ ONLY. | ||
1189 | */ | ||
1190 | struct mschmd_sec_uncompressed { | ||
1191 | /** Generic section data. */ | ||
1192 | struct mschmd_section base; | ||
1193 | |||
1194 | /** The file offset of where this section begins in the CHM helpfile. */ | ||
1195 | off_t offset; | ||
1196 | }; | ||
1197 | |||
1198 | /** | ||
1199 | * A structure which represents the LZX compressed section of a CHM helpfile. | ||
1200 | * | ||
1201 | * All fields are READ ONLY. | ||
1202 | */ | ||
1203 | struct mschmd_sec_mscompressed { | ||
1204 | /** Generic section data. */ | ||
1205 | struct mschmd_section base; | ||
1206 | |||
1207 | /** A pointer to the meta-file which represents all LZX compressed data. */ | ||
1208 | struct mschmd_file *content; | ||
1209 | |||
1210 | /** A pointer to the file which contains the LZX control data. */ | ||
1211 | struct mschmd_file *control; | ||
1212 | |||
1213 | /** A pointer to the file which contains the LZX reset table. */ | ||
1214 | struct mschmd_file *rtable; | ||
1215 | |||
1216 | /** A pointer to the file which contains the LZX span information. | ||
1217 | * Available only in CHM decoder version 2 and above. | ||
1218 | */ | ||
1219 | struct mschmd_file *spaninfo; | ||
1220 | }; | ||
1221 | |||
1222 | /** | ||
1223 | * A structure which represents a CHM helpfile. | ||
1224 | * | ||
1225 | * All fields are READ ONLY. | ||
1226 | */ | ||
1227 | struct mschmd_header { | ||
1228 | /** The version of the CHM file format used in this file. */ | ||
1229 | unsigned int version; | ||
1230 | |||
1231 | /** | ||
1232 | * The "timestamp" of the CHM helpfile. | ||
1233 | * | ||
1234 | * It is the lower 32 bits of a 64-bit value representing the number of | ||
1235 | * centiseconds since 1601-01-01 00:00:00 UTC, plus 42. It is not useful | ||
1236 | * as a timestamp, but it is useful as a semi-unique ID. | ||
1237 | */ | ||
1238 | unsigned int timestamp; | ||
1239 | |||
1240 | /** | ||
1241 | * The default Language and Country ID (LCID) of the user who ran the | ||
1242 | * HTMLHelp Compiler. This is not the language of the CHM file itself. | ||
1243 | */ | ||
1244 | unsigned int language; | ||
1245 | |||
1246 | /** | ||
1247 | * The filename of the CHM helpfile. This is given by the library user | ||
1248 | * and may be in any format. | ||
1249 | */ | ||
1250 | const char *filename; | ||
1251 | |||
1252 | /** The length of the CHM helpfile, in bytes. */ | ||
1253 | off_t length; | ||
1254 | |||
1255 | /** A list of all non-system files in the CHM helpfile. */ | ||
1256 | struct mschmd_file *files; | ||
1257 | |||
1258 | /** | ||
1259 | * A list of all system files in the CHM helpfile. | ||
1260 | * | ||
1261 | * System files are files which begin with "::". They are meta-files | ||
1262 | * generated by the CHM creation process. | ||
1263 | */ | ||
1264 | struct mschmd_file *sysfiles; | ||
1265 | |||
1266 | /** The section 0 (uncompressed) data in this CHM helpfile. */ | ||
1267 | struct mschmd_sec_uncompressed sec0; | ||
1268 | |||
1269 | /** The section 1 (MSCompressed) data in this CHM helpfile. */ | ||
1270 | struct mschmd_sec_mscompressed sec1; | ||
1271 | |||
1272 | /** The file offset of the first PMGL/PMGI directory chunk. */ | ||
1273 | off_t dir_offset; | ||
1274 | |||
1275 | /** The number of PMGL/PMGI directory chunks in this CHM helpfile. */ | ||
1276 | unsigned int num_chunks; | ||
1277 | |||
1278 | /** The size of each PMGL/PMGI chunk, in bytes. */ | ||
1279 | unsigned int chunk_size; | ||
1280 | |||
1281 | /** The "density" of the quick-reference section in PMGL/PMGI chunks. */ | ||
1282 | unsigned int density; | ||
1283 | |||
1284 | /** The depth of the index tree. | ||
1285 | * | ||
1286 | * - if 1, there are no PMGI chunks, only PMGL chunks. | ||
1287 | * - if 2, there is 1 PMGI chunk. All chunk indices point to PMGL chunks. | ||
1288 | * - if 3, the root PMGI chunk points to secondary PMGI chunks, which in | ||
1289 | * turn point to PMGL chunks. | ||
1290 | * - and so on... | ||
1291 | */ | ||
1292 | unsigned int depth; | ||
1293 | |||
1294 | /** | ||
1295 | * The number of the root PMGI chunk. | ||
1296 | * | ||
1297 | * If there is no index in the CHM helpfile, this will be 0xFFFFFFFF. | ||
1298 | */ | ||
1299 | unsigned int index_root; | ||
1300 | |||
1301 | /** | ||
1302 | * The number of the first PMGL chunk. Usually zero. | ||
1303 | * Available only in CHM decoder version 2 and above. | ||
1304 | */ | ||
1305 | unsigned int first_pmgl; | ||
1306 | |||
1307 | /** | ||
1308 | * The number of the last PMGL chunk. Usually num_chunks-1. | ||
1309 | * Available only in CHM decoder version 2 and above. | ||
1310 | */ | ||
1311 | unsigned int last_pmgl; | ||
1312 | |||
1313 | /** | ||
1314 | * A cache of loaded chunks, filled in by mschm_decoder::fast_find(). | ||
1315 | * Available only in CHM decoder version 2 and above. | ||
1316 | */ | ||
1317 | unsigned char **chunk_cache; | ||
1318 | }; | ||
1319 | |||
1320 | /** | ||
1321 | * A structure which represents a file stored in a CHM helpfile. | ||
1322 | * | ||
1323 | * All fields are READ ONLY. | ||
1324 | */ | ||
1325 | struct mschmd_file { | ||
1326 | /** | ||
1327 | * A pointer to the next file in the list, or NULL if this is the final | ||
1328 | * file. | ||
1329 | */ | ||
1330 | struct mschmd_file *next; | ||
1331 | |||
1332 | /** | ||
1333 | * A pointer to the section that this file is located in. Indirectly, | ||
1334 | * it also points to the CHM helpfile the file is located in. | ||
1335 | */ | ||
1336 | struct mschmd_section *section; | ||
1337 | |||
1338 | /** The offset within the section data that this file is located at. */ | ||
1339 | off_t offset; | ||
1340 | |||
1341 | /** The length of this file, in bytes */ | ||
1342 | off_t length; | ||
1343 | |||
1344 | /** The filename of this file -- a null terminated string in UTF-8. */ | ||
1345 | char *filename; | ||
1346 | }; | ||
1347 | |||
1348 | /** mschmc_file::section value: end of CHM file list */ | ||
1349 | #define MSCHMC_ENDLIST (0) | ||
1350 | /** mschmc_file::section value: this file is in the Uncompressed section */ | ||
1351 | #define MSCHMC_UNCOMP (1) | ||
1352 | /** mschmc_file::section value: this file is in the MSCompressed section */ | ||
1353 | #define MSCHMC_MSCOMP (2) | ||
1354 | |||
1355 | /** mschm_compressor::set_param() parameter: "timestamp" header */ | ||
1356 | #define MSCHMC_PARAM_TIMESTAMP (0) | ||
1357 | /** mschm_compressor::set_param() parameter: "language" header */ | ||
1358 | #define MSCHMC_PARAM_LANGUAGE (1) | ||
1359 | /** mschm_compressor::set_param() parameter: LZX window size */ | ||
1360 | #define MSCHMC_PARAM_LZXWINDOW (2) | ||
1361 | /** mschm_compressor::set_param() parameter: intra-chunk quickref density */ | ||
1362 | #define MSCHMC_PARAM_DENSITY (3) | ||
1363 | /** mschm_compressor::set_param() parameter: whether to create indices */ | ||
1364 | #define MSCHMC_PARAM_INDEX (4) | ||
1365 | |||
1366 | /** | ||
1367 | * A compressor for .CHM (Microsoft HTMLHelp) files. | ||
1368 | * | ||
1369 | * All fields are READ ONLY. | ||
1370 | * | ||
1371 | * @see mspack_create_chm_compressor(), mspack_destroy_chm_compressor() | ||
1372 | */ | ||
1373 | struct mschm_compressor { | ||
1374 | /** | ||
1375 | * Generates a CHM help file. | ||
1376 | * | ||
1377 | * The help file will contain up to two sections, an Uncompressed | ||
1378 | * section and potentially an MSCompressed (LZX compressed) | ||
1379 | * section. | ||
1380 | * | ||
1381 | * While the contents listing of a CHM file is always in lexical order, | ||
1382 | * the file list passed in will be taken as the correct order for files | ||
1383 | * within the sections. It is in your interest to place similar files | ||
1384 | * together for better compression. | ||
1385 | * | ||
1386 | * There are two modes of generation, to use a temporary file or not to | ||
1387 | * use one. See use_temporary_file() for the behaviour of generate() in | ||
1388 | * these two different modes. | ||
1389 | * | ||
1390 | * @param self a self-referential pointer to the mschm_compressor | ||
1391 | * instance being called | ||
1392 | * @param file_list an array of mschmc_file structures, terminated | ||
1393 | * with an entry whose mschmc_file::section field is | ||
1394 | * #MSCHMC_ENDLIST. The order of the list is | ||
1395 | * preserved within each section. The length of any | ||
1396 | * mschmc_file::chm_filename string cannot exceed | ||
1397 | * roughly 4096 bytes. Each source file must be able | ||
1398 | * to supply as many bytes as given in the | ||
1399 | * mschmc_file::length field. | ||
1400 | * @param output_file the file to write the generated CHM helpfile to. | ||
1401 | * This is passed directly to mspack_system::open() | ||
1402 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1403 | * @see use_temporary_file() set_param() | ||
1404 | */ | ||
1405 | int (*generate)(struct mschm_compressor *self, | ||
1406 | struct mschmc_file file_list[], | ||
1407 | const char *output_file); | ||
1408 | |||
1409 | /** | ||
1410 | * Specifies whether a temporary file is used during CHM generation. | ||
1411 | * | ||
1412 | * The CHM file format includes data about the compressed section (such | ||
1413 | * as its overall size) that is stored in the output CHM file prior to | ||
1414 | * the compressed section itself. This unavoidably requires that the | ||
1415 | * compressed section has to be generated, before these details can be | ||
1416 | * set. There are several ways this can be handled. Firstly, the | ||
1417 | * compressed section could be generated entirely in memory before | ||
1418 | * writing any of the output CHM file. This approach is not used in | ||
1419 | * libmspack, as the compressed section can exceed the addressable | ||
1420 | * memory space on most architectures. | ||
1421 | * | ||
1422 | * libmspack has two options, either to write these unknowable sections | ||
1423 | * with blank data, generate the compressed section, then re-open the | ||
1424 | * output file for update once the compressed section has been | ||
1425 | * completed, or to write the compressed section to a temporary file, | ||
1426 | * then write the entire output file at once, performing a simple | ||
1427 | * file-to-file copy for the compressed section. | ||
1428 | * | ||
1429 | * The simple solution of buffering the entire compressed section in | ||
1430 | * memory can still be used, if desired. As the temporary file's | ||
1431 | * filename is passed directly to mspack_system::open(), it is possible | ||
1432 | * for a custom mspack_system implementation to hold this file in memory, | ||
1433 | * without writing to a disk. | ||
1434 | * | ||
1435 | * If a temporary file is set, generate() performs the following | ||
1436 | * sequence of events: the temporary file is opened for writing, the | ||
1437 | * compression algorithm writes to the temporary file, the temporary | ||
1438 | * file is closed. Then the output file is opened for writing and the | ||
1439 | * temporary file is re-opened for reading. The output file is written | ||
1440 | * and the temporary file is read from. Both files are then closed. The | ||
1441 | * temporary file itself is not deleted. If that is desired, the | ||
1442 | * temporary file should be deleted after the completion of generate(), | ||
1443 | * if it exists. | ||
1444 | * | ||
1445 | * If a temporary file is set not to be used, generate() performs the | ||
1446 | * following sequence of events: the output file is opened for writing, | ||
1447 | * then it is written and closed. The output file is then re-opened for | ||
1448 | * update, the appropriate sections are seek()ed to and re-written, then | ||
1449 | * the output file is closed. | ||
1450 | * | ||
1451 | * @param self a self-referential pointer to the | ||
1452 | * mschm_compressor instance being called | ||
1453 | * @param use_temp_file non-zero if the temporary file should be used, | ||
1454 | * zero if the temporary file should not be used. | ||
1455 | * @param temp_file a file to temporarily write compressed data to, | ||
1456 | * before opening it for reading and copying the | ||
1457 | * contents to the output file. This is passed | ||
1458 | * directly to mspack_system::open(). | ||
1459 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1460 | * @see generate() | ||
1461 | */ | ||
1462 | int (*use_temporary_file)(struct mschm_compressor *self, | ||
1463 | int use_temp_file, | ||
1464 | const char *temp_file); | ||
1465 | /** | ||
1466 | * Sets a CHM compression engine parameter. | ||
1467 | * | ||
1468 | * The following parameters are defined: | ||
1469 | |||
1470 | * - #MSCHMC_PARAM_TIMESTAMP: Sets the "timestamp" of the CHM file | ||
1471 | * generated. This is not a timestamp, see mschmd_header::timestamp | ||
1472 | * for a description. If this timestamp is 0, generate() will use its | ||
1473 | * own algorithm for making a unique ID, based on the lengths and | ||
1474 | * names of files in the CHM itself. Defaults to 0, any value between | ||
1475 | * 0 and (2^32)-1 is valid. | ||
1476 | * - #MSCHMC_PARAM_LANGUAGE: Sets the "language" of the CHM file | ||
1477 | * generated. This is not the language used in the CHM file, but the | ||
1478 | * language setting of the user who ran the HTMLHelp compiler. It | ||
1479 | * defaults to 0x0409. The valid range is between 0x0000 and 0x7F7F. | ||
1480 | * - #MSCHMC_PARAM_LZXWINDOW: Sets the size of the LZX history window, | ||
1481 | * which is also the interval at which the compressed data stream can be | ||
1482 | * randomly accessed. The value is not a size in bytes, but a power of | ||
1483 | * two. The default value is 16 (which makes the window 2^16 bytes, or | ||
1484 | * 64 kilobytes), the valid range is from 15 (32 kilobytes) to 21 (2 | ||
1485 | * megabytes). | ||
1486 | * - #MSCHMC_PARAM_DENSITY: Sets the "density" of quick reference | ||
1487 | * entries stored at the end of directory listing chunk. Each chunk is | ||
1488 | * 4096 bytes in size, and contains as many file entries as there is | ||
1489 | * room for. At the other end of the chunk, a list of "quick reference" | ||
1490 | * pointers is included. The offset of every 'N'th file entry is given a | ||
1491 | * quick reference, where N = (2^density) + 1. The default density is | ||
1492 | * 2. The smallest density is 0 (N=2), the maximum is 10 (N=1025). As | ||
1493 | * each file entry requires at least 5 bytes, the maximum number of | ||
1494 | * entries in a single chunk is roughly 800, so the maximum value 10 | ||
1495 | * can be used to indicate there are no quickrefs at all. | ||
1496 | * - #MSCHMC_PARAM_INDEX: Sets whether or not to include quick lookup | ||
1497 | * index chunk(s), in addition to normal directory listing chunks. A | ||
1498 | * value of zero means no index chunks will be created, a non-zero value | ||
1499 | * means index chunks will be created. The default is zero, "don't | ||
1500 | * create an index". | ||
1501 | * | ||
1502 | * @param self a self-referential pointer to the mschm_compressor | ||
1503 | * instance being called | ||
1504 | * @param param the parameter to set | ||
1505 | * @param value the value to set the parameter to | ||
1506 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there | ||
1507 | * is a problem with either parameter or value. | ||
1508 | * @see generate() | ||
1509 | */ | ||
1510 | int (*set_param)(struct mschm_compressor *self, | ||
1511 | int param, | ||
1512 | unsigned int value); | ||
1513 | |||
1514 | /** | ||
1515 | * Returns the error code set by the most recently called method. | ||
1516 | * | ||
1517 | * @param self a self-referential pointer to the mschm_compressor | ||
1518 | * instance being called | ||
1519 | * @return the most recent error code | ||
1520 | * @see set_param(), generate() | ||
1521 | */ | ||
1522 | int (*last_error)(struct mschm_compressor *self); | ||
1523 | }; | ||
1524 | |||
1525 | /** | ||
1526 | * A decompressor for .CHM (Microsoft HTMLHelp) files | ||
1527 | * | ||
1528 | * All fields are READ ONLY. | ||
1529 | * | ||
1530 | * @see mspack_create_chm_decompressor(), mspack_destroy_chm_decompressor() | ||
1531 | */ | ||
1532 | struct mschm_decompressor { | ||
1533 | /** | ||
1534 | * Opens a CHM helpfile and reads its contents. | ||
1535 | * | ||
1536 | * If the file opened is a valid CHM helpfile, all headers will be read | ||
1537 | * and a mschmd_header structure will be returned, with a full list of | ||
1538 | * files. | ||
1539 | * | ||
1540 | * In the case of an error occuring, NULL is returned and the error code | ||
1541 | * is available from last_error(). | ||
1542 | * | ||
1543 | * The filename pointer should be considered "in use" until close() is | ||
1544 | * called on the CHM helpfile. | ||
1545 | * | ||
1546 | * @param self a self-referential pointer to the mschm_decompressor | ||
1547 | * instance being called | ||
1548 | * @param filename the filename of the CHM helpfile. This is passed | ||
1549 | * directly to mspack_system::open(). | ||
1550 | * @return a pointer to a mschmd_header structure, or NULL on failure | ||
1551 | * @see close() | ||
1552 | */ | ||
1553 | struct mschmd_header *(*open)(struct mschm_decompressor *self, | ||
1554 | const char *filename); | ||
1555 | |||
1556 | /** | ||
1557 | * Closes a previously opened CHM helpfile. | ||
1558 | * | ||
1559 | * This closes a CHM helpfile, frees the mschmd_header and all | ||
1560 | * mschmd_file structures associated with it (if any). This works on | ||
1561 | * both helpfiles opened with open() and helpfiles opened with | ||
1562 | * fast_open(). | ||
1563 | * | ||
1564 | * The CHM header pointer is now invalid and cannot be used again. All | ||
1565 | * mschmd_file pointers referencing that CHM are also now invalid, and | ||
1566 | * cannot be used again. | ||
1567 | * | ||
1568 | * @param self a self-referential pointer to the mschm_decompressor | ||
1569 | * instance being called | ||
1570 | * @param chm the CHM helpfile to close | ||
1571 | * @see open(), fast_open() | ||
1572 | */ | ||
1573 | void (*close)(struct mschm_decompressor *self, | ||
1574 | struct mschmd_header *chm); | ||
1575 | |||
1576 | /** | ||
1577 | * Extracts a file from a CHM helpfile. | ||
1578 | * | ||
1579 | * This extracts a file from a CHM helpfile and writes it to the given | ||
1580 | * filename. The filename of the file, mscabd_file::filename, is not | ||
1581 | * used by extract(), but can be used by the caller as a guide for | ||
1582 | * constructing an appropriate filename. | ||
1583 | * | ||
1584 | * This method works both with files found in the mschmd_header::files | ||
1585 | * and mschmd_header::sysfiles list and mschmd_file structures generated | ||
1586 | * on the fly by fast_find(). | ||
1587 | * | ||
1588 | * @param self a self-referential pointer to the mschm_decompressor | ||
1589 | * instance being called | ||
1590 | * @param file the file to be decompressed | ||
1591 | * @param filename the filename of the file being written to | ||
1592 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1593 | */ | ||
1594 | int (*extract)(struct mschm_decompressor *self, | ||
1595 | struct mschmd_file *file, | ||
1596 | const char *filename); | ||
1597 | |||
1598 | /** | ||
1599 | * Returns the error code set by the most recently called method. | ||
1600 | * | ||
1601 | * This is useful for open() and fast_open(), which do not return an | ||
1602 | * error code directly. | ||
1603 | * | ||
1604 | * @param self a self-referential pointer to the mschm_decompressor | ||
1605 | * instance being called | ||
1606 | * @return the most recent error code | ||
1607 | * @see open(), extract() | ||
1608 | */ | ||
1609 | int (*last_error)(struct mschm_decompressor *self); | ||
1610 | |||
1611 | /** | ||
1612 | * Opens a CHM helpfile quickly. | ||
1613 | * | ||
1614 | * If the file opened is a valid CHM helpfile, only essential headers | ||
1615 | * will be read. A mschmd_header structure will be still be returned, as | ||
1616 | * with open(), but the mschmd_header::files field will be NULL. No | ||
1617 | * files details will be automatically read. The fast_find() method | ||
1618 | * must be used to obtain file details. | ||
1619 | * | ||
1620 | * In the case of an error occuring, NULL is returned and the error code | ||
1621 | * is available from last_error(). | ||
1622 | * | ||
1623 | * The filename pointer should be considered "in use" until close() is | ||
1624 | * called on the CHM helpfile. | ||
1625 | * | ||
1626 | * @param self a self-referential pointer to the mschm_decompressor | ||
1627 | * instance being called | ||
1628 | * @param filename the filename of the CHM helpfile. This is passed | ||
1629 | * directly to mspack_system::open(). | ||
1630 | * @return a pointer to a mschmd_header structure, or NULL on failure | ||
1631 | * @see open(), close(), fast_find(), extract() | ||
1632 | */ | ||
1633 | struct mschmd_header *(*fast_open)(struct mschm_decompressor *self, | ||
1634 | const char *filename); | ||
1635 | |||
1636 | /** | ||
1637 | * Finds file details quickly. | ||
1638 | * | ||
1639 | * Instead of reading all CHM helpfile headers and building a list of | ||
1640 | * files, fast_open() and fast_find() are intended for finding file | ||
1641 | * details only when they are needed. The CHM file format includes an | ||
1642 | * on-disk file index to allow this. | ||
1643 | * | ||
1644 | * Given a case-sensitive filename, fast_find() will search the on-disk | ||
1645 | * index for that file. | ||
1646 | * | ||
1647 | * If the file was found, the caller-provided mschmd_file structure will | ||
1648 | * be filled out like so: | ||
1649 | * - section: the correct value for the found file | ||
1650 | * - offset: the correct value for the found file | ||
1651 | * - length: the correct value for the found file | ||
1652 | * - all other structure elements: NULL or 0 | ||
1653 | * | ||
1654 | * If the file was not found, MSPACK_ERR_OK will still be returned as the | ||
1655 | * result, but the caller-provided structure will be filled out like so: | ||
1656 | * - section: NULL | ||
1657 | * - offset: 0 | ||
1658 | * - length: 0 | ||
1659 | * - all other structure elements: NULL or 0 | ||
1660 | * | ||
1661 | * This method is intended to be used in conjunction with CHM helpfiles | ||
1662 | * opened with fast_open(), but it also works with helpfiles opened | ||
1663 | * using the regular open(). | ||
1664 | * | ||
1665 | * @param self a self-referential pointer to the mschm_decompressor | ||
1666 | * instance being called | ||
1667 | * @param chm the CHM helpfile to search for the file | ||
1668 | * @param filename the filename of the file to search for | ||
1669 | * @param f_ptr a pointer to a caller-provded mschmd_file structure | ||
1670 | * @param f_size <tt>sizeof(struct mschmd_file)</tt> | ||
1671 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1672 | * @see open(), close(), fast_find(), extract() | ||
1673 | */ | ||
1674 | int (*fast_find)(struct mschm_decompressor *self, | ||
1675 | struct mschmd_header *chm, | ||
1676 | const char *filename, | ||
1677 | struct mschmd_file *f_ptr, | ||
1678 | int f_size); | ||
1679 | }; | ||
1680 | |||
1681 | /* --- support for .LIT (EBook) file format -------------------------------- */ | ||
1682 | |||
1683 | /** TODO */ | ||
1684 | struct mslit_compressor { | ||
1685 | int dummy; | ||
1686 | }; | ||
1687 | |||
1688 | /** TODO */ | ||
1689 | struct mslit_decompressor { | ||
1690 | int dummy; | ||
1691 | }; | ||
1692 | |||
1693 | |||
1694 | /* --- support for .HLP (MS Help) file format ------------------------------ */ | ||
1695 | |||
1696 | /** TODO */ | ||
1697 | struct mshlp_compressor { | ||
1698 | int dummy; | ||
1699 | }; | ||
1700 | |||
1701 | /** TODO */ | ||
1702 | struct mshlp_decompressor { | ||
1703 | int dummy; | ||
1704 | }; | ||
1705 | |||
1706 | |||
1707 | /* --- support for SZDD file format ---------------------------------------- */ | ||
1708 | |||
1709 | /** msszdd_compressor::set_param() parameter: the missing character */ | ||
1710 | #define MSSZDDC_PARAM_MISSINGCHAR (0) | ||
1711 | |||
1712 | /** msszddd_header::format value - a regular SZDD file */ | ||
1713 | #define MSSZDD_FMT_NORMAL (0) | ||
1714 | |||
1715 | /** msszddd_header::format value - a special QBasic SZDD file */ | ||
1716 | #define MSSZDD_FMT_QBASIC (1) | ||
1717 | |||
1718 | /** | ||
1719 | * A structure which represents an SZDD compressed file. | ||
1720 | * | ||
1721 | * All fields are READ ONLY. | ||
1722 | */ | ||
1723 | struct msszddd_header { | ||
1724 | /** The file format; either #MSSZDD_FMT_NORMAL or #MSSZDD_FMT_QBASIC */ | ||
1725 | int format; | ||
1726 | |||
1727 | /** The amount of data in the SZDD file once uncompressed. */ | ||
1728 | off_t length; | ||
1729 | |||
1730 | /** | ||
1731 | * The last character in the filename, traditionally replaced with an | ||
1732 | * underscore to show the file is compressed. The null character is used | ||
1733 | * to show that this character has not been stored (e.g. because the | ||
1734 | * filename is not known). Generally, only characters that may appear in | ||
1735 | * an MS-DOS filename (except ".") are valid. | ||
1736 | */ | ||
1737 | char missing_char; | ||
1738 | }; | ||
1739 | |||
1740 | /** | ||
1741 | * A compressor for the SZDD file format. | ||
1742 | * | ||
1743 | * All fields are READ ONLY. | ||
1744 | * | ||
1745 | * @see mspack_create_szdd_compressor(), mspack_destroy_szdd_compressor() | ||
1746 | */ | ||
1747 | struct msszdd_compressor { | ||
1748 | /** | ||
1749 | * Reads an input file and creates a compressed output file in the | ||
1750 | * SZDD compressed file format. The SZDD compression format is quick | ||
1751 | * but gives poor compression. It is possible for the compressed output | ||
1752 | * file to be larger than the input file. | ||
1753 | * | ||
1754 | * Conventionally, SZDD compressed files have the final character in | ||
1755 | * their filename replaced with an underscore, to show they are | ||
1756 | * compressed. The missing character is stored in the compressed file | ||
1757 | * itself. This is due to the restricted filename conventions of MS-DOS, | ||
1758 | * most operating systems, such as UNIX, simply append another file | ||
1759 | * extension to the existing filename. As mspack does not deal with | ||
1760 | * filenames, this is left up to you. If you wish to set the missing | ||
1761 | * character stored in the file header, use set_param() with the | ||
1762 | * #MSSZDDC_PARAM_MISSINGCHAR parameter. | ||
1763 | * | ||
1764 | * "Stream" compression (where the length of the input data is not | ||
1765 | * known) is not possible. The length of the input data is stored in the | ||
1766 | * header of the SZDD file and must therefore be known before any data | ||
1767 | * is compressed. Due to technical limitations of the file format, the | ||
1768 | * maximum size of uncompressed file that will be accepted is 2147483647 | ||
1769 | * bytes. | ||
1770 | * | ||
1771 | * @param self a self-referential pointer to the msszdd_compressor | ||
1772 | * instance being called | ||
1773 | * @param input the name of the file to compressed. This is passed | ||
1774 | * passed directly to mspack_system::open() | ||
1775 | * @param output the name of the file to write compressed data to. | ||
1776 | * This is passed directly to mspack_system::open(). | ||
1777 | * @param length the length of the uncompressed file, or -1 to indicate | ||
1778 | * that this should be determined automatically by using | ||
1779 | * mspack_system::seek() on the input file. | ||
1780 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1781 | * @see set_param() | ||
1782 | */ | ||
1783 | int (*compress)(struct msszdd_compressor *self, | ||
1784 | const char *input, | ||
1785 | const char *output, | ||
1786 | off_t length); | ||
1787 | |||
1788 | /** | ||
1789 | * Sets an SZDD compression engine parameter. | ||
1790 | * | ||
1791 | * The following parameters are defined: | ||
1792 | |||
1793 | * - #MSSZDDC_PARAM_CHARACTER: the "missing character", the last character | ||
1794 | * in the uncompressed file's filename, which is traditionally replaced | ||
1795 | * with an underscore to show the file is compressed. Traditionally, | ||
1796 | * this can only be a character that is a valid part of an MS-DOS, | ||
1797 | * filename, but libmspack permits any character between 0x00 and 0xFF | ||
1798 | * to be stored. 0x00 is the default, and it represents "no character | ||
1799 | * stored". | ||
1800 | * | ||
1801 | * @param self a self-referential pointer to the msszdd_compressor | ||
1802 | * instance being called | ||
1803 | * @param param the parameter to set | ||
1804 | * @param value the value to set the parameter to | ||
1805 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there | ||
1806 | * is a problem with either parameter or value. | ||
1807 | * @see compress() | ||
1808 | */ | ||
1809 | int (*set_param)(struct msszdd_compressor *self, | ||
1810 | int param, | ||
1811 | unsigned int value); | ||
1812 | |||
1813 | /** | ||
1814 | * Returns the error code set by the most recently called method. | ||
1815 | * | ||
1816 | * @param self a self-referential pointer to the msszdd_compressor | ||
1817 | * instance being called | ||
1818 | * @return the most recent error code | ||
1819 | * @see compress() | ||
1820 | */ | ||
1821 | int (*last_error)(struct mschm_decompressor *self); | ||
1822 | }; | ||
1823 | |||
1824 | /** | ||
1825 | * A decompressor for SZDD compressed files. | ||
1826 | * | ||
1827 | * All fields are READ ONLY. | ||
1828 | * | ||
1829 | * @see mspack_create_szdd_decompressor(), mspack_destroy_szdd_decompressor() | ||
1830 | */ | ||
1831 | struct msszdd_decompressor { | ||
1832 | /** | ||
1833 | * Opens a SZDD file and reads the header. | ||
1834 | * | ||
1835 | * If the file opened is a valid SZDD file, all headers will be read and | ||
1836 | * a msszddd_header structure will be returned. | ||
1837 | * | ||
1838 | * In the case of an error occuring, NULL is returned and the error code | ||
1839 | * is available from last_error(). | ||
1840 | * | ||
1841 | * The filename pointer should be considered "in use" until close() is | ||
1842 | * called on the SZDD file. | ||
1843 | * | ||
1844 | * @param self a self-referential pointer to the msszdd_decompressor | ||
1845 | * instance being called | ||
1846 | * @param filename the filename of the SZDD compressed file. This is | ||
1847 | * passed directly to mspack_system::open(). | ||
1848 | * @return a pointer to a msszddd_header structure, or NULL on failure | ||
1849 | * @see close() | ||
1850 | */ | ||
1851 | struct msszddd_header *(*open)(struct msszdd_decompressor *self, | ||
1852 | const char *filename); | ||
1853 | |||
1854 | /** | ||
1855 | * Closes a previously opened SZDD file. | ||
1856 | * | ||
1857 | * This closes a SZDD file and frees the msszddd_header associated with | ||
1858 | * it. | ||
1859 | * | ||
1860 | * The SZDD header pointer is now invalid and cannot be used again. | ||
1861 | * | ||
1862 | * @param self a self-referential pointer to the msszdd_decompressor | ||
1863 | * instance being called | ||
1864 | * @param szdd the SZDD file to close | ||
1865 | * @see open() | ||
1866 | */ | ||
1867 | void (*close)(struct msszdd_decompressor *self, | ||
1868 | struct msszddd_header *szdd); | ||
1869 | |||
1870 | /** | ||
1871 | * Extracts the compressed data from a SZDD file. | ||
1872 | * | ||
1873 | * This decompresses the compressed SZDD data stream and writes it to | ||
1874 | * an output file. | ||
1875 | * | ||
1876 | * @param self a self-referential pointer to the msszdd_decompressor | ||
1877 | * instance being called | ||
1878 | * @param szdd the SZDD file to extract data from | ||
1879 | * @param filename the filename to write the decompressed data to. This | ||
1880 | * is passed directly to mspack_system::open(). | ||
1881 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1882 | */ | ||
1883 | int (*extract)(struct msszdd_decompressor *self, | ||
1884 | struct msszddd_header *szdd, | ||
1885 | const char *filename); | ||
1886 | |||
1887 | /** | ||
1888 | * Decompresses an SZDD file to an output file in one step. | ||
1889 | * | ||
1890 | * This opens an SZDD file as input, reads the header, then decompresses | ||
1891 | * the compressed data immediately to an output file, finally closing | ||
1892 | * both the input and output file. It is more convenient to use than | ||
1893 | * open() then extract() then close(), if you do not need to know the | ||
1894 | * SZDD output size or missing character. | ||
1895 | * | ||
1896 | * @param self a self-referential pointer to the msszdd_decompressor | ||
1897 | * instance being called | ||
1898 | * @param input the filename of the input SZDD file. This is passed | ||
1899 | * directly to mspack_system::open(). | ||
1900 | * @param output the filename to write the decompressed data to. This | ||
1901 | * is passed directly to mspack_system::open(). | ||
1902 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1903 | */ | ||
1904 | int (*decompress)(struct msszdd_decompressor *self, | ||
1905 | const char *input, | ||
1906 | const char *output); | ||
1907 | |||
1908 | /** | ||
1909 | * Returns the error code set by the most recently called method. | ||
1910 | * | ||
1911 | * This is useful for open() which does not return an | ||
1912 | * error code directly. | ||
1913 | * | ||
1914 | * @param self a self-referential pointer to the msszdd_decompressor | ||
1915 | * instance being called | ||
1916 | * @return the most recent error code | ||
1917 | * @see open(), extract(), decompress() | ||
1918 | */ | ||
1919 | int (*last_error)(struct msszdd_decompressor *self); | ||
1920 | }; | ||
1921 | |||
1922 | /* --- support for KWAJ file format ---------------------------------------- */ | ||
1923 | |||
1924 | /** mskwaj_compressor::set_param() parameter: compression type */ | ||
1925 | #define MSKWAJC_PARAM_COMP_TYPE (0) | ||
1926 | |||
1927 | /** mskwaj_compressor::set_param() parameter: include the length of the | ||
1928 | * uncompressed file in the header? | ||
1929 | */ | ||
1930 | #define MSKWAJC_PARAM_INCLUDE_LENGTH (1) | ||
1931 | |||
1932 | /** KWAJ compression type: no compression. */ | ||
1933 | #define MSKWAJ_COMP_NONE (0) | ||
1934 | /** KWAJ compression type: no compression, 0xFF XOR "encryption". */ | ||
1935 | #define MSKWAJ_COMP_XOR (1) | ||
1936 | /** KWAJ compression type: LZSS (same method as SZDD) */ | ||
1937 | #define MSKWAJ_COMP_SZDD (2) | ||
1938 | /** KWAJ compression type: LZ+Huffman compression */ | ||
1939 | #define MSKWAJ_COMP_LZH (3) | ||
1940 | |||
1941 | /** KWAJ optional header flag: decompressed file length is included */ | ||
1942 | #define MSKWAJ_HDR_HASLENGTH (0x01) | ||
1943 | |||
1944 | /** KWAJ optional header flag: unknown 2-byte structure is included */ | ||
1945 | #define MSKWAJ_HDR_HASUNKNOWN1 (0x02) | ||
1946 | |||
1947 | /** KWAJ optional header flag: unknown multi-sized structure is included */ | ||
1948 | #define MSKWAJ_HDR_HASUNKNOWN2 (0x04) | ||
1949 | |||
1950 | /** KWAJ optional header flag: file name (no extension) is included */ | ||
1951 | #define MSKWAJ_HDR_HASFILENAME (0x08) | ||
1952 | |||
1953 | /** KWAJ optional header flag: file extension is included */ | ||
1954 | #define MSKWAJ_HDR_HASFILEEXT (0x10) | ||
1955 | |||
1956 | /** KWAJ optional header flag: extra text is included */ | ||
1957 | #define MSKWAJ_HDR_HASEXTRATEXT (0x20) | ||
1958 | |||
1959 | /** | ||
1960 | * A structure which represents an KWAJ compressed file. | ||
1961 | * | ||
1962 | * All fields are READ ONLY. | ||
1963 | */ | ||
1964 | struct mskwajd_header { | ||
1965 | /** The compression type; should be one of #MSKWAJ_COMP_NONE, | ||
1966 | * #MSKWAJ_COMP_XOR, #MSKWAJ_COMP_SZDD or #MSKWAJ_COMP_LZH | ||
1967 | */ | ||
1968 | unsigned short comp_type; | ||
1969 | |||
1970 | /** The offset in the file where the compressed data stream begins */ | ||
1971 | off_t data_offset; | ||
1972 | |||
1973 | /** Flags indicating which optional headers were included. */ | ||
1974 | int headers; | ||
1975 | |||
1976 | /** The amount of uncompressed data in the file, or 0 if not present. */ | ||
1977 | off_t length; | ||
1978 | |||
1979 | /** output filename, or NULL if not present */ | ||
1980 | char *filename; | ||
1981 | |||
1982 | /** extra uncompressed data (usually text) in the header. | ||
1983 | * This data can contain nulls so use extra_length to get the size. | ||
1984 | */ | ||
1985 | char *extra; | ||
1986 | |||
1987 | /** length of extra uncompressed data in the header */ | ||
1988 | unsigned short extra_length; | ||
1989 | }; | ||
1990 | |||
1991 | /** | ||
1992 | * A compressor for the KWAJ file format. | ||
1993 | * | ||
1994 | * All fields are READ ONLY. | ||
1995 | * | ||
1996 | * @see mspack_create_kwaj_compressor(), mspack_destroy_kwaj_compressor() | ||
1997 | */ | ||
1998 | struct mskwaj_compressor { | ||
1999 | /** | ||
2000 | * Reads an input file and creates a compressed output file in the | ||
2001 | * KWAJ compressed file format. The KWAJ compression format is quick | ||
2002 | * but gives poor compression. It is possible for the compressed output | ||
2003 | * file to be larger than the input file. | ||
2004 | * | ||
2005 | * @param self a self-referential pointer to the mskwaj_compressor | ||
2006 | * instance being called | ||
2007 | * @param input the name of the file to compressed. This is passed | ||
2008 | * passed directly to mspack_system::open() | ||
2009 | * @param output the name of the file to write compressed data to. | ||
2010 | * This is passed directly to mspack_system::open(). | ||
2011 | * @param length the length of the uncompressed file, or -1 to indicate | ||
2012 | * that this should be determined automatically by using | ||
2013 | * mspack_system::seek() on the input file. | ||
2014 | * @return an error code, or MSPACK_ERR_OK if successful | ||
2015 | * @see set_param() | ||
2016 | */ | ||
2017 | int (*compress)(struct mskwaj_compressor *self, | ||
2018 | const char *input, | ||
2019 | const char *output, | ||
2020 | off_t length); | ||
2021 | |||
2022 | /** | ||
2023 | * Sets an KWAJ compression engine parameter. | ||
2024 | * | ||
2025 | * The following parameters are defined: | ||
2026 | * | ||
2027 | * - #MSKWAJC_PARAM_COMP_TYPE: the compression method to use. Must | ||
2028 | * be one of #MSKWAJC_COMP_NONE, #MSKWAJC_COMP_XOR, #MSKWAJ_COMP_SZDD | ||
2029 | * or #MSKWAJ_COMP_LZH. The default is #MSKWAJ_COMP_LZH. | ||
2030 | * | ||
2031 | * - #MSKWAJC_PARAM_INCLUDE_LENGTH: a boolean; should the compressed | ||
2032 | * output file should include the uncompressed length of the input | ||
2033 | * file in the header? This adds 4 bytes to the size of the output | ||
2034 | * file. A value of zero says "no", non-zero says "yes". The default | ||
2035 | * is "no". | ||
2036 | * | ||
2037 | * @param self a self-referential pointer to the mskwaj_compressor | ||
2038 | * instance being called | ||
2039 | * @param param the parameter to set | ||
2040 | * @param value the value to set the parameter to | ||
2041 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there | ||
2042 | * is a problem with either parameter or value. | ||
2043 | * @see generate() | ||
2044 | */ | ||
2045 | int (*set_param)(struct mskwaj_compressor *self, | ||
2046 | int param, | ||
2047 | unsigned int value); | ||
2048 | |||
2049 | |||
2050 | /** | ||
2051 | * Sets the original filename of the file before compression, | ||
2052 | * which will be stored in the header of the output file. | ||
2053 | * | ||
2054 | * The filename should be a null-terminated string, it must be an | ||
2055 | * MS-DOS "8.3" type filename (up to 8 bytes for the filename, then | ||
2056 | * optionally a "." and up to 3 bytes for a filename extension). | ||
2057 | * | ||
2058 | * If NULL is passed as the filename, no filename is included in the | ||
2059 | * header. This is the default. | ||
2060 | * | ||
2061 | * @param self a self-referential pointer to the mskwaj_compressor | ||
2062 | * instance being called | ||
2063 | * @param filename the original filename to use | ||
2064 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if the | ||
2065 | * filename is too long | ||
2066 | */ | ||
2067 | int (*set_filename)(struct mskwaj_compressor *self, | ||
2068 | const char *filename); | ||
2069 | |||
2070 | /** | ||
2071 | * Sets arbitrary data that will be stored in the header of the | ||
2072 | * output file, uncompressed. It can be up to roughly 64 kilobytes, | ||
2073 | * as the overall size of the header must not exceed 65535 bytes. | ||
2074 | * The data can contain null bytes if desired. | ||
2075 | * | ||
2076 | * If NULL is passed as the data pointer, or zero is passed as the | ||
2077 | * length, no extra data is included in the header. This is the | ||
2078 | * default. | ||
2079 | * | ||
2080 | * @param self a self-referential pointer to the mskwaj_compressor | ||
2081 | * instance being called | ||
2082 | * @param data a pointer to the data to be stored in the header | ||
2083 | * @param bytes the length of the data in bytes | ||
2084 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS extra data | ||
2085 | * is too long | ||
2086 | */ | ||
2087 | int (*set_extra_data)(struct mskwaj_compressor *self, | ||
2088 | void *data, | ||
2089 | size_t bytes); | ||
2090 | |||
2091 | /** | ||
2092 | * Returns the error code set by the most recently called method. | ||
2093 | * | ||
2094 | * @param self a self-referential pointer to the mskwaj_compressor | ||
2095 | * instance being called | ||
2096 | * @return the most recent error code | ||
2097 | * @see compress() | ||
2098 | */ | ||
2099 | int (*last_error)(struct mschm_decompressor *self); | ||
2100 | }; | ||
2101 | |||
2102 | /** | ||
2103 | * A decompressor for KWAJ compressed files. | ||
2104 | * | ||
2105 | * All fields are READ ONLY. | ||
2106 | * | ||
2107 | * @see mspack_create_kwaj_decompressor(), mspack_destroy_kwaj_decompressor() | ||
2108 | */ | ||
2109 | struct mskwaj_decompressor { | ||
2110 | /** | ||
2111 | * Opens a KWAJ file and reads the header. | ||
2112 | * | ||
2113 | * If the file opened is a valid KWAJ file, all headers will be read and | ||
2114 | * a mskwajd_header structure will be returned. | ||
2115 | * | ||
2116 | * In the case of an error occuring, NULL is returned and the error code | ||
2117 | * is available from last_error(). | ||
2118 | * | ||
2119 | * The filename pointer should be considered "in use" until close() is | ||
2120 | * called on the KWAJ file. | ||
2121 | * | ||
2122 | * @param self a self-referential pointer to the mskwaj_decompressor | ||
2123 | * instance being called | ||
2124 | * @param filename the filename of the KWAJ compressed file. This is | ||
2125 | * passed directly to mspack_system::open(). | ||
2126 | * @return a pointer to a mskwajd_header structure, or NULL on failure | ||
2127 | * @see close() | ||
2128 | */ | ||
2129 | struct mskwajd_header *(*open)(struct mskwaj_decompressor *self, | ||
2130 | const char *filename); | ||
2131 | |||
2132 | /** | ||
2133 | * Closes a previously opened KWAJ file. | ||
2134 | * | ||
2135 | * This closes a KWAJ file and frees the mskwajd_header associated | ||
2136 | * with it. The KWAJ header pointer is now invalid and cannot be | ||
2137 | * used again. | ||
2138 | * | ||
2139 | * @param self a self-referential pointer to the mskwaj_decompressor | ||
2140 | * instance being called | ||
2141 | * @param kwaj the KWAJ file to close | ||
2142 | * @see open() | ||
2143 | */ | ||
2144 | void (*close)(struct mskwaj_decompressor *self, | ||
2145 | struct mskwajd_header *kwaj); | ||
2146 | |||
2147 | /** | ||
2148 | * Extracts the compressed data from a KWAJ file. | ||
2149 | * | ||
2150 | * This decompresses the compressed KWAJ data stream and writes it to | ||
2151 | * an output file. | ||
2152 | * | ||
2153 | * @param self a self-referential pointer to the mskwaj_decompressor | ||
2154 | * instance being called | ||
2155 | * @param kwaj the KWAJ file to extract data from | ||
2156 | * @param filename the filename to write the decompressed data to. This | ||
2157 | * is passed directly to mspack_system::open(). | ||
2158 | * @return an error code, or MSPACK_ERR_OK if successful | ||
2159 | */ | ||
2160 | int (*extract)(struct mskwaj_decompressor *self, | ||
2161 | struct mskwajd_header *kwaj, | ||
2162 | const char *filename); | ||
2163 | |||
2164 | /** | ||
2165 | * Decompresses an KWAJ file to an output file in one step. | ||
2166 | * | ||
2167 | * This opens an KWAJ file as input, reads the header, then decompresses | ||
2168 | * the compressed data immediately to an output file, finally closing | ||
2169 | * both the input and output file. It is more convenient to use than | ||
2170 | * open() then extract() then close(), if you do not need to know the | ||
2171 | * KWAJ output size or output filename. | ||
2172 | * | ||
2173 | * @param self a self-referential pointer to the mskwaj_decompressor | ||
2174 | * instance being called | ||
2175 | * @param input the filename of the input KWAJ file. This is passed | ||
2176 | * directly to mspack_system::open(). | ||
2177 | * @param output the filename to write the decompressed data to. This | ||
2178 | * is passed directly to mspack_system::open(). | ||
2179 | * @return an error code, or MSPACK_ERR_OK if successful | ||
2180 | */ | ||
2181 | int (*decompress)(struct mskwaj_decompressor *self, | ||
2182 | const char *input, | ||
2183 | const char *output); | ||
2184 | |||
2185 | /** | ||
2186 | * Returns the error code set by the most recently called method. | ||
2187 | * | ||
2188 | * This is useful for open() which does not return an | ||
2189 | * error code directly. | ||
2190 | * | ||
2191 | * @param self a self-referential pointer to the mskwaj_decompressor | ||
2192 | * instance being called | ||
2193 | * @return the most recent error code | ||
2194 | * @see open(), search() | ||
2195 | */ | ||
2196 | int (*last_error)(struct mskwaj_decompressor *self); | ||
2197 | }; | ||
2198 | |||
2199 | #ifdef __cplusplus | ||
2200 | } | ||
2201 | #endif | ||
2202 | |||
2203 | #endif | ||
diff --git a/rbutil/rbutilqt/mspack/mszip.h b/rbutil/rbutilqt/mspack/mszip.h new file mode 100644 index 0000000000..13c9542ee6 --- /dev/null +++ b/rbutil/rbutilqt/mspack/mszip.h | |||
@@ -0,0 +1,121 @@ | |||
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 | ||
17 | extern "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 | |||
45 | struct 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 | */ | ||
85 | extern 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 | */ | ||
109 | extern int mszipd_decompress(struct mszipd_stream *zip, off_t out_bytes); | ||
110 | |||
111 | /* frees all stream associated with an MS-ZIP data stream | ||
112 | * | ||
113 | * - calls system->free() using the system pointer given in mszipd_init() | ||
114 | */ | ||
115 | void mszipd_free(struct mszipd_stream *zip); | ||
116 | |||
117 | #ifdef __cplusplus | ||
118 | } | ||
119 | #endif | ||
120 | |||
121 | #endif | ||
diff --git a/rbutil/rbutilqt/mspack/mszipc.c b/rbutil/rbutilqt/mspack/mszipc.c new file mode 100644 index 0000000000..2f1ecb2e87 --- /dev/null +++ b/rbutil/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.h> | ||
16 | #include <mszip.h> | ||
17 | |||
18 | /* todo */ | ||
diff --git a/rbutil/rbutilqt/mspack/mszipd.c b/rbutil/rbutilqt/mspack/mszipd.c new file mode 100644 index 0000000000..5b7ef4ff4d --- /dev/null +++ b/rbutil/rbutilqt/mspack/mszipd.c | |||
@@ -0,0 +1,475 @@ | |||
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.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 */ | ||
47 | static 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 */ | ||
53 | static 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 */ | ||
59 | static 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 */ | ||
65 | static 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 */ | ||
71 | static 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 | |||
91 | static 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 */ | ||
157 | static 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 | */ | ||
330 | static 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 | |||
342 | struct 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 | input_buffer_size = (input_buffer_size + 1) & -2; | ||
353 | if (!input_buffer_size) return NULL; | ||
354 | |||
355 | /* allocate decompression state */ | ||
356 | if (!(zip = (struct mszipd_stream *) system->alloc(system, sizeof(struct mszipd_stream)))) { | ||
357 | return NULL; | ||
358 | } | ||
359 | |||
360 | /* allocate input buffer */ | ||
361 | zip->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size); | ||
362 | if (!zip->inbuf) { | ||
363 | system->free(zip); | ||
364 | return NULL; | ||
365 | } | ||
366 | |||
367 | /* initialise decompression state */ | ||
368 | zip->sys = system; | ||
369 | zip->input = input; | ||
370 | zip->output = output; | ||
371 | zip->inbuf_size = input_buffer_size; | ||
372 | zip->input_end = 0; | ||
373 | zip->error = MSPACK_ERR_OK; | ||
374 | zip->repair_mode = repair_mode; | ||
375 | zip->flush_window = &mszipd_flush_window; | ||
376 | |||
377 | zip->i_ptr = zip->i_end = &zip->inbuf[0]; | ||
378 | zip->o_ptr = zip->o_end = NULL; | ||
379 | zip->bit_buffer = 0; zip->bits_left = 0; | ||
380 | return zip; | ||
381 | } | ||
382 | |||
383 | int mszipd_decompress(struct mszipd_stream *zip, off_t out_bytes) { | ||
384 | /* for the bit buffer */ | ||
385 | register unsigned int bit_buffer; | ||
386 | register int bits_left; | ||
387 | unsigned char *i_ptr, *i_end; | ||
388 | |||
389 | int i, state, error; | ||
390 | |||
391 | /* easy answers */ | ||
392 | if (!zip || (out_bytes < 0)) return MSPACK_ERR_ARGS; | ||
393 | if (zip->error) return zip->error; | ||
394 | |||
395 | /* flush out any stored-up bytes before we begin */ | ||
396 | i = zip->o_end - zip->o_ptr; | ||
397 | if ((off_t) i > out_bytes) i = (int) out_bytes; | ||
398 | if (i) { | ||
399 | if (zip->sys->write(zip->output, zip->o_ptr, i) != i) { | ||
400 | return zip->error = MSPACK_ERR_WRITE; | ||
401 | } | ||
402 | zip->o_ptr += i; | ||
403 | out_bytes -= i; | ||
404 | } | ||
405 | if (out_bytes == 0) return MSPACK_ERR_OK; | ||
406 | |||
407 | |||
408 | while (out_bytes > 0) { | ||
409 | /* unpack another block */ | ||
410 | RESTORE_BITS; | ||
411 | |||
412 | /* skip to next read 'CK' header */ | ||
413 | i = bits_left & 7; REMOVE_BITS(i); /* align to bytestream */ | ||
414 | state = 0; | ||
415 | do { | ||
416 | READ_BITS(i, 8); | ||
417 | if (i == 'C') state = 1; | ||
418 | else if ((state == 1) && (i == 'K')) state = 2; | ||
419 | else state = 0; | ||
420 | } while (state != 2); | ||
421 | |||
422 | /* inflate a block, repair and realign if necessary */ | ||
423 | zip->window_posn = 0; | ||
424 | zip->bytes_output = 0; | ||
425 | STORE_BITS; | ||
426 | if ((error = inflate(zip))) { | ||
427 | D(("inflate error %d", error)) | ||
428 | if (zip->repair_mode) { | ||
429 | /* recover partially-inflated buffers */ | ||
430 | if (zip->bytes_output == 0 && zip->window_posn > 0) { | ||
431 | zip->flush_window(zip, zip->window_posn); | ||
432 | } | ||
433 | zip->sys->message(NULL, "MSZIP error, %u bytes of data lost.", | ||
434 | MSZIP_FRAME_SIZE - zip->bytes_output); | ||
435 | for (i = zip->bytes_output; i < MSZIP_FRAME_SIZE; i++) { | ||
436 | zip->window[i] = '\0'; | ||
437 | } | ||
438 | zip->bytes_output = MSZIP_FRAME_SIZE; | ||
439 | } | ||
440 | else { | ||
441 | return zip->error = (error > 0) ? error : MSPACK_ERR_DECRUNCH; | ||
442 | } | ||
443 | } | ||
444 | zip->o_ptr = &zip->window[0]; | ||
445 | zip->o_end = &zip->o_ptr[zip->bytes_output]; | ||
446 | |||
447 | /* write a frame */ | ||
448 | i = (out_bytes < (off_t)zip->bytes_output) ? | ||
449 | (int)out_bytes : zip->bytes_output; | ||
450 | if (zip->sys->write(zip->output, zip->o_ptr, i) != i) { | ||
451 | return zip->error = MSPACK_ERR_WRITE; | ||
452 | } | ||
453 | |||
454 | /* mspack errors (i.e. read errors) are fatal and can't be recovered */ | ||
455 | if ((error > 0) && zip->repair_mode) return error; | ||
456 | |||
457 | zip->o_ptr += i; | ||
458 | out_bytes -= i; | ||
459 | } | ||
460 | |||
461 | if (out_bytes) { | ||
462 | D(("bytes left to output")) | ||
463 | return zip->error = MSPACK_ERR_DECRUNCH; | ||
464 | } | ||
465 | return MSPACK_ERR_OK; | ||
466 | } | ||
467 | |||
468 | void mszipd_free(struct mszipd_stream *zip) { | ||
469 | struct mspack_system *sys; | ||
470 | if (zip) { | ||
471 | sys = zip->sys; | ||
472 | sys->free(zip->inbuf); | ||
473 | sys->free(zip); | ||
474 | } | ||
475 | } | ||
diff --git a/rbutil/rbutilqt/mspack/qtm.h b/rbutil/rbutilqt/mspack/qtm.h new file mode 100644 index 0000000000..ab0bb4c32c --- /dev/null +++ b/rbutil/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 | ||
17 | extern "C" { | ||
18 | #endif | ||
19 | |||
20 | /* Quantum compression / decompression definitions */ | ||
21 | |||
22 | #define QTM_FRAME_SIZE (32768) | ||
23 | |||
24 | struct qtmd_modelsym { | ||
25 | unsigned short sym, cumfreq; | ||
26 | }; | ||
27 | |||
28 | struct qtmd_model { | ||
29 | int shiftsleft, entries; | ||
30 | struct qtmd_modelsym *syms; | ||
31 | }; | ||
32 | |||
33 | struct 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 | */ | ||
92 | extern 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 | */ | ||
116 | extern 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 | */ | ||
122 | void qtmd_free(struct qtmd_stream *qtm); | ||
123 | |||
124 | #ifdef __cplusplus | ||
125 | } | ||
126 | #endif | ||
127 | |||
128 | #endif | ||
diff --git a/rbutil/rbutilqt/mspack/qtmd.c b/rbutil/rbutilqt/mspack/qtmd.c new file mode 100644 index 0000000000..12b27f5608 --- /dev/null +++ b/rbutil/rbutilqt/mspack/qtmd.c | |||
@@ -0,0 +1,489 @@ | |||
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.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 | */ | ||
66 | static 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 | }; | ||
71 | static 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 | }; | ||
75 | static 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 | }; | ||
79 | static 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 | |||
124 | static 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 */ | ||
168 | static 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 | |||
186 | struct 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 | input_buffer_size = (input_buffer_size + 1) & -2; | ||
201 | if (input_buffer_size < 2) return NULL; | ||
202 | |||
203 | /* allocate decompression state */ | ||
204 | if (!(qtm = (struct qtmd_stream *) system->alloc(system, sizeof(struct qtmd_stream)))) { | ||
205 | return NULL; | ||
206 | } | ||
207 | |||
208 | /* allocate decompression window and input buffer */ | ||
209 | qtm->window = (unsigned char *) system->alloc(system, (size_t) window_size); | ||
210 | qtm->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size); | ||
211 | if (!qtm->window || !qtm->inbuf) { | ||
212 | system->free(qtm->window); | ||
213 | system->free(qtm->inbuf); | ||
214 | system->free(qtm); | ||
215 | return NULL; | ||
216 | } | ||
217 | |||
218 | /* initialise decompression state */ | ||
219 | qtm->sys = system; | ||
220 | qtm->input = input; | ||
221 | qtm->output = output; | ||
222 | qtm->inbuf_size = input_buffer_size; | ||
223 | qtm->window_size = window_size; | ||
224 | qtm->window_posn = 0; | ||
225 | qtm->frame_todo = QTM_FRAME_SIZE; | ||
226 | qtm->header_read = 0; | ||
227 | qtm->error = MSPACK_ERR_OK; | ||
228 | |||
229 | qtm->i_ptr = qtm->i_end = &qtm->inbuf[0]; | ||
230 | qtm->o_ptr = qtm->o_end = &qtm->window[0]; | ||
231 | qtm->input_end = 0; | ||
232 | qtm->bits_left = 0; | ||
233 | qtm->bit_buffer = 0; | ||
234 | |||
235 | /* initialise arithmetic coding models | ||
236 | * - model 4 depends on window size, ranges from 20 to 24 | ||
237 | * - model 5 depends on window size, ranges from 20 to 36 | ||
238 | * - model 6pos depends on window size, ranges from 20 to 42 | ||
239 | */ | ||
240 | i = window_bits * 2; | ||
241 | qtmd_init_model(&qtm->model0, &qtm->m0sym[0], 0, 64); | ||
242 | qtmd_init_model(&qtm->model1, &qtm->m1sym[0], 64, 64); | ||
243 | qtmd_init_model(&qtm->model2, &qtm->m2sym[0], 128, 64); | ||
244 | qtmd_init_model(&qtm->model3, &qtm->m3sym[0], 192, 64); | ||
245 | qtmd_init_model(&qtm->model4, &qtm->m4sym[0], 0, (i > 24) ? 24 : i); | ||
246 | qtmd_init_model(&qtm->model5, &qtm->m5sym[0], 0, (i > 36) ? 36 : i); | ||
247 | qtmd_init_model(&qtm->model6, &qtm->m6sym[0], 0, i); | ||
248 | qtmd_init_model(&qtm->model6len, &qtm->m6lsym[0], 0, 27); | ||
249 | qtmd_init_model(&qtm->model7, &qtm->m7sym[0], 0, 7); | ||
250 | |||
251 | /* all ok */ | ||
252 | return qtm; | ||
253 | } | ||
254 | |||
255 | int qtmd_decompress(struct qtmd_stream *qtm, off_t out_bytes) { | ||
256 | unsigned int frame_todo, frame_end, window_posn, match_offset, range; | ||
257 | unsigned char *window, *i_ptr, *i_end, *runsrc, *rundest; | ||
258 | int i, j, selector, extra, sym, match_length; | ||
259 | unsigned short H, L, C, symf; | ||
260 | |||
261 | register unsigned int bit_buffer; | ||
262 | register unsigned char bits_left; | ||
263 | |||
264 | /* easy answers */ | ||
265 | if (!qtm || (out_bytes < 0)) return MSPACK_ERR_ARGS; | ||
266 | if (qtm->error) return qtm->error; | ||
267 | |||
268 | /* flush out any stored-up bytes before we begin */ | ||
269 | i = qtm->o_end - qtm->o_ptr; | ||
270 | if ((off_t) i > out_bytes) i = (int) out_bytes; | ||
271 | if (i) { | ||
272 | if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { | ||
273 | return qtm->error = MSPACK_ERR_WRITE; | ||
274 | } | ||
275 | qtm->o_ptr += i; | ||
276 | out_bytes -= i; | ||
277 | } | ||
278 | if (out_bytes == 0) return MSPACK_ERR_OK; | ||
279 | |||
280 | /* restore local state */ | ||
281 | RESTORE_BITS; | ||
282 | window = qtm->window; | ||
283 | window_posn = qtm->window_posn; | ||
284 | frame_todo = qtm->frame_todo; | ||
285 | H = qtm->H; | ||
286 | L = qtm->L; | ||
287 | C = qtm->C; | ||
288 | |||
289 | /* while we do not have enough decoded bytes in reserve: */ | ||
290 | while ((qtm->o_end - qtm->o_ptr) < out_bytes) { | ||
291 | /* read header if necessary. Initialises H, L and C */ | ||
292 | if (!qtm->header_read) { | ||
293 | H = 0xFFFF; L = 0; READ_BITS(C, 16); | ||
294 | qtm->header_read = 1; | ||
295 | } | ||
296 | |||
297 | /* decode more, up to the number of bytes needed, the frame boundary, | ||
298 | * or the window boundary, whichever comes first */ | ||
299 | frame_end = window_posn + (out_bytes - (qtm->o_end - qtm->o_ptr)); | ||
300 | if ((window_posn + frame_todo) < frame_end) { | ||
301 | frame_end = window_posn + frame_todo; | ||
302 | } | ||
303 | if (frame_end > qtm->window_size) { | ||
304 | frame_end = qtm->window_size; | ||
305 | } | ||
306 | |||
307 | while (window_posn < frame_end) { | ||
308 | GET_SYMBOL(qtm->model7, selector); | ||
309 | if (selector < 4) { | ||
310 | /* literal byte */ | ||
311 | struct qtmd_model *mdl = (selector == 0) ? &qtm->model0 : | ||
312 | ((selector == 1) ? &qtm->model1 : | ||
313 | ((selector == 2) ? &qtm->model2 : | ||
314 | &qtm->model3)); | ||
315 | GET_SYMBOL((*mdl), sym); | ||
316 | window[window_posn++] = sym; | ||
317 | frame_todo--; | ||
318 | } | ||
319 | else { | ||
320 | /* match repeated string */ | ||
321 | switch (selector) { | ||
322 | case 4: /* selector 4 = fixed length match (3 bytes) */ | ||
323 | GET_SYMBOL(qtm->model4, sym); | ||
324 | READ_MANY_BITS(extra, extra_bits[sym]); | ||
325 | match_offset = position_base[sym] + extra + 1; | ||
326 | match_length = 3; | ||
327 | break; | ||
328 | |||
329 | case 5: /* selector 5 = fixed length match (4 bytes) */ | ||
330 | GET_SYMBOL(qtm->model5, sym); | ||
331 | READ_MANY_BITS(extra, extra_bits[sym]); | ||
332 | match_offset = position_base[sym] + extra + 1; | ||
333 | match_length = 4; | ||
334 | break; | ||
335 | |||
336 | case 6: /* selector 6 = variable length match */ | ||
337 | GET_SYMBOL(qtm->model6len, sym); | ||
338 | READ_MANY_BITS(extra, length_extra[sym]); | ||
339 | match_length = length_base[sym] + extra + 5; | ||
340 | |||
341 | GET_SYMBOL(qtm->model6, sym); | ||
342 | READ_MANY_BITS(extra, extra_bits[sym]); | ||
343 | match_offset = position_base[sym] + extra + 1; | ||
344 | break; | ||
345 | |||
346 | default: | ||
347 | /* should be impossible, model7 can only return 0-6 */ | ||
348 | D(("got %d from selector", selector)) | ||
349 | return qtm->error = MSPACK_ERR_DECRUNCH; | ||
350 | } | ||
351 | |||
352 | rundest = &window[window_posn]; | ||
353 | frame_todo -= match_length; | ||
354 | |||
355 | /* does match destination wrap the window? This situation is possible | ||
356 | * where the window size is less than the 32k frame size, but matches | ||
357 | * must not go beyond a frame boundary */ | ||
358 | if ((window_posn + match_length) > qtm->window_size) { | ||
359 | /* copy first part of match, before window end */ | ||
360 | i = qtm->window_size - window_posn; | ||
361 | j = window_posn - match_offset; | ||
362 | while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; | ||
363 | |||
364 | /* flush currently stored data */ | ||
365 | i = (&window[qtm->window_size] - qtm->o_ptr); | ||
366 | |||
367 | /* this should not happen, but if it does then this code | ||
368 | * can't handle the situation (can't flush up to the end of | ||
369 | * the window, but can't break out either because we haven't | ||
370 | * finished writing the match). bail out in this case */ | ||
371 | if (i > out_bytes) { | ||
372 | D(("during window-wrap match; %d bytes to flush but only need %d", | ||
373 | i, (int) out_bytes)) | ||
374 | return qtm->error = MSPACK_ERR_DECRUNCH; | ||
375 | } | ||
376 | if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { | ||
377 | return qtm->error = MSPACK_ERR_WRITE; | ||
378 | } | ||
379 | out_bytes -= i; | ||
380 | qtm->o_ptr = &window[0]; | ||
381 | qtm->o_end = &window[0]; | ||
382 | |||
383 | /* copy second part of match, after window wrap */ | ||
384 | rundest = &window[0]; | ||
385 | i = match_length - (qtm->window_size - window_posn); | ||
386 | while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; | ||
387 | window_posn = window_posn + match_length - qtm->window_size; | ||
388 | |||
389 | break; /* because "window_posn < frame_end" has now failed */ | ||
390 | } | ||
391 | else { | ||
392 | /* normal match - output won't wrap window or frame end */ | ||
393 | i = match_length; | ||
394 | |||
395 | /* does match _offset_ wrap the window? */ | ||
396 | if (match_offset > window_posn) { | ||
397 | /* j = length from match offset to end of window */ | ||
398 | j = match_offset - window_posn; | ||
399 | if (j > (int) qtm->window_size) { | ||
400 | D(("match offset beyond window boundaries")) | ||
401 | return qtm->error = MSPACK_ERR_DECRUNCH; | ||
402 | } | ||
403 | runsrc = &window[qtm->window_size - j]; | ||
404 | if (j < i) { | ||
405 | /* if match goes over the window edge, do two copy runs */ | ||
406 | i -= j; while (j-- > 0) *rundest++ = *runsrc++; | ||
407 | runsrc = window; | ||
408 | } | ||
409 | while (i-- > 0) *rundest++ = *runsrc++; | ||
410 | } | ||
411 | else { | ||
412 | runsrc = rundest - match_offset; | ||
413 | while (i-- > 0) *rundest++ = *runsrc++; | ||
414 | } | ||
415 | window_posn += match_length; | ||
416 | } | ||
417 | } /* if (window_posn+match_length > frame_end) */ | ||
418 | } /* while (window_posn < frame_end) */ | ||
419 | |||
420 | qtm->o_end = &window[window_posn]; | ||
421 | |||
422 | /* if we subtracted too much from frame_todo, it will | ||
423 | * wrap around past zero and go above its max value */ | ||
424 | if (frame_todo > QTM_FRAME_SIZE) { | ||
425 | D(("overshot frame alignment")) | ||
426 | return qtm->error = MSPACK_ERR_DECRUNCH; | ||
427 | } | ||
428 | |||
429 | /* another frame completed? */ | ||
430 | if (frame_todo == 0) { | ||
431 | /* re-align input */ | ||
432 | if (bits_left & 7) REMOVE_BITS(bits_left & 7); | ||
433 | |||
434 | /* special Quantum hack -- cabd.c injects a trailer byte to allow the | ||
435 | * decompressor to realign itself. CAB Quantum blocks, unlike LZX | ||
436 | * blocks, can have anything from 0 to 4 trailing null bytes. */ | ||
437 | do { READ_BITS(i, 8); } while (i != 0xFF); | ||
438 | |||
439 | qtm->header_read = 0; | ||
440 | |||
441 | frame_todo = QTM_FRAME_SIZE; | ||
442 | } | ||
443 | |||
444 | /* window wrap? */ | ||
445 | if (window_posn == qtm->window_size) { | ||
446 | /* flush all currently stored data */ | ||
447 | i = (qtm->o_end - qtm->o_ptr); | ||
448 | /* break out if we have more than enough to finish this request */ | ||
449 | if (i >= out_bytes) break; | ||
450 | if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { | ||
451 | return qtm->error = MSPACK_ERR_WRITE; | ||
452 | } | ||
453 | out_bytes -= i; | ||
454 | qtm->o_ptr = &window[0]; | ||
455 | qtm->o_end = &window[0]; | ||
456 | window_posn = 0; | ||
457 | } | ||
458 | |||
459 | } /* while (more bytes needed) */ | ||
460 | |||
461 | if (out_bytes) { | ||
462 | i = (int) out_bytes; | ||
463 | if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { | ||
464 | return qtm->error = MSPACK_ERR_WRITE; | ||
465 | } | ||
466 | qtm->o_ptr += i; | ||
467 | } | ||
468 | |||
469 | /* store local state */ | ||
470 | |||
471 | STORE_BITS; | ||
472 | qtm->window_posn = window_posn; | ||
473 | qtm->frame_todo = frame_todo; | ||
474 | qtm->H = H; | ||
475 | qtm->L = L; | ||
476 | qtm->C = C; | ||
477 | |||
478 | return MSPACK_ERR_OK; | ||
479 | } | ||
480 | |||
481 | void qtmd_free(struct qtmd_stream *qtm) { | ||
482 | struct mspack_system *sys; | ||
483 | if (qtm) { | ||
484 | sys = qtm->sys; | ||
485 | sys->free(qtm->window); | ||
486 | sys->free(qtm->inbuf); | ||
487 | sys->free(qtm); | ||
488 | } | ||
489 | } | ||
diff --git a/rbutil/rbutilqt/mspack/readbits.h b/rbutil/rbutilqt/mspack/readbits.h new file mode 100644 index 0000000000..457cbdd7d4 --- /dev/null +++ b/rbutil/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 */ | ||
161 | static 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 | |||
183 | static 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/rbutil/rbutilqt/mspack/readhuff.h b/rbutil/rbutilqt/mspack/readhuff.h new file mode 100644 index 0000000000..bb15c0a123 --- /dev/null +++ b/rbutil/rbutilqt/mspack/readhuff.h | |||
@@ -0,0 +1,173 @@ | |||
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_READHUFF_H | ||
11 | #define MSPACK_READHUFF_H 1 | ||
12 | |||
13 | /* This implements a fast Huffman tree decoding system. | ||
14 | */ | ||
15 | |||
16 | #if !(defined(BITS_ORDER_MSB) || defined(BITS_ORDER_LSB)) | ||
17 | # error "readhuff.h is used in conjunction with readbits.h, include that first" | ||
18 | #endif | ||
19 | #if !(defined(TABLEBITS) && defined(MAXSYMBOLS)) | ||
20 | # error "define TABLEBITS(tbl) and MAXSYMBOLS(tbl) before using readhuff.h" | ||
21 | #endif | ||
22 | #if !(defined(HUFF_TABLE) && defined(HUFF_LEN)) | ||
23 | # error "define HUFF_TABLE(tbl) and HUFF_LEN(tbl) before using readhuff.h" | ||
24 | #endif | ||
25 | #ifndef HUFF_ERROR | ||
26 | # error "define HUFF_ERROR before using readhuff.h" | ||
27 | #endif | ||
28 | #ifndef HUFF_MAXBITS | ||
29 | # define HUFF_MAXBITS 16 | ||
30 | #endif | ||
31 | |||
32 | /* Decodes the next huffman symbol from the input bitstream into var. | ||
33 | * Do not use this macro on a table unless build_decode_table() succeeded. | ||
34 | */ | ||
35 | #define READ_HUFFSYM(tbl, var) do { \ | ||
36 | ENSURE_BITS(HUFF_MAXBITS); \ | ||
37 | sym = HUFF_TABLE(tbl, PEEK_BITS(TABLEBITS(tbl))); \ | ||
38 | if (sym >= MAXSYMBOLS(tbl)) HUFF_TRAVERSE(tbl); \ | ||
39 | (var) = sym; \ | ||
40 | i = HUFF_LEN(tbl, sym); \ | ||
41 | REMOVE_BITS(i); \ | ||
42 | } while (0) | ||
43 | |||
44 | #ifdef BITS_ORDER_LSB | ||
45 | # define HUFF_TRAVERSE(tbl) do { \ | ||
46 | i = TABLEBITS(tbl) - 1; \ | ||
47 | do { \ | ||
48 | if (i++ > HUFF_MAXBITS) HUFF_ERROR; \ | ||
49 | sym = HUFF_TABLE(tbl, \ | ||
50 | (sym << 1) | ((bit_buffer >> i) & 1)); \ | ||
51 | } while (sym >= MAXSYMBOLS(tbl)); \ | ||
52 | } while (0) | ||
53 | #else | ||
54 | #define HUFF_TRAVERSE(tbl) do { \ | ||
55 | i = 1 << (BITBUF_WIDTH - TABLEBITS(tbl)); \ | ||
56 | do { \ | ||
57 | if ((i >>= 1) == 0) HUFF_ERROR; \ | ||
58 | sym = HUFF_TABLE(tbl, \ | ||
59 | (sym << 1) | ((bit_buffer & i) ? 1 : 0)); \ | ||
60 | } while (sym >= MAXSYMBOLS(tbl)); \ | ||
61 | } while (0) | ||
62 | #endif | ||
63 | |||
64 | /* make_decode_table(nsyms, nbits, length[], table[]) | ||
65 | * | ||
66 | * This function was originally coded by David Tritscher. | ||
67 | * It builds a fast huffman decoding table from | ||
68 | * a canonical huffman code lengths table. | ||
69 | * | ||
70 | * nsyms = total number of symbols in this huffman tree. | ||
71 | * nbits = any symbols with a code length of nbits or less can be decoded | ||
72 | * in one lookup of the table. | ||
73 | * length = A table to get code lengths from [0 to nsyms-1] | ||
74 | * table = The table to fill up with decoded symbols and pointers. | ||
75 | * Should be ((1<<nbits) + (nsyms*2)) in length. | ||
76 | * | ||
77 | * Returns 0 for OK or 1 for error | ||
78 | */ | ||
79 | static int make_decode_table(unsigned int nsyms, unsigned int nbits, | ||
80 | unsigned char *length, unsigned short *table) | ||
81 | { | ||
82 | register unsigned short sym, next_symbol; | ||
83 | register unsigned int leaf, fill; | ||
84 | #ifdef BITS_ORDER_LSB | ||
85 | register unsigned int reverse; | ||
86 | #endif | ||
87 | register unsigned char bit_num; | ||
88 | unsigned int pos = 0; /* the current position in the decode table */ | ||
89 | unsigned int table_mask = 1 << nbits; | ||
90 | unsigned int bit_mask = table_mask >> 1; /* don't do 0 length codes */ | ||
91 | |||
92 | /* fill entries for codes short enough for a direct mapping */ | ||
93 | for (bit_num = 1; bit_num <= nbits; bit_num++) { | ||
94 | for (sym = 0; sym < nsyms; sym++) { | ||
95 | if (length[sym] != bit_num) continue; | ||
96 | #ifdef BITS_ORDER_MSB | ||
97 | leaf = pos; | ||
98 | #else | ||
99 | /* reverse the significant bits */ | ||
100 | fill = length[sym]; reverse = pos >> (nbits - fill); leaf = 0; | ||
101 | do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); | ||
102 | #endif | ||
103 | |||
104 | if((pos += bit_mask) > table_mask) return 1; /* table overrun */ | ||
105 | |||
106 | /* fill all possible lookups of this symbol with the symbol itself */ | ||
107 | #ifdef BITS_ORDER_MSB | ||
108 | for (fill = bit_mask; fill-- > 0;) table[leaf++] = sym; | ||
109 | #else | ||
110 | fill = bit_mask; next_symbol = 1 << bit_num; | ||
111 | do { table[leaf] = sym; leaf += next_symbol; } while (--fill); | ||
112 | #endif | ||
113 | } | ||
114 | bit_mask >>= 1; | ||
115 | } | ||
116 | |||
117 | /* exit with success if table is now complete */ | ||
118 | if (pos == table_mask) return 0; | ||
119 | |||
120 | /* mark all remaining table entries as unused */ | ||
121 | for (sym = pos; sym < table_mask; sym++) { | ||
122 | #ifdef BITS_ORDER_MSB | ||
123 | table[sym] = 0xFFFF; | ||
124 | #else | ||
125 | reverse = sym; leaf = 0; fill = nbits; | ||
126 | do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill); | ||
127 | table[leaf] = 0xFFFF; | ||
128 | #endif | ||
129 | } | ||
130 | |||
131 | /* next_symbol = base of allocation for long codes */ | ||
132 | next_symbol = ((table_mask >> 1) < nsyms) ? nsyms : (table_mask >> 1); | ||
133 | |||
134 | /* give ourselves room for codes to grow by up to 16 more bits. | ||
135 | * codes now start at bit nbits+16 and end at (nbits+16-codelength) */ | ||
136 | pos <<= 16; | ||
137 | table_mask <<= 16; | ||
138 | bit_mask = 1 << 15; | ||
139 | |||
140 | for (bit_num = nbits+1; bit_num <= HUFF_MAXBITS; bit_num++) { | ||
141 | for (sym = 0; sym < nsyms; sym++) { | ||
142 | if (length[sym] != bit_num) continue; | ||
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 | |||
165 | if ((pos += bit_mask) > table_mask) return 1; /* table overflow */ | ||
166 | } | ||
167 | bit_mask >>= 1; | ||
168 | } | ||
169 | |||
170 | /* full table? */ | ||
171 | return (pos == table_mask) ? 0 : 1; | ||
172 | } | ||
173 | #endif | ||
diff --git a/rbutil/rbutilqt/mspack/sha.h b/rbutil/rbutilqt/mspack/sha.h new file mode 100644 index 0000000000..360521519b --- /dev/null +++ b/rbutil/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/rbutil/rbutilqt/mspack/system.c b/rbutil/rbutilqt/mspack/system.c new file mode 100644 index 0000000000..f3f0019f5e --- /dev/null +++ b/rbutil/rbutilqt/mspack/system.c | |||
@@ -0,0 +1,237 @@ | |||
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.h> | ||
15 | |||
16 | #ifndef LARGEFILE_SUPPORT | ||
17 | const char *largefile_msg = "library not compiled to support large files."; | ||
18 | #endif | ||
19 | |||
20 | |||
21 | int 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 | |||
50 | int 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 */ | ||
55 | int 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 */ | ||
63 | int 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 | ||
98 | struct 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 | |||
108 | struct mspack_file_p { | ||
109 | FILE *fh; | ||
110 | const char *name; | ||
111 | }; | ||
112 | |||
113 | static 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 | |||
119 | switch (mode) { | ||
120 | case MSPACK_SYS_OPEN_READ: fmode = "rb"; break; | ||
121 | case MSPACK_SYS_OPEN_WRITE: fmode = "wb"; break; | ||
122 | case MSPACK_SYS_OPEN_UPDATE: fmode = "r+b"; break; | ||
123 | case MSPACK_SYS_OPEN_APPEND: fmode = "ab"; break; | ||
124 | default: return NULL; | ||
125 | } | ||
126 | |||
127 | if ((fh = (struct mspack_file_p *) malloc(sizeof(struct mspack_file_p)))) { | ||
128 | fh->name = filename; | ||
129 | if ((fh->fh = fopen(filename, fmode))) return (struct mspack_file *) fh; | ||
130 | free(fh); | ||
131 | } | ||
132 | return NULL; | ||
133 | } | ||
134 | |||
135 | static void msp_close(struct mspack_file *file) { | ||
136 | struct mspack_file_p *self = (struct mspack_file_p *) file; | ||
137 | if (self) { | ||
138 | fclose(self->fh); | ||
139 | free(self); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | static int msp_read(struct mspack_file *file, void *buffer, int bytes) { | ||
144 | struct mspack_file_p *self = (struct mspack_file_p *) file; | ||
145 | if (self && buffer && bytes >= 0) { | ||
146 | size_t count = fread(buffer, 1, (size_t) bytes, self->fh); | ||
147 | if (!ferror(self->fh)) return (int) count; | ||
148 | } | ||
149 | return -1; | ||
150 | } | ||
151 | |||
152 | static int msp_write(struct mspack_file *file, void *buffer, int bytes) { | ||
153 | struct mspack_file_p *self = (struct mspack_file_p *) file; | ||
154 | if (self && buffer && bytes >= 0) { | ||
155 | size_t count = fwrite(buffer, 1, (size_t) bytes, self->fh); | ||
156 | if (!ferror(self->fh)) return (int) count; | ||
157 | } | ||
158 | return -1; | ||
159 | } | ||
160 | |||
161 | static int msp_seek(struct mspack_file *file, off_t offset, int mode) { | ||
162 | struct mspack_file_p *self = (struct mspack_file_p *) file; | ||
163 | if (self) { | ||
164 | switch (mode) { | ||
165 | case MSPACK_SYS_SEEK_START: mode = SEEK_SET; break; | ||
166 | case MSPACK_SYS_SEEK_CUR: mode = SEEK_CUR; break; | ||
167 | case MSPACK_SYS_SEEK_END: mode = SEEK_END; break; | ||
168 | default: return -1; | ||
169 | } | ||
170 | #ifdef HAVE_FSEEKO | ||
171 | return fseeko(self->fh, offset, mode); | ||
172 | #else | ||
173 | return fseek(self->fh, offset, mode); | ||
174 | #endif | ||
175 | } | ||
176 | return -1; | ||
177 | } | ||
178 | |||
179 | static off_t msp_tell(struct mspack_file *file) { | ||
180 | struct mspack_file_p *self = (struct mspack_file_p *) file; | ||
181 | #ifdef HAVE_FSEEKO | ||
182 | return (self) ? (off_t) ftello(self->fh) : 0; | ||
183 | #else | ||
184 | return (self) ? (off_t) ftell(self->fh) : 0; | ||
185 | #endif | ||
186 | } | ||
187 | |||
188 | static void msp_msg(struct mspack_file *file, const char *format, ...) { | ||
189 | va_list ap; | ||
190 | if (file) fprintf(stderr, "%s: ", ((struct mspack_file_p *) file)->name); | ||
191 | va_start(ap, format); | ||
192 | vfprintf(stderr, format, ap); | ||
193 | va_end(ap); | ||
194 | fputc((int) '\n', stderr); | ||
195 | fflush(stderr); | ||
196 | } | ||
197 | |||
198 | static void *msp_alloc(struct mspack_system *self, size_t bytes) { | ||
199 | #ifdef DEBUG | ||
200 | /* make uninitialised data obvious */ | ||
201 | char *buf = malloc(bytes + 8); | ||
202 | if (buf) memset(buf, 0xDC, bytes); | ||
203 | *((size_t *)buf) = bytes; | ||
204 | return &buf[8]; | ||
205 | #else | ||
206 | return malloc(bytes); | ||
207 | #endif | ||
208 | } | ||
209 | |||
210 | static void msp_free(void *buffer) { | ||
211 | #ifdef DEBUG | ||
212 | char *buf = buffer; | ||
213 | size_t bytes; | ||
214 | if (buf) { | ||
215 | buf -= 8; | ||
216 | bytes = *((size_t *)buf); | ||
217 | /* make freed data obvious */ | ||
218 | memset(buf, 0xED, bytes); | ||
219 | free(buf); | ||
220 | } | ||
221 | #else | ||
222 | free(buffer); | ||
223 | #endif | ||
224 | } | ||
225 | |||
226 | static void msp_copy(void *src, void *dest, size_t bytes) { | ||
227 | memcpy(dest, src, bytes); | ||
228 | } | ||
229 | |||
230 | static struct mspack_system msp_system = { | ||
231 | &msp_open, &msp_close, &msp_read, &msp_write, &msp_seek, | ||
232 | &msp_tell, &msp_msg, &msp_alloc, &msp_free, &msp_copy, NULL | ||
233 | }; | ||
234 | |||
235 | struct mspack_system *mspack_default_system = &msp_system; | ||
236 | |||
237 | #endif | ||
diff --git a/rbutil/rbutilqt/mspack/system.h b/rbutil/rbutilqt/mspack/system.h new file mode 100644 index 0000000000..4a400850aa --- /dev/null +++ b/rbutil/rbutilqt/mspack/system.h | |||
@@ -0,0 +1,124 @@ | |||
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 | ||
14 | extern "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 | ||
65 | # define LD "lld" | ||
66 | # define LU "llu" | ||
67 | #else | ||
68 | extern 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 | |||
90 | extern struct mspack_system *mspack_default_system; | ||
91 | |||
92 | /* returns the length of a file opened for reading */ | ||
93 | extern int mspack_sys_filelen(struct mspack_system *system, | ||
94 | struct mspack_file *file, off_t *length); | ||
95 | |||
96 | /* validates a system structure */ | ||
97 | extern 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 | static inline int mspack_memcmp(const void *s1, const void *s2, size_t n) { | ||
112 | unsigned char *c1 = (unsigned char *) s1; | ||
113 | unsigned char *c2 = (unsigned char *) s2; | ||
114 | if (n == 0) return 0; | ||
115 | while (--n && (*c1 == *c2)) c1++, c2++; | ||
116 | return *c1 - *c2; | ||
117 | } | ||
118 | #endif | ||
119 | |||
120 | #ifdef __cplusplus | ||
121 | } | ||
122 | #endif | ||
123 | |||
124 | #endif | ||
diff --git a/rbutil/rbutilqt/mspack/szdd.h b/rbutil/rbutilqt/mspack/szdd.h new file mode 100644 index 0000000000..e07c6b7c8e --- /dev/null +++ b/rbutil/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 | |||
20 | struct msszdd_compressor_p { | ||
21 | struct msszdd_compressor base; | ||
22 | struct mspack_system *system; | ||
23 | int error; | ||
24 | }; | ||
25 | |||
26 | /* SZDD decompression definitions */ | ||
27 | |||
28 | struct msszdd_decompressor_p { | ||
29 | struct msszdd_decompressor base; | ||
30 | struct mspack_system *system; | ||
31 | int error; | ||
32 | }; | ||
33 | |||
34 | struct msszddd_header_p { | ||
35 | struct msszddd_header base; | ||
36 | struct mspack_file *fh; | ||
37 | }; | ||
38 | |||
39 | #endif | ||
diff --git a/rbutil/rbutilqt/mspack/szddc.c b/rbutil/rbutilqt/mspack/szddc.c new file mode 100644 index 0000000000..cdd39a6305 --- /dev/null +++ b/rbutil/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.h> | ||
13 | #include <szdd.h> | ||
14 | |||
15 | struct msszdd_compressor * | ||
16 | mspack_create_szdd_compressor(struct mspack_system *sys) | ||
17 | { | ||
18 | /* todo */ | ||
19 | return NULL; | ||
20 | } | ||
21 | |||
22 | void mspack_destroy_szdd_compressor(struct msszdd_compressor *self) { | ||
23 | /* todo */ | ||
24 | } | ||
diff --git a/rbutil/rbutilqt/mspack/szddd.c b/rbutil/rbutilqt/mspack/szddd.c new file mode 100644 index 0000000000..99c5aa4658 --- /dev/null +++ b/rbutil/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.h> | ||
17 | #include <szdd.h> | ||
18 | |||
19 | /* prototypes */ | ||
20 | static struct msszddd_header *szddd_open( | ||
21 | struct msszdd_decompressor *base, const char *filename); | ||
22 | static void szddd_close( | ||
23 | struct msszdd_decompressor *base, struct msszddd_header *hdr); | ||
24 | static int szddd_read_headers( | ||
25 | struct mspack_system *sys, struct mspack_file *fh, | ||
26 | struct msszddd_header *hdr); | ||
27 | static int szddd_extract( | ||
28 | struct msszdd_decompressor *base, struct msszddd_header *hdr, | ||
29 | const char *filename); | ||
30 | static int szddd_decompress( | ||
31 | struct msszdd_decompressor *base, const char *input, const char *output); | ||
32 | static int szddd_error( | ||
33 | struct msszdd_decompressor *base); | ||
34 | |||
35 | /*************************************** | ||
36 | * MSPACK_CREATE_SZDD_DECOMPRESSOR | ||
37 | *************************************** | ||
38 | * constructor | ||
39 | */ | ||
40 | struct 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 | */ | ||
65 | void 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 | */ | ||
79 | static 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 | if (hdr) sys->free(hdr); | ||
104 | hdr = NULL; | ||
105 | } | ||
106 | |||
107 | return hdr; | ||
108 | } | ||
109 | |||
110 | /*************************************** | ||
111 | * SZDDD_CLOSE | ||
112 | *************************************** | ||
113 | * closes an SZDD file | ||
114 | */ | ||
115 | static 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 | */ | ||
137 | static unsigned char szdd_signature_expand[8] = { | ||
138 | 0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33 | ||
139 | }; | ||
140 | static unsigned char szdd_signature_qbasic[8] = { | ||
141 | 0x53, 0x5A, 0x20, 0x88, 0xF0, 0x27, 0x33, 0xD1 | ||
142 | }; | ||
143 | |||
144 | static 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 ((mspack_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 ((mspack_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 | */ | ||
181 | static 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 | */ | ||
223 | static 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 | */ | ||
243 | static 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 | } | ||