diff options
Diffstat (limited to 'utils/rbutilqt/mspack')
37 files changed, 10630 insertions, 0 deletions
diff --git a/utils/rbutilqt/mspack/COPYING.LIB b/utils/rbutilqt/mspack/COPYING.LIB new file mode 100644 index 0000000000..b1e3f5a263 --- /dev/null +++ b/utils/rbutilqt/mspack/COPYING.LIB | |||
@@ -0,0 +1,504 @@ | |||
1 | GNU LESSER GENERAL PUBLIC LICENSE | ||
2 | Version 2.1, February 1999 | ||
3 | |||
4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. | ||
5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
6 | Everyone is permitted to copy and distribute verbatim copies | ||
7 | of this license document, but changing it is not allowed. | ||
8 | |||
9 | [This is the first released version of the Lesser GPL. It also counts | ||
10 | as the successor of the GNU Library Public License, version 2, hence | ||
11 | the version number 2.1.] | ||
12 | |||
13 | Preamble | ||
14 | |||
15 | The licenses for most software are designed to take away your | ||
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/utils/rbutilqt/mspack/README.ROCKBOX b/utils/rbutilqt/mspack/README.ROCKBOX new file mode 100644 index 0000000000..220691af2c --- /dev/null +++ b/utils/rbutilqt/mspack/README.ROCKBOX | |||
@@ -0,0 +1,6 @@ | |||
1 | This folder contains the mspack project for MS files compression/decompression. | ||
2 | These files are distributed under the LGPL. | ||
3 | |||
4 | The source files have been last synced with libmspack-0.10.1alpha | ||
5 | https://www.cabextract.org.uk/libmspack/ on June 8, 2020 | ||
6 | |||
diff --git a/utils/rbutilqt/mspack/cab.h b/utils/rbutilqt/mspack/cab.h new file mode 100644 index 0000000000..79d9951252 --- /dev/null +++ b/utils/rbutilqt/mspack/cab.h | |||
@@ -0,0 +1,140 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2018 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifndef MSPACK_CAB_H | ||
11 | #define MSPACK_CAB_H 1 | ||
12 | |||
13 | /* generic CAB definitions */ | ||
14 | |||
15 | /* structure offsets */ | ||
16 | #define cfhead_Signature (0x00) | ||
17 | #define cfhead_CabinetSize (0x08) | ||
18 | #define cfhead_FileOffset (0x10) | ||
19 | #define cfhead_MinorVersion (0x18) | ||
20 | #define cfhead_MajorVersion (0x19) | ||
21 | #define cfhead_NumFolders (0x1A) | ||
22 | #define cfhead_NumFiles (0x1C) | ||
23 | #define cfhead_Flags (0x1E) | ||
24 | #define cfhead_SetID (0x20) | ||
25 | #define cfhead_CabinetIndex (0x22) | ||
26 | #define cfhead_SIZEOF (0x24) | ||
27 | #define cfheadext_HeaderReserved (0x00) | ||
28 | #define cfheadext_FolderReserved (0x02) | ||
29 | #define cfheadext_DataReserved (0x03) | ||
30 | #define cfheadext_SIZEOF (0x04) | ||
31 | #define cffold_DataOffset (0x00) | ||
32 | #define cffold_NumBlocks (0x04) | ||
33 | #define cffold_CompType (0x06) | ||
34 | #define cffold_SIZEOF (0x08) | ||
35 | #define cffile_UncompressedSize (0x00) | ||
36 | #define cffile_FolderOffset (0x04) | ||
37 | #define cffile_FolderIndex (0x08) | ||
38 | #define cffile_Date (0x0A) | ||
39 | #define cffile_Time (0x0C) | ||
40 | #define cffile_Attribs (0x0E) | ||
41 | #define cffile_SIZEOF (0x10) | ||
42 | #define cfdata_CheckSum (0x00) | ||
43 | #define cfdata_CompressedSize (0x04) | ||
44 | #define cfdata_UncompressedSize (0x06) | ||
45 | #define cfdata_SIZEOF (0x08) | ||
46 | |||
47 | /* flags */ | ||
48 | #define cffoldCOMPTYPE_MASK (0x000f) | ||
49 | #define cffoldCOMPTYPE_NONE (0x0000) | ||
50 | #define cffoldCOMPTYPE_MSZIP (0x0001) | ||
51 | #define cffoldCOMPTYPE_QUANTUM (0x0002) | ||
52 | #define cffoldCOMPTYPE_LZX (0x0003) | ||
53 | #define cfheadPREV_CABINET (0x0001) | ||
54 | #define cfheadNEXT_CABINET (0x0002) | ||
55 | #define cfheadRESERVE_PRESENT (0x0004) | ||
56 | #define cffileCONTINUED_FROM_PREV (0xFFFD) | ||
57 | #define cffileCONTINUED_TO_NEXT (0xFFFE) | ||
58 | #define cffileCONTINUED_PREV_AND_NEXT (0xFFFF) | ||
59 | |||
60 | /* CAB data blocks are <= 32768 bytes in uncompressed form. Uncompressed | ||
61 | * blocks have zero growth. MSZIP guarantees that it won't grow above | ||
62 | * uncompressed size by more than 12 bytes. LZX guarantees it won't grow | ||
63 | * more than 6144 bytes. Quantum has no documentation, but the largest | ||
64 | * block seen in the wild is 337 bytes above uncompressed size. | ||
65 | */ | ||
66 | #define CAB_BLOCKMAX (32768) | ||
67 | #define CAB_INPUTMAX (CAB_BLOCKMAX+6144) | ||
68 | |||
69 | /* input buffer needs to be CAB_INPUTMAX + 1 byte to allow for max-sized block | ||
70 | * plus 1 trailer byte added by cabd_sys_read_block() for Quantum alignment. | ||
71 | * | ||
72 | * When MSCABD_PARAM_SALVAGE is set, block size is not checked so can be | ||
73 | * up to 65535 bytes, so max input buffer size needed is 65535 + 1 | ||
74 | */ | ||
75 | #define CAB_INPUTMAX_SALVAGE (65535) | ||
76 | #define CAB_INPUTBUF (CAB_INPUTMAX_SALVAGE + 1) | ||
77 | |||
78 | /* There are no more than 65535 data blocks per folder, so a folder cannot | ||
79 | * be more than 32768*65535 bytes in length. As files cannot span more than | ||
80 | * one folder, this is also their max offset, length and offset+length limit. | ||
81 | */ | ||
82 | #define CAB_FOLDERMAX (65535) | ||
83 | #define CAB_LENGTHMAX (CAB_BLOCKMAX * CAB_FOLDERMAX) | ||
84 | |||
85 | /* CAB compression definitions */ | ||
86 | |||
87 | struct mscab_compressor_p { | ||
88 | struct mscab_compressor base; | ||
89 | struct mspack_system *system; | ||
90 | /* todo */ | ||
91 | }; | ||
92 | |||
93 | /* CAB decompression definitions */ | ||
94 | |||
95 | struct mscabd_decompress_state { | ||
96 | struct mscabd_folder_p *folder; /* current folder we're extracting from */ | ||
97 | struct mscabd_folder_data *data; /* current folder split we're in */ | ||
98 | unsigned int offset; /* uncompressed offset within folder */ | ||
99 | unsigned int block; /* which block are we decompressing? */ | ||
100 | off_t outlen; /* cumulative sum of block output sizes */ | ||
101 | struct mspack_system sys; /* special I/O code for decompressor */ | ||
102 | int comp_type; /* type of compression used by folder */ | ||
103 | int (*decompress)(void *, off_t); /* decompressor code */ | ||
104 | void *state; /* decompressor state */ | ||
105 | struct mscabd_cabinet_p *incab; /* cabinet where input data comes from */ | ||
106 | struct mspack_file *infh; /* input file handle */ | ||
107 | struct mspack_file *outfh; /* output file handle */ | ||
108 | unsigned char *i_ptr, *i_end; /* input data consumed, end */ | ||
109 | unsigned char input[CAB_INPUTBUF]; /* one input block of data */ | ||
110 | }; | ||
111 | |||
112 | struct mscab_decompressor_p { | ||
113 | struct mscab_decompressor base; | ||
114 | struct mscabd_decompress_state *d; | ||
115 | struct mspack_system *system; | ||
116 | int buf_size, searchbuf_size, fix_mszip, salvage; /* params */ | ||
117 | int error, read_error; | ||
118 | }; | ||
119 | |||
120 | struct mscabd_cabinet_p { | ||
121 | struct mscabd_cabinet base; | ||
122 | off_t blocks_off; /* offset to data blocks */ | ||
123 | int block_resv; /* reserved space in data blocks */ | ||
124 | }; | ||
125 | |||
126 | /* there is one of these for every cabinet a folder spans */ | ||
127 | struct mscabd_folder_data { | ||
128 | struct mscabd_folder_data *next; | ||
129 | struct mscabd_cabinet_p *cab; /* cabinet file of this folder span */ | ||
130 | off_t offset; /* cabinet offset of first datablock */ | ||
131 | }; | ||
132 | |||
133 | struct mscabd_folder_p { | ||
134 | struct mscabd_folder base; | ||
135 | struct mscabd_folder_data data; /* where are the data blocks? */ | ||
136 | struct mscabd_file *merge_prev; /* first file needing backwards merge */ | ||
137 | struct mscabd_file *merge_next; /* first file needing forwards merge */ | ||
138 | }; | ||
139 | |||
140 | #endif | ||
diff --git a/utils/rbutilqt/mspack/cabc.c b/utils/rbutilqt/mspack/cabc.c new file mode 100644 index 0000000000..eb85011056 --- /dev/null +++ b/utils/rbutilqt/mspack/cabc.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | /* CAB compression implementation */ | ||
11 | |||
12 | #include "system-mspack.h" | ||
13 | #include "cab.h" | ||
14 | |||
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/utils/rbutilqt/mspack/cabd.c b/utils/rbutilqt/mspack/cabd.c new file mode 100644 index 0000000000..ae66769b24 --- /dev/null +++ b/utils/rbutilqt/mspack/cabd.c | |||
@@ -0,0 +1,1508 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2018 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | /* Cabinet (.CAB) files are a form of file archive. Each cabinet contains | ||
11 | * "folders", which are compressed spans of data. Each cabinet has | ||
12 | * "files", whose metadata is in the cabinet header, but whose actual data | ||
13 | * is stored compressed in one of the "folders". Cabinets can span more | ||
14 | * than one physical file on disk, in which case they are a "cabinet set", | ||
15 | * and usually the last folder of each cabinet extends into the next | ||
16 | * cabinet. | ||
17 | * | ||
18 | * For a complete description of the format, see the MSDN site: | ||
19 | * http://msdn.microsoft.com/en-us/library/bb267310.aspx | ||
20 | */ | ||
21 | |||
22 | /* CAB decompression implementation */ | ||
23 | |||
24 | #include "system-mspack.h" | ||
25 | #include "cab.h" | ||
26 | #include "mszip.h" | ||
27 | #include "lzx.h" | ||
28 | #include "qtm.h" | ||
29 | |||
30 | /* Notes on compliance with cabinet specification: | ||
31 | * | ||
32 | * One of the main changes between cabextract 0.6 and libmspack's cab | ||
33 | * decompressor is the move from block-oriented decompression to | ||
34 | * stream-oriented decompression. | ||
35 | * | ||
36 | * cabextract would read one data block from disk, decompress it with the | ||
37 | * appropriate method, then write the decompressed data. The CAB | ||
38 | * specification is specifically designed to work like this, as it ensures | ||
39 | * compression matches do not span the maximum decompressed block size | ||
40 | * limit of 32kb. | ||
41 | * | ||
42 | * However, the compression algorithms used are stream oriented, with | ||
43 | * specific hacks added to them to enforce the "individual 32kb blocks" | ||
44 | * rule in CABs. In other file formats, they do not have this limitation. | ||
45 | * | ||
46 | * In order to make more generalised decompressors, libmspack's CAB | ||
47 | * decompressor has moved from being block-oriented to more stream | ||
48 | * oriented. This also makes decompression slightly faster. | ||
49 | * | ||
50 | * However, this leads to incompliance with the CAB specification. The | ||
51 | * CAB controller can no longer ensure each block of input given to the | ||
52 | * decompressors is matched with their output. The "decompressed size" of | ||
53 | * each individual block is thrown away. | ||
54 | * | ||
55 | * Each CAB block is supposed to be seen as individually compressed. This | ||
56 | * means each consecutive data block can have completely different | ||
57 | * "uncompressed" sizes, ranging from 1 to 32768 bytes. However, in | ||
58 | * reality, all data blocks in a folder decompress to exactly 32768 bytes, | ||
59 | * excepting the final block. | ||
60 | * | ||
61 | * Given this situation, the decompression algorithms are designed to | ||
62 | * realign their input bitstreams on 32768 output-byte boundaries, and | ||
63 | * various other special cases have been made. libmspack will not | ||
64 | * correctly decompress LZX or Quantum compressed folders where the blocks | ||
65 | * do not follow this "32768 bytes until last block" pattern. It could be | ||
66 | * implemented if needed, but hopefully this is not necessary -- it has | ||
67 | * not been seen in over 3Gb of CAB archives. | ||
68 | */ | ||
69 | |||
70 | /* prototypes */ | ||
71 | static struct mscabd_cabinet * cabd_open( | ||
72 | struct mscab_decompressor *base, const char *filename); | ||
73 | static void cabd_close( | ||
74 | struct mscab_decompressor *base, struct mscabd_cabinet *origcab); | ||
75 | static int cabd_read_headers( | ||
76 | struct mspack_system *sys, struct mspack_file *fh, | ||
77 | struct mscabd_cabinet_p *cab, off_t offset, int salvage, int quiet); | ||
78 | static char *cabd_read_string( | ||
79 | struct mspack_system *sys, struct mspack_file *fh, int *error); | ||
80 | |||
81 | static struct mscabd_cabinet *cabd_search( | ||
82 | struct mscab_decompressor *base, const char *filename); | ||
83 | static int cabd_find( | ||
84 | struct mscab_decompressor_p *self, unsigned char *buf, | ||
85 | struct mspack_file *fh, const char *filename, off_t flen, | ||
86 | off_t *firstlen, struct mscabd_cabinet_p **firstcab); | ||
87 | |||
88 | static int cabd_prepend( | ||
89 | struct mscab_decompressor *base, struct mscabd_cabinet *cab, | ||
90 | struct mscabd_cabinet *prevcab); | ||
91 | static int cabd_append( | ||
92 | struct mscab_decompressor *base, struct mscabd_cabinet *cab, | ||
93 | struct mscabd_cabinet *nextcab); | ||
94 | static int cabd_merge( | ||
95 | struct mscab_decompressor *base, struct mscabd_cabinet *lcab, | ||
96 | struct mscabd_cabinet *rcab); | ||
97 | static int cabd_can_merge_folders( | ||
98 | struct mspack_system *sys, struct mscabd_folder_p *lfol, | ||
99 | struct mscabd_folder_p *rfol); | ||
100 | |||
101 | static int cabd_extract( | ||
102 | struct mscab_decompressor *base, struct mscabd_file *file, | ||
103 | const char *filename); | ||
104 | static int cabd_init_decomp( | ||
105 | struct mscab_decompressor_p *self, unsigned int ct); | ||
106 | static void cabd_free_decomp( | ||
107 | struct mscab_decompressor_p *self); | ||
108 | static int cabd_sys_read( | ||
109 | struct mspack_file *file, void *buffer, int bytes); | ||
110 | static int cabd_sys_write( | ||
111 | struct mspack_file *file, void *buffer, int bytes); | ||
112 | static int cabd_sys_read_block( | ||
113 | struct mspack_system *sys, struct mscabd_decompress_state *d, int *out, | ||
114 | int ignore_cksum, int ignore_blocksize); | ||
115 | static unsigned int cabd_checksum( | ||
116 | unsigned char *data, unsigned int bytes, unsigned int cksum); | ||
117 | static struct noned_state *noned_init( | ||
118 | struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out, | ||
119 | int bufsize); | ||
120 | |||
121 | static int noned_decompress( | ||
122 | struct noned_state *s, off_t bytes); | ||
123 | static void noned_free( | ||
124 | struct noned_state *state); | ||
125 | |||
126 | static int cabd_param( | ||
127 | struct mscab_decompressor *base, int param, int value); | ||
128 | |||
129 | static int cabd_error( | ||
130 | struct mscab_decompressor *base); | ||
131 | |||
132 | |||
133 | /*************************************** | ||
134 | * MSPACK_CREATE_CAB_DECOMPRESSOR | ||
135 | *************************************** | ||
136 | * constructor | ||
137 | */ | ||
138 | struct mscab_decompressor * | ||
139 | mspack_create_cab_decompressor(struct mspack_system *sys) | ||
140 | { | ||
141 | struct mscab_decompressor_p *self = NULL; | ||
142 | |||
143 | if (!sys) sys = mspack_default_system; | ||
144 | if (!mspack_valid_system(sys)) return NULL; | ||
145 | |||
146 | if ((self = (struct mscab_decompressor_p *) sys->alloc(sys, sizeof(struct mscab_decompressor_p)))) { | ||
147 | self->base.open = &cabd_open; | ||
148 | self->base.close = &cabd_close; | ||
149 | self->base.search = &cabd_search; | ||
150 | self->base.extract = &cabd_extract; | ||
151 | self->base.prepend = &cabd_prepend; | ||
152 | self->base.append = &cabd_append; | ||
153 | self->base.set_param = &cabd_param; | ||
154 | self->base.last_error = &cabd_error; | ||
155 | self->system = sys; | ||
156 | self->d = NULL; | ||
157 | self->error = MSPACK_ERR_OK; | ||
158 | |||
159 | self->searchbuf_size = 32768; | ||
160 | self->fix_mszip = 0; | ||
161 | self->buf_size = 4096; | ||
162 | self->salvage = 0; | ||
163 | } | ||
164 | return (struct mscab_decompressor *) self; | ||
165 | } | ||
166 | |||
167 | /*************************************** | ||
168 | * MSPACK_DESTROY_CAB_DECOMPRESSOR | ||
169 | *************************************** | ||
170 | * destructor | ||
171 | */ | ||
172 | void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) { | ||
173 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
174 | if (self) { | ||
175 | struct mspack_system *sys = self->system; | ||
176 | if (self->d) { | ||
177 | if (self->d->infh) sys->close(self->d->infh); | ||
178 | cabd_free_decomp(self); | ||
179 | sys->free(self->d); | ||
180 | } | ||
181 | sys->free(self); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | |||
186 | /*************************************** | ||
187 | * CABD_OPEN | ||
188 | *************************************** | ||
189 | * opens a file and tries to read it as a cabinet file | ||
190 | */ | ||
191 | static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, | ||
192 | const char *filename) | ||
193 | { | ||
194 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
195 | struct mscabd_cabinet_p *cab = NULL; | ||
196 | struct mspack_system *sys; | ||
197 | struct mspack_file *fh; | ||
198 | int error; | ||
199 | |||
200 | if (!base) return NULL; | ||
201 | sys = self->system; | ||
202 | |||
203 | if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { | ||
204 | if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { | ||
205 | cab->base.filename = filename; | ||
206 | error = cabd_read_headers(sys, fh, cab, (off_t) 0, self->salvage, 0); | ||
207 | if (error) { | ||
208 | cabd_close(base, (struct mscabd_cabinet *) cab); | ||
209 | cab = NULL; | ||
210 | } | ||
211 | self->error = error; | ||
212 | } | ||
213 | else { | ||
214 | self->error = MSPACK_ERR_NOMEMORY; | ||
215 | } | ||
216 | sys->close(fh); | ||
217 | } | ||
218 | else { | ||
219 | self->error = MSPACK_ERR_OPEN; | ||
220 | } | ||
221 | return (struct mscabd_cabinet *) cab; | ||
222 | } | ||
223 | |||
224 | /*************************************** | ||
225 | * CABD_CLOSE | ||
226 | *************************************** | ||
227 | * frees all memory associated with a given mscabd_cabinet. | ||
228 | */ | ||
229 | static void cabd_close(struct mscab_decompressor *base, | ||
230 | struct mscabd_cabinet *origcab) | ||
231 | { | ||
232 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
233 | struct mscabd_folder_data *dat, *ndat; | ||
234 | struct mscabd_cabinet *cab, *ncab; | ||
235 | struct mscabd_folder *fol, *nfol; | ||
236 | struct mscabd_file *fi, *nfi; | ||
237 | struct mspack_system *sys; | ||
238 | |||
239 | if (!base) return; | ||
240 | sys = self->system; | ||
241 | |||
242 | self->error = MSPACK_ERR_OK; | ||
243 | |||
244 | while (origcab) { | ||
245 | /* free files */ | ||
246 | for (fi = origcab->files; fi; fi = nfi) { | ||
247 | nfi = fi->next; | ||
248 | sys->free(fi->filename); | ||
249 | sys->free(fi); | ||
250 | } | ||
251 | |||
252 | /* free folders */ | ||
253 | for (fol = origcab->folders; fol; fol = nfol) { | ||
254 | nfol = fol->next; | ||
255 | |||
256 | /* free folder decompression state if it has been decompressed */ | ||
257 | if (self->d && (self->d->folder == (struct mscabd_folder_p *) fol)) { | ||
258 | if (self->d->infh) sys->close(self->d->infh); | ||
259 | cabd_free_decomp(self); | ||
260 | sys->free(self->d); | ||
261 | self->d = NULL; | ||
262 | } | ||
263 | |||
264 | /* free folder data segments */ | ||
265 | for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) { | ||
266 | ndat = dat->next; | ||
267 | sys->free(dat); | ||
268 | } | ||
269 | sys->free(fol); | ||
270 | } | ||
271 | |||
272 | /* free predecessor cabinets (and the original cabinet's strings) */ | ||
273 | for (cab = origcab; cab; cab = ncab) { | ||
274 | ncab = cab->prevcab; | ||
275 | sys->free(cab->prevname); | ||
276 | sys->free(cab->nextname); | ||
277 | sys->free(cab->previnfo); | ||
278 | sys->free(cab->nextinfo); | ||
279 | if (cab != origcab) sys->free(cab); | ||
280 | } | ||
281 | |||
282 | /* free successor cabinets */ | ||
283 | for (cab = origcab->nextcab; cab; cab = ncab) { | ||
284 | ncab = cab->nextcab; | ||
285 | sys->free(cab->prevname); | ||
286 | sys->free(cab->nextname); | ||
287 | sys->free(cab->previnfo); | ||
288 | sys->free(cab->nextinfo); | ||
289 | sys->free(cab); | ||
290 | } | ||
291 | |||
292 | /* free actual cabinet structure */ | ||
293 | cab = origcab->next; | ||
294 | sys->free(origcab); | ||
295 | |||
296 | /* repeat full procedure again with the cab->next pointer (if set) */ | ||
297 | origcab = cab; | ||
298 | } | ||
299 | } | ||
300 | |||
301 | /*************************************** | ||
302 | * CABD_READ_HEADERS | ||
303 | *************************************** | ||
304 | * reads the cabinet file header, folder list and file list. | ||
305 | * fills out a pre-existing mscabd_cabinet structure, allocates memory | ||
306 | * for folders and files as necessary | ||
307 | */ | ||
308 | static int cabd_read_headers(struct mspack_system *sys, | ||
309 | struct mspack_file *fh, | ||
310 | struct mscabd_cabinet_p *cab, | ||
311 | off_t offset, int salvage, int quiet) | ||
312 | { | ||
313 | int num_folders, num_files, folder_resv, i, x, err, fidx; | ||
314 | struct mscabd_folder_p *fol, *linkfol = NULL; | ||
315 | struct mscabd_file *file, *linkfile = NULL; | ||
316 | unsigned char buf[64]; | ||
317 | |||
318 | /* initialise pointers */ | ||
319 | cab->base.next = NULL; | ||
320 | cab->base.files = NULL; | ||
321 | cab->base.folders = NULL; | ||
322 | cab->base.prevcab = cab->base.nextcab = NULL; | ||
323 | cab->base.prevname = cab->base.nextname = NULL; | ||
324 | cab->base.previnfo = cab->base.nextinfo = NULL; | ||
325 | |||
326 | cab->base.base_offset = offset; | ||
327 | |||
328 | /* seek to CFHEADER */ | ||
329 | if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { | ||
330 | return MSPACK_ERR_SEEK; | ||
331 | } | ||
332 | |||
333 | /* read in the CFHEADER */ | ||
334 | if (sys->read(fh, &buf[0], cfhead_SIZEOF) != cfhead_SIZEOF) { | ||
335 | return MSPACK_ERR_READ; | ||
336 | } | ||
337 | |||
338 | /* check for "MSCF" signature */ | ||
339 | if (EndGetI32(&buf[cfhead_Signature]) != 0x4643534D) { | ||
340 | return MSPACK_ERR_SIGNATURE; | ||
341 | } | ||
342 | |||
343 | /* some basic header fields */ | ||
344 | cab->base.length = EndGetI32(&buf[cfhead_CabinetSize]); | ||
345 | cab->base.set_id = EndGetI16(&buf[cfhead_SetID]); | ||
346 | cab->base.set_index = EndGetI16(&buf[cfhead_CabinetIndex]); | ||
347 | |||
348 | /* get the number of folders */ | ||
349 | num_folders = EndGetI16(&buf[cfhead_NumFolders]); | ||
350 | if (num_folders == 0) { | ||
351 | if (!quiet) sys->message(fh, "no folders in cabinet."); | ||
352 | return MSPACK_ERR_DATAFORMAT; | ||
353 | } | ||
354 | |||
355 | /* get the number of files */ | ||
356 | num_files = EndGetI16(&buf[cfhead_NumFiles]); | ||
357 | if (num_files == 0) { | ||
358 | if (!quiet) sys->message(fh, "no files in cabinet."); | ||
359 | return MSPACK_ERR_DATAFORMAT; | ||
360 | } | ||
361 | |||
362 | /* check cabinet version */ | ||
363 | if ((buf[cfhead_MajorVersion] != 1) && (buf[cfhead_MinorVersion] != 3)) { | ||
364 | if (!quiet) sys->message(fh, "WARNING; cabinet version is not 1.3"); | ||
365 | } | ||
366 | |||
367 | /* read the reserved-sizes part of header, if present */ | ||
368 | cab->base.flags = EndGetI16(&buf[cfhead_Flags]); | ||
369 | |||
370 | if (cab->base.flags & cfheadRESERVE_PRESENT) { | ||
371 | if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) { | ||
372 | return MSPACK_ERR_READ; | ||
373 | } | ||
374 | cab->base.header_resv = EndGetI16(&buf[cfheadext_HeaderReserved]); | ||
375 | folder_resv = buf[cfheadext_FolderReserved]; | ||
376 | cab->block_resv = buf[cfheadext_DataReserved]; | ||
377 | |||
378 | if (cab->base.header_resv > 60000) { | ||
379 | if (!quiet) sys->message(fh, "WARNING; reserved header > 60000."); | ||
380 | } | ||
381 | |||
382 | /* skip the reserved header */ | ||
383 | if (cab->base.header_resv) { | ||
384 | if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) { | ||
385 | return MSPACK_ERR_SEEK; | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | else { | ||
390 | cab->base.header_resv = 0; | ||
391 | folder_resv = 0; | ||
392 | cab->block_resv = 0; | ||
393 | } | ||
394 | |||
395 | /* read name and info of preceeding cabinet in set, if present */ | ||
396 | if (cab->base.flags & cfheadPREV_CABINET) { | ||
397 | cab->base.prevname = cabd_read_string(sys, fh, &err); | ||
398 | if (err) return err; | ||
399 | cab->base.previnfo = cabd_read_string(sys, fh, &err); | ||
400 | if (err) return err; | ||
401 | } | ||
402 | |||
403 | /* read name and info of next cabinet in set, if present */ | ||
404 | if (cab->base.flags & cfheadNEXT_CABINET) { | ||
405 | cab->base.nextname = cabd_read_string(sys, fh, &err); | ||
406 | if (err) return err; | ||
407 | cab->base.nextinfo = cabd_read_string(sys, fh, &err); | ||
408 | if (err) return err; | ||
409 | } | ||
410 | |||
411 | /* read folders */ | ||
412 | for (i = 0; i < num_folders; i++) { | ||
413 | if (sys->read(fh, &buf[0], cffold_SIZEOF) != cffold_SIZEOF) { | ||
414 | return MSPACK_ERR_READ; | ||
415 | } | ||
416 | if (folder_resv) { | ||
417 | if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) { | ||
418 | return MSPACK_ERR_SEEK; | ||
419 | } | ||
420 | } | ||
421 | |||
422 | if (!(fol = (struct mscabd_folder_p *) sys->alloc(sys, sizeof(struct mscabd_folder_p)))) { | ||
423 | return MSPACK_ERR_NOMEMORY; | ||
424 | } | ||
425 | fol->base.next = NULL; | ||
426 | fol->base.comp_type = EndGetI16(&buf[cffold_CompType]); | ||
427 | fol->base.num_blocks = EndGetI16(&buf[cffold_NumBlocks]); | ||
428 | fol->data.next = NULL; | ||
429 | fol->data.cab = (struct mscabd_cabinet_p *) cab; | ||
430 | fol->data.offset = offset + (off_t) | ||
431 | ( (unsigned int) EndGetI32(&buf[cffold_DataOffset]) ); | ||
432 | fol->merge_prev = NULL; | ||
433 | fol->merge_next = NULL; | ||
434 | |||
435 | /* link folder into list of folders */ | ||
436 | if (!linkfol) cab->base.folders = (struct mscabd_folder *) fol; | ||
437 | else linkfol->base.next = (struct mscabd_folder *) fol; | ||
438 | linkfol = fol; | ||
439 | } | ||
440 | |||
441 | /* read files */ | ||
442 | for (i = 0; i < num_files; i++) { | ||
443 | if (sys->read(fh, &buf[0], cffile_SIZEOF) != cffile_SIZEOF) { | ||
444 | return MSPACK_ERR_READ; | ||
445 | } | ||
446 | |||
447 | if (!(file = (struct mscabd_file *) sys->alloc(sys, sizeof(struct mscabd_file)))) { | ||
448 | return MSPACK_ERR_NOMEMORY; | ||
449 | } | ||
450 | |||
451 | file->next = NULL; | ||
452 | file->length = EndGetI32(&buf[cffile_UncompressedSize]); | ||
453 | file->attribs = EndGetI16(&buf[cffile_Attribs]); | ||
454 | file->offset = EndGetI32(&buf[cffile_FolderOffset]); | ||
455 | |||
456 | /* set folder pointer */ | ||
457 | fidx = EndGetI16(&buf[cffile_FolderIndex]); | ||
458 | if (fidx < cffileCONTINUED_FROM_PREV) { | ||
459 | /* normal folder index; count up to the correct folder */ | ||
460 | if (fidx < num_folders) { | ||
461 | struct mscabd_folder *ifol = cab->base.folders; | ||
462 | while (fidx--) if (ifol) ifol = ifol->next; | ||
463 | file->folder = ifol; | ||
464 | } | ||
465 | else { | ||
466 | D(("invalid folder index")) | ||
467 | file->folder = NULL; | ||
468 | } | ||
469 | } | ||
470 | else { | ||
471 | /* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or | ||
472 | * CONTINUED_PREV_AND_NEXT */ | ||
473 | if ((fidx == cffileCONTINUED_TO_NEXT) || | ||
474 | (fidx == cffileCONTINUED_PREV_AND_NEXT)) | ||
475 | { | ||
476 | /* get last folder */ | ||
477 | struct mscabd_folder *ifol = cab->base.folders; | ||
478 | while (ifol->next) ifol = ifol->next; | ||
479 | file->folder = ifol; | ||
480 | |||
481 | /* set "merge next" pointer */ | ||
482 | fol = (struct mscabd_folder_p *) ifol; | ||
483 | if (!fol->merge_next) fol->merge_next = file; | ||
484 | } | ||
485 | |||
486 | if ((fidx == cffileCONTINUED_FROM_PREV) || | ||
487 | (fidx == cffileCONTINUED_PREV_AND_NEXT)) | ||
488 | { | ||
489 | /* get first folder */ | ||
490 | file->folder = cab->base.folders; | ||
491 | |||
492 | /* set "merge prev" pointer */ | ||
493 | fol = (struct mscabd_folder_p *) file->folder; | ||
494 | if (!fol->merge_prev) fol->merge_prev = file; | ||
495 | } | ||
496 | } | ||
497 | |||
498 | /* get time */ | ||
499 | x = EndGetI16(&buf[cffile_Time]); | ||
500 | file->time_h = x >> 11; | ||
501 | file->time_m = (x >> 5) & 0x3F; | ||
502 | file->time_s = (x << 1) & 0x3E; | ||
503 | |||
504 | /* get date */ | ||
505 | x = EndGetI16(&buf[cffile_Date]); | ||
506 | file->date_d = x & 0x1F; | ||
507 | file->date_m = (x >> 5) & 0xF; | ||
508 | file->date_y = (x >> 9) + 1980; | ||
509 | |||
510 | /* get filename */ | ||
511 | file->filename = cabd_read_string(sys, fh, &err); | ||
512 | |||
513 | /* if folder index or filename are bad, either skip it or fail */ | ||
514 | if (err || !file->folder) { | ||
515 | sys->free(file->filename); | ||
516 | sys->free(file); | ||
517 | if (salvage) continue; | ||
518 | return err ? err : MSPACK_ERR_DATAFORMAT; | ||
519 | } | ||
520 | |||
521 | /* link file entry into file list */ | ||
522 | if (!linkfile) cab->base.files = file; | ||
523 | else linkfile->next = file; | ||
524 | linkfile = file; | ||
525 | } | ||
526 | |||
527 | if (cab->base.files == NULL) { | ||
528 | /* We never actually added any files to the file list. Something went wrong. | ||
529 | * The file header may have been invalid */ | ||
530 | D(("No files found, even though header claimed to have %d files", num_files)) | ||
531 | return MSPACK_ERR_DATAFORMAT; | ||
532 | } | ||
533 | |||
534 | return MSPACK_ERR_OK; | ||
535 | } | ||
536 | |||
537 | static char *cabd_read_string(struct mspack_system *sys, | ||
538 | struct mspack_file *fh, int *error) | ||
539 | { | ||
540 | off_t base = sys->tell(fh); | ||
541 | char buf[256], *str; | ||
542 | int len, i, ok; | ||
543 | |||
544 | /* read up to 256 bytes */ | ||
545 | if ((len = sys->read(fh, &buf[0], 256)) <= 0) { | ||
546 | *error = MSPACK_ERR_READ; | ||
547 | return NULL; | ||
548 | } | ||
549 | |||
550 | /* search for a null terminator in the buffer */ | ||
551 | for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; } | ||
552 | /* reject empty strings */ | ||
553 | if (i == 0) ok = 0; | ||
554 | |||
555 | if (!ok) { | ||
556 | *error = MSPACK_ERR_DATAFORMAT; | ||
557 | return NULL; | ||
558 | } | ||
559 | |||
560 | len = i + 1; | ||
561 | |||
562 | /* set the data stream to just after the string and return */ | ||
563 | if (sys->seek(fh, base + (off_t)len, MSPACK_SYS_SEEK_START)) { | ||
564 | *error = MSPACK_ERR_SEEK; | ||
565 | return NULL; | ||
566 | } | ||
567 | |||
568 | if (!(str = (char *) sys->alloc(sys, len))) { | ||
569 | *error = MSPACK_ERR_NOMEMORY; | ||
570 | return NULL; | ||
571 | } | ||
572 | |||
573 | sys->copy(&buf[0], str, len); | ||
574 | *error = MSPACK_ERR_OK; | ||
575 | return str; | ||
576 | } | ||
577 | |||
578 | /*************************************** | ||
579 | * CABD_SEARCH, CABD_FIND | ||
580 | *************************************** | ||
581 | * cabd_search opens a file, finds its extent, allocates a search buffer, | ||
582 | * then reads through the whole file looking for possible cabinet headers. | ||
583 | * if it finds any, it tries to read them as real cabinets. returns a linked | ||
584 | * list of results | ||
585 | * | ||
586 | * cabd_find is the inner loop of cabd_search, to make it easier to | ||
587 | * break out of the loop and be sure that all resources are freed | ||
588 | */ | ||
589 | static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, | ||
590 | const char *filename) | ||
591 | { | ||
592 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
593 | struct mscabd_cabinet_p *cab = NULL; | ||
594 | struct mspack_system *sys; | ||
595 | unsigned char *search_buf; | ||
596 | struct mspack_file *fh; | ||
597 | off_t filelen, firstlen = 0; | ||
598 | |||
599 | if (!base) return NULL; | ||
600 | sys = self->system; | ||
601 | |||
602 | /* allocate a search buffer */ | ||
603 | search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->searchbuf_size); | ||
604 | if (!search_buf) { | ||
605 | self->error = MSPACK_ERR_NOMEMORY; | ||
606 | return NULL; | ||
607 | } | ||
608 | |||
609 | /* open file and get its full file length */ | ||
610 | if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { | ||
611 | if (!(self->error = mspack_sys_filelen(sys, fh, &filelen))) { | ||
612 | self->error = cabd_find(self, search_buf, fh, filename, | ||
613 | filelen, &firstlen, &cab); | ||
614 | } | ||
615 | |||
616 | /* truncated / extraneous data warning: */ | ||
617 | if (firstlen && (firstlen != filelen) && | ||
618 | (!cab || (cab->base.base_offset == 0))) | ||
619 | { | ||
620 | if (firstlen < filelen) { | ||
621 | sys->message(fh, "WARNING; possible %" LD | ||
622 | " extra bytes at end of file.", | ||
623 | filelen - firstlen); | ||
624 | } | ||
625 | else { | ||
626 | sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.", | ||
627 | firstlen - filelen); | ||
628 | } | ||
629 | } | ||
630 | |||
631 | sys->close(fh); | ||
632 | } | ||
633 | else { | ||
634 | self->error = MSPACK_ERR_OPEN; | ||
635 | } | ||
636 | |||
637 | /* free the search buffer */ | ||
638 | sys->free(search_buf); | ||
639 | |||
640 | return (struct mscabd_cabinet *) cab; | ||
641 | } | ||
642 | |||
643 | static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, | ||
644 | struct mspack_file *fh, const char *filename, off_t flen, | ||
645 | off_t *firstlen, struct mscabd_cabinet_p **firstcab) | ||
646 | { | ||
647 | struct mscabd_cabinet_p *cab, *link = NULL; | ||
648 | off_t caboff, offset, length; | ||
649 | struct mspack_system *sys = self->system; | ||
650 | unsigned char *p, *pend, state = 0; | ||
651 | unsigned int cablen_u32 = 0, foffset_u32 = 0; | ||
652 | int false_cabs = 0; | ||
653 | |||
654 | #if !LARGEFILE_SUPPORT | ||
655 | /* detect 32-bit off_t overflow */ | ||
656 | if (flen < 0) { | ||
657 | sys->message(fh, largefile_msg); | ||
658 | return MSPACK_ERR_OK; | ||
659 | } | ||
660 | #endif | ||
661 | |||
662 | /* search through the full file length */ | ||
663 | for (offset = 0; offset < flen; offset += length) { | ||
664 | /* search length is either the full length of the search buffer, or the | ||
665 | * amount of data remaining to the end of the file, whichever is less. */ | ||
666 | length = flen - offset; | ||
667 | if (length > self->searchbuf_size) { | ||
668 | length = self->searchbuf_size; | ||
669 | } | ||
670 | |||
671 | /* fill the search buffer with data from disk */ | ||
672 | if (sys->read(fh, &buf[0], (int) length) != (int) length) { | ||
673 | return MSPACK_ERR_READ; | ||
674 | } | ||
675 | |||
676 | /* FAQ avoidance strategy */ | ||
677 | if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) { | ||
678 | sys->message(fh, "WARNING; found InstallShield header. Use unshield " | ||
679 | "(https://github.com/twogood/unshield) to unpack this file"); | ||
680 | } | ||
681 | |||
682 | /* read through the entire buffer. */ | ||
683 | for (p = &buf[0], pend = &buf[length]; p < pend; ) { | ||
684 | switch (state) { | ||
685 | /* starting state */ | ||
686 | case 0: | ||
687 | /* we spend most of our time in this while loop, looking for | ||
688 | * a leading 'M' of the 'MSCF' signature */ | ||
689 | while (p < pend && *p != 0x4D) p++; | ||
690 | /* if we found tht 'M', advance state */ | ||
691 | if (p++ < pend) state = 1; | ||
692 | break; | ||
693 | |||
694 | /* verify that the next 3 bytes are 'S', 'C' and 'F' */ | ||
695 | case 1: state = (*p++ == 0x53) ? 2 : 0; break; | ||
696 | case 2: state = (*p++ == 0x43) ? 3 : 0; break; | ||
697 | case 3: state = (*p++ == 0x46) ? 4 : 0; break; | ||
698 | |||
699 | /* we don't care about bytes 4-7 (see default: for action) */ | ||
700 | |||
701 | /* bytes 8-11 are the overall length of the cabinet */ | ||
702 | case 8: cablen_u32 = *p++; state++; break; | ||
703 | case 9: cablen_u32 |= *p++ << 8; state++; break; | ||
704 | case 10: cablen_u32 |= *p++ << 16; state++; break; | ||
705 | case 11: cablen_u32 |= *p++ << 24; state++; break; | ||
706 | |||
707 | /* we don't care about bytes 12-15 (see default: for action) */ | ||
708 | |||
709 | /* bytes 16-19 are the offset within the cabinet of the filedata */ | ||
710 | case 16: foffset_u32 = *p++; state++; break; | ||
711 | case 17: foffset_u32 |= *p++ << 8; state++; break; | ||
712 | case 18: foffset_u32 |= *p++ << 16; state++; break; | ||
713 | case 19: foffset_u32 |= *p++ << 24; | ||
714 | /* now we have recieved 20 bytes of potential cab header. work out | ||
715 | * the offset in the file of this potential cabinet */ | ||
716 | caboff = offset + (p - &buf[0]) - 20; | ||
717 | |||
718 | /* should reading cabinet fail, restart search just after 'MSCF' */ | ||
719 | offset = caboff + 4; | ||
720 | |||
721 | /* capture the "length of cabinet" field if there is a cabinet at | ||
722 | * offset 0 in the file, regardless of whether the cabinet can be | ||
723 | * read correctly or not */ | ||
724 | if (caboff == 0) *firstlen = (off_t) cablen_u32; | ||
725 | |||
726 | /* check that the files offset is less than the alleged length of | ||
727 | * the cabinet, and that the offset + the alleged length are | ||
728 | * 'roughly' within the end of overall file length. In salvage | ||
729 | * mode, don't check the alleged length, allow it to be garbage */ | ||
730 | if ((foffset_u32 < cablen_u32) && | ||
731 | ((caboff + (off_t) foffset_u32) < (flen + 32)) && | ||
732 | (((caboff + (off_t) cablen_u32) < (flen + 32)) || self->salvage)) | ||
733 | { | ||
734 | /* likely cabinet found -- try reading it */ | ||
735 | if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { | ||
736 | return MSPACK_ERR_NOMEMORY; | ||
737 | } | ||
738 | cab->base.filename = filename; | ||
739 | if (cabd_read_headers(sys, fh, cab, caboff, self->salvage, 1)) { | ||
740 | /* destroy the failed cabinet */ | ||
741 | cabd_close((struct mscab_decompressor *) self, | ||
742 | (struct mscabd_cabinet *) cab); | ||
743 | false_cabs++; | ||
744 | } | ||
745 | else { | ||
746 | /* cabinet read correctly! */ | ||
747 | |||
748 | /* link the cab into the list */ | ||
749 | if (!link) *firstcab = cab; | ||
750 | else link->base.next = (struct mscabd_cabinet *) cab; | ||
751 | link = cab; | ||
752 | |||
753 | /* cause the search to restart after this cab's data. */ | ||
754 | offset = caboff + (off_t) cablen_u32; | ||
755 | |||
756 | #if !LARGEFILE_SUPPORT | ||
757 | /* detect 32-bit off_t overflow */ | ||
758 | if (offset < caboff) { | ||
759 | sys->message(fh, largefile_msg); | ||
760 | return MSPACK_ERR_OK; | ||
761 | } | ||
762 | #endif | ||
763 | } | ||
764 | } | ||
765 | |||
766 | /* restart search */ | ||
767 | if (offset >= flen) return MSPACK_ERR_OK; | ||
768 | if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { | ||
769 | return MSPACK_ERR_SEEK; | ||
770 | } | ||
771 | length = 0; | ||
772 | p = pend; | ||
773 | state = 0; | ||
774 | break; | ||
775 | |||
776 | /* for bytes 4-7 and 12-15, just advance state/pointer */ | ||
777 | default: | ||
778 | p++, state++; | ||
779 | } /* switch(state) */ | ||
780 | } /* for (... p < pend ...) */ | ||
781 | } /* for (... offset < length ...) */ | ||
782 | |||
783 | if (false_cabs) { | ||
784 | D(("%d false cabinets found", false_cabs)) | ||
785 | } | ||
786 | |||
787 | return MSPACK_ERR_OK; | ||
788 | } | ||
789 | |||
790 | /*************************************** | ||
791 | * CABD_MERGE, CABD_PREPEND, CABD_APPEND | ||
792 | *************************************** | ||
793 | * joins cabinets together, also merges split folders between these two | ||
794 | * cabinets only. This includes freeing the duplicate folder and file(s) | ||
795 | * and allocating a further mscabd_folder_data structure to append to the | ||
796 | * merged folder's data parts list. | ||
797 | */ | ||
798 | static int cabd_prepend(struct mscab_decompressor *base, | ||
799 | struct mscabd_cabinet *cab, | ||
800 | struct mscabd_cabinet *prevcab) | ||
801 | { | ||
802 | return cabd_merge(base, prevcab, cab); | ||
803 | } | ||
804 | |||
805 | static int cabd_append(struct mscab_decompressor *base, | ||
806 | struct mscabd_cabinet *cab, | ||
807 | struct mscabd_cabinet *nextcab) | ||
808 | { | ||
809 | return cabd_merge(base, cab, nextcab); | ||
810 | } | ||
811 | |||
812 | static int cabd_merge(struct mscab_decompressor *base, | ||
813 | struct mscabd_cabinet *lcab, | ||
814 | struct mscabd_cabinet *rcab) | ||
815 | { | ||
816 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
817 | struct mscabd_folder_data *data, *ndata; | ||
818 | struct mscabd_folder_p *lfol, *rfol; | ||
819 | struct mscabd_file *fi, *rfi, *lfi; | ||
820 | struct mscabd_cabinet *cab; | ||
821 | struct mspack_system *sys; | ||
822 | |||
823 | if (!self) return MSPACK_ERR_ARGS; | ||
824 | sys = self->system; | ||
825 | |||
826 | /* basic args check */ | ||
827 | if (!lcab || !rcab || (lcab == rcab)) { | ||
828 | D(("lcab NULL, rcab NULL or lcab = rcab")) | ||
829 | return self->error = MSPACK_ERR_ARGS; | ||
830 | } | ||
831 | |||
832 | /* check there's not already a cabinet attached */ | ||
833 | if (lcab->nextcab || rcab->prevcab) { | ||
834 | D(("cabs already joined")) | ||
835 | return self->error = MSPACK_ERR_ARGS; | ||
836 | } | ||
837 | |||
838 | /* do not create circular cabinet chains */ | ||
839 | for (cab = lcab->prevcab; cab; cab = cab->prevcab) { | ||
840 | if (cab == rcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;} | ||
841 | } | ||
842 | for (cab = rcab->nextcab; cab; cab = cab->nextcab) { | ||
843 | if (cab == lcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;} | ||
844 | } | ||
845 | |||
846 | /* warn about odd set IDs or indices */ | ||
847 | if (lcab->set_id != rcab->set_id) { | ||
848 | sys->message(NULL, "WARNING; merged cabinets with differing Set IDs."); | ||
849 | } | ||
850 | |||
851 | if (lcab->set_index > rcab->set_index) { | ||
852 | sys->message(NULL, "WARNING; merged cabinets with odd order."); | ||
853 | } | ||
854 | |||
855 | /* merging the last folder in lcab with the first folder in rcab */ | ||
856 | lfol = (struct mscabd_folder_p *) lcab->folders; | ||
857 | rfol = (struct mscabd_folder_p *) rcab->folders; | ||
858 | while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; | ||
859 | |||
860 | /* do we need to merge folders? */ | ||
861 | if (!lfol->merge_next && !rfol->merge_prev) { | ||
862 | /* no, at least one of the folders is not for merging */ | ||
863 | |||
864 | /* attach cabs */ | ||
865 | lcab->nextcab = rcab; | ||
866 | rcab->prevcab = lcab; | ||
867 | |||
868 | /* attach folders */ | ||
869 | lfol->base.next = (struct mscabd_folder *) rfol; | ||
870 | |||
871 | /* attach files */ | ||
872 | fi = lcab->files; | ||
873 | while (fi->next) fi = fi->next; | ||
874 | fi->next = rcab->files; | ||
875 | } | ||
876 | else { | ||
877 | /* folder merge required - do the files match? */ | ||
878 | if (! cabd_can_merge_folders(sys, lfol, rfol)) { | ||
879 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
880 | } | ||
881 | |||
882 | /* allocate a new folder data structure */ | ||
883 | if (!(data = (struct mscabd_folder_data *) sys->alloc(sys, sizeof(struct mscabd_folder_data)))) { | ||
884 | return self->error = MSPACK_ERR_NOMEMORY; | ||
885 | } | ||
886 | |||
887 | /* attach cabs */ | ||
888 | lcab->nextcab = rcab; | ||
889 | rcab->prevcab = lcab; | ||
890 | |||
891 | /* append rfol's data to lfol */ | ||
892 | ndata = &lfol->data; | ||
893 | while (ndata->next) ndata = ndata->next; | ||
894 | ndata->next = data; | ||
895 | *data = rfol->data; | ||
896 | rfol->data.next = NULL; | ||
897 | |||
898 | /* lfol becomes rfol. | ||
899 | * NOTE: special case, don't merge if rfol is merge prev and next, | ||
900 | * rfol->merge_next is going to be deleted, so keep lfol's version | ||
901 | * instead */ | ||
902 | lfol->base.num_blocks += rfol->base.num_blocks - 1; | ||
903 | if ((rfol->merge_next == NULL) || | ||
904 | (rfol->merge_next->folder != (struct mscabd_folder *) rfol)) | ||
905 | { | ||
906 | lfol->merge_next = rfol->merge_next; | ||
907 | } | ||
908 | |||
909 | /* attach the rfol's folder (except the merge folder) */ | ||
910 | while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; | ||
911 | lfol->base.next = rfol->base.next; | ||
912 | |||
913 | /* free disused merge folder */ | ||
914 | sys->free(rfol); | ||
915 | |||
916 | /* attach rfol's files */ | ||
917 | fi = lcab->files; | ||
918 | while (fi->next) fi = fi->next; | ||
919 | fi->next = rcab->files; | ||
920 | |||
921 | /* delete all files from rfol's merge folder */ | ||
922 | lfi = NULL; | ||
923 | for (fi = lcab->files; fi ; fi = rfi) { | ||
924 | rfi = fi->next; | ||
925 | /* if file's folder matches the merge folder, unlink and free it */ | ||
926 | if (fi->folder == (struct mscabd_folder *) rfol) { | ||
927 | if (lfi) lfi->next = rfi; else lcab->files = rfi; | ||
928 | sys->free(fi->filename); | ||
929 | sys->free(fi); | ||
930 | } | ||
931 | else lfi = fi; | ||
932 | } | ||
933 | } | ||
934 | |||
935 | /* all done! fix files and folders pointers in all cabs so they all | ||
936 | * point to the same list */ | ||
937 | for (cab = lcab->prevcab; cab; cab = cab->prevcab) { | ||
938 | cab->files = lcab->files; | ||
939 | cab->folders = lcab->folders; | ||
940 | } | ||
941 | |||
942 | for (cab = lcab->nextcab; cab; cab = cab->nextcab) { | ||
943 | cab->files = lcab->files; | ||
944 | cab->folders = lcab->folders; | ||
945 | } | ||
946 | |||
947 | return self->error = MSPACK_ERR_OK; | ||
948 | } | ||
949 | |||
950 | /* decides if two folders are OK to merge */ | ||
951 | static int cabd_can_merge_folders(struct mspack_system *sys, | ||
952 | struct mscabd_folder_p *lfol, | ||
953 | struct mscabd_folder_p *rfol) | ||
954 | { | ||
955 | struct mscabd_file *lfi, *rfi, *l, *r; | ||
956 | int matching = 1; | ||
957 | |||
958 | /* check that both folders use the same compression method/settings */ | ||
959 | if (lfol->base.comp_type != rfol->base.comp_type) { | ||
960 | D(("folder merge: compression type mismatch")) | ||
961 | return 0; | ||
962 | } | ||
963 | |||
964 | /* check there are not too many data blocks after merging */ | ||
965 | if ((lfol->base.num_blocks + rfol->base.num_blocks) > CAB_FOLDERMAX) { | ||
966 | D(("folder merge: too many data blocks in merged folders")) | ||
967 | return 0; | ||
968 | } | ||
969 | |||
970 | if (!(lfi = lfol->merge_next) || !(rfi = rfol->merge_prev)) { | ||
971 | D(("folder merge: one cabinet has no files to merge")) | ||
972 | return 0; | ||
973 | } | ||
974 | |||
975 | /* for all files in lfol (which is the last folder in whichever cab and | ||
976 | * only has files to merge), compare them to the files from rfol. They | ||
977 | * should be identical in number and order. to verify this, check the | ||
978 | * offset and length of each file. */ | ||
979 | for (l=lfi, r=rfi; l; l=l->next, r=r->next) { | ||
980 | if (!r || (l->offset != r->offset) || (l->length != r->length)) { | ||
981 | matching = 0; | ||
982 | break; | ||
983 | } | ||
984 | } | ||
985 | |||
986 | if (matching) return 1; | ||
987 | |||
988 | /* if rfol does not begin with an identical copy of the files in lfol, make | ||
989 | * make a judgement call; if at least ONE file from lfol is in rfol, allow | ||
990 | * the merge with a warning about missing files. */ | ||
991 | matching = 0; | ||
992 | for (l = lfi; l; l = l->next) { | ||
993 | for (r = rfi; r; r = r->next) { | ||
994 | if (l->offset == r->offset && l->length == r->length) break; | ||
995 | } | ||
996 | if (r) matching = 1; else sys->message(NULL, | ||
997 | "WARNING; merged file %s not listed in both cabinets", l->filename); | ||
998 | } | ||
999 | return matching; | ||
1000 | } | ||
1001 | |||
1002 | |||
1003 | /*************************************** | ||
1004 | * CABD_EXTRACT | ||
1005 | *************************************** | ||
1006 | * extracts a file from a cabinet | ||
1007 | */ | ||
1008 | static int cabd_extract(struct mscab_decompressor *base, | ||
1009 | struct mscabd_file *file, const char *filename) | ||
1010 | { | ||
1011 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
1012 | struct mscabd_folder_p *fol; | ||
1013 | struct mspack_system *sys; | ||
1014 | struct mspack_file *fh; | ||
1015 | off_t filelen; | ||
1016 | |||
1017 | if (!self) return MSPACK_ERR_ARGS; | ||
1018 | if (!file) return self->error = MSPACK_ERR_ARGS; | ||
1019 | |||
1020 | sys = self->system; | ||
1021 | fol = (struct mscabd_folder_p *) file->folder; | ||
1022 | |||
1023 | /* if offset is beyond 2GB, nothing can be extracted */ | ||
1024 | if (file->offset > CAB_LENGTHMAX) { | ||
1025 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1026 | } | ||
1027 | |||
1028 | /* if file claims to go beyond 2GB either error out, | ||
1029 | * or in salvage mode reduce file length so it fits 2GB limit | ||
1030 | */ | ||
1031 | filelen = file->length; | ||
1032 | if (filelen > CAB_LENGTHMAX || (file->offset + filelen) > CAB_LENGTHMAX) { | ||
1033 | if (self->salvage) { | ||
1034 | filelen = CAB_LENGTHMAX - file->offset; | ||
1035 | } | ||
1036 | else { | ||
1037 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1038 | } | ||
1039 | } | ||
1040 | |||
1041 | /* extraction impossible if no folder, or folder needs predecessor */ | ||
1042 | if (!fol || fol->merge_prev) { | ||
1043 | sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " | ||
1044 | "cabinet set is incomplete", file->filename); | ||
1045 | return self->error = MSPACK_ERR_DECRUNCH; | ||
1046 | } | ||
1047 | |||
1048 | /* if file goes beyond what can be decoded, given an error. | ||
1049 | * In salvage mode, don't assume block sizes, just try decoding | ||
1050 | */ | ||
1051 | if (!self->salvage) { | ||
1052 | off_t maxlen = fol->base.num_blocks * CAB_BLOCKMAX; | ||
1053 | if ((file->offset + filelen) > maxlen) { | ||
1054 | sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " | ||
1055 | "cabinet set is incomplete", file->filename); | ||
1056 | return self->error = MSPACK_ERR_DECRUNCH; | ||
1057 | } | ||
1058 | } | ||
1059 | |||
1060 | /* allocate generic decompression state */ | ||
1061 | if (!self->d) { | ||
1062 | self->d = (struct mscabd_decompress_state *) sys->alloc(sys, sizeof(struct mscabd_decompress_state)); | ||
1063 | if (!self->d) return self->error = MSPACK_ERR_NOMEMORY; | ||
1064 | self->d->folder = NULL; | ||
1065 | self->d->data = NULL; | ||
1066 | self->d->sys = *sys; | ||
1067 | self->d->sys.read = &cabd_sys_read; | ||
1068 | self->d->sys.write = &cabd_sys_write; | ||
1069 | self->d->state = NULL; | ||
1070 | self->d->infh = NULL; | ||
1071 | self->d->incab = NULL; | ||
1072 | } | ||
1073 | |||
1074 | /* do we need to change folder or reset the current folder? */ | ||
1075 | if ((self->d->folder != fol) || (self->d->offset > file->offset) || | ||
1076 | !self->d->state) | ||
1077 | { | ||
1078 | /* free any existing decompressor */ | ||
1079 | cabd_free_decomp(self); | ||
1080 | |||
1081 | /* do we need to open a new cab file? */ | ||
1082 | if (!self->d->infh || (fol->data.cab != self->d->incab)) { | ||
1083 | /* close previous file handle if from a different cab */ | ||
1084 | if (self->d->infh) sys->close(self->d->infh); | ||
1085 | self->d->incab = fol->data.cab; | ||
1086 | self->d->infh = sys->open(sys, fol->data.cab->base.filename, | ||
1087 | MSPACK_SYS_OPEN_READ); | ||
1088 | if (!self->d->infh) return self->error = MSPACK_ERR_OPEN; | ||
1089 | } | ||
1090 | /* seek to start of data blocks */ | ||
1091 | if (sys->seek(self->d->infh, fol->data.offset, MSPACK_SYS_SEEK_START)) { | ||
1092 | return self->error = MSPACK_ERR_SEEK; | ||
1093 | } | ||
1094 | |||
1095 | /* set up decompressor */ | ||
1096 | if (cabd_init_decomp(self, (unsigned int) fol->base.comp_type)) { | ||
1097 | return self->error; | ||
1098 | } | ||
1099 | |||
1100 | /* initialise new folder state */ | ||
1101 | self->d->folder = fol; | ||
1102 | self->d->data = &fol->data; | ||
1103 | self->d->offset = 0; | ||
1104 | self->d->block = 0; | ||
1105 | self->d->outlen = 0; | ||
1106 | self->d->i_ptr = self->d->i_end = &self->d->input[0]; | ||
1107 | |||
1108 | /* read_error lasts for the lifetime of a decompressor */ | ||
1109 | self->read_error = MSPACK_ERR_OK; | ||
1110 | } | ||
1111 | |||
1112 | /* open file for output */ | ||
1113 | if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { | ||
1114 | return self->error = MSPACK_ERR_OPEN; | ||
1115 | } | ||
1116 | |||
1117 | self->error = MSPACK_ERR_OK; | ||
1118 | |||
1119 | /* if file has more than 0 bytes */ | ||
1120 | if (filelen) { | ||
1121 | off_t bytes; | ||
1122 | int error; | ||
1123 | /* get to correct offset. | ||
1124 | * - use NULL fh to say 'no writing' to cabd_sys_write() | ||
1125 | * - if cabd_sys_read() has an error, it will set self->read_error | ||
1126 | * and pass back MSPACK_ERR_READ | ||
1127 | */ | ||
1128 | self->d->outfh = NULL; | ||
1129 | if ((bytes = file->offset - self->d->offset)) { | ||
1130 | error = self->d->decompress(self->d->state, bytes); | ||
1131 | self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; | ||
1132 | } | ||
1133 | |||
1134 | /* if getting to the correct offset was error free, unpack file */ | ||
1135 | if (!self->error) { | ||
1136 | self->d->outfh = fh; | ||
1137 | error = self->d->decompress(self->d->state, filelen); | ||
1138 | self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; | ||
1139 | } | ||
1140 | } | ||
1141 | |||
1142 | /* close output file */ | ||
1143 | sys->close(fh); | ||
1144 | self->d->outfh = NULL; | ||
1145 | |||
1146 | return self->error; | ||
1147 | } | ||
1148 | |||
1149 | /*************************************** | ||
1150 | * CABD_INIT_DECOMP, CABD_FREE_DECOMP | ||
1151 | *************************************** | ||
1152 | * cabd_init_decomp initialises decompression state, according to which | ||
1153 | * decompression method was used. relies on self->d->folder being the same | ||
1154 | * as when initialised. | ||
1155 | * | ||
1156 | * cabd_free_decomp frees decompression state, according to which method | ||
1157 | * was used. | ||
1158 | */ | ||
1159 | static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct) | ||
1160 | { | ||
1161 | struct mspack_file *fh = (struct mspack_file *) self; | ||
1162 | |||
1163 | self->d->comp_type = ct; | ||
1164 | |||
1165 | switch (ct & cffoldCOMPTYPE_MASK) { | ||
1166 | case cffoldCOMPTYPE_NONE: | ||
1167 | self->d->decompress = (int (*)(void *, off_t)) &noned_decompress; | ||
1168 | self->d->state = noned_init(&self->d->sys, fh, fh, self->buf_size); | ||
1169 | break; | ||
1170 | case cffoldCOMPTYPE_MSZIP: | ||
1171 | self->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress; | ||
1172 | self->d->state = mszipd_init(&self->d->sys, fh, fh, self->buf_size, | ||
1173 | self->fix_mszip); | ||
1174 | break; | ||
1175 | case cffoldCOMPTYPE_QUANTUM: | ||
1176 | self->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress; | ||
1177 | self->d->state = qtmd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, | ||
1178 | self->buf_size); | ||
1179 | break; | ||
1180 | case cffoldCOMPTYPE_LZX: | ||
1181 | self->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress; | ||
1182 | self->d->state = lzxd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, 0, | ||
1183 | self->buf_size, (off_t)0,0); | ||
1184 | break; | ||
1185 | default: | ||
1186 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1187 | } | ||
1188 | return self->error = (self->d->state) ? MSPACK_ERR_OK : MSPACK_ERR_NOMEMORY; | ||
1189 | } | ||
1190 | |||
1191 | static void cabd_free_decomp(struct mscab_decompressor_p *self) { | ||
1192 | if (!self || !self->d || !self->d->state) return; | ||
1193 | |||
1194 | switch (self->d->comp_type & cffoldCOMPTYPE_MASK) { | ||
1195 | case cffoldCOMPTYPE_NONE: noned_free((struct noned_state *) self->d->state); break; | ||
1196 | case cffoldCOMPTYPE_MSZIP: mszipd_free((struct mszipd_stream *) self->d->state); break; | ||
1197 | case cffoldCOMPTYPE_QUANTUM: qtmd_free((struct qtmd_stream *) self->d->state); break; | ||
1198 | case cffoldCOMPTYPE_LZX: lzxd_free((struct lzxd_stream *) self->d->state); break; | ||
1199 | } | ||
1200 | self->d->decompress = NULL; | ||
1201 | self->d->state = NULL; | ||
1202 | } | ||
1203 | |||
1204 | /*************************************** | ||
1205 | * CABD_SYS_READ, CABD_SYS_WRITE | ||
1206 | *************************************** | ||
1207 | * cabd_sys_read is the internal reader function which the decompressors | ||
1208 | * use. will read data blocks (and merge split blocks) from the cabinet | ||
1209 | * and serve the read bytes to the decompressors | ||
1210 | * | ||
1211 | * cabd_sys_write is the internal writer function which the decompressors | ||
1212 | * use. it either writes data to disk (self->d->outfh) with the real | ||
1213 | * sys->write() function, or does nothing with the data when | ||
1214 | * self->d->outfh == NULL. advances self->d->offset | ||
1215 | */ | ||
1216 | static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) { | ||
1217 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; | ||
1218 | unsigned char *buf = (unsigned char *) buffer; | ||
1219 | struct mspack_system *sys = self->system; | ||
1220 | int avail, todo, outlen, ignore_cksum, ignore_blocksize; | ||
1221 | |||
1222 | ignore_cksum = self->salvage || | ||
1223 | (self->fix_mszip && | ||
1224 | ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP)); | ||
1225 | ignore_blocksize = self->salvage; | ||
1226 | |||
1227 | todo = bytes; | ||
1228 | while (todo > 0) { | ||
1229 | avail = self->d->i_end - self->d->i_ptr; | ||
1230 | |||
1231 | /* if out of input data, read a new block */ | ||
1232 | if (avail) { | ||
1233 | /* copy as many input bytes available as possible */ | ||
1234 | if (avail > todo) avail = todo; | ||
1235 | sys->copy(self->d->i_ptr, buf, (size_t) avail); | ||
1236 | self->d->i_ptr += avail; | ||
1237 | buf += avail; | ||
1238 | todo -= avail; | ||
1239 | } | ||
1240 | else { | ||
1241 | /* out of data, read a new block */ | ||
1242 | |||
1243 | /* check if we're out of input blocks, advance block counter */ | ||
1244 | if (self->d->block++ >= self->d->folder->base.num_blocks) { | ||
1245 | if (!self->salvage) { | ||
1246 | self->read_error = MSPACK_ERR_DATAFORMAT; | ||
1247 | } | ||
1248 | else { | ||
1249 | D(("Ran out of CAB input blocks prematurely")) | ||
1250 | } | ||
1251 | break; | ||
1252 | } | ||
1253 | |||
1254 | /* read a block */ | ||
1255 | self->read_error = cabd_sys_read_block(sys, self->d, &outlen, | ||
1256 | ignore_cksum, ignore_blocksize); | ||
1257 | if (self->read_error) return -1; | ||
1258 | self->d->outlen += outlen; | ||
1259 | |||
1260 | /* special Quantum hack -- trailer byte to allow the decompressor | ||
1261 | * to realign itself. CAB Quantum blocks, unlike LZX blocks, can have | ||
1262 | * anything from 0 to 4 trailing null bytes. */ | ||
1263 | if ((self->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) { | ||
1264 | *self->d->i_end++ = 0xFF; | ||
1265 | } | ||
1266 | |||
1267 | /* is this the last block? */ | ||
1268 | if (self->d->block >= self->d->folder->base.num_blocks) { | ||
1269 | if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) { | ||
1270 | /* special LZX hack -- on the last block, inform LZX of the | ||
1271 | * size of the output data stream. */ | ||
1272 | lzxd_set_output_length((struct lzxd_stream *) self->d->state, self->d->outlen); | ||
1273 | } | ||
1274 | } | ||
1275 | } /* if (avail) */ | ||
1276 | } /* while (todo > 0) */ | ||
1277 | return bytes - todo; | ||
1278 | } | ||
1279 | |||
1280 | static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) { | ||
1281 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; | ||
1282 | self->d->offset += bytes; | ||
1283 | if (self->d->outfh) { | ||
1284 | return self->system->write(self->d->outfh, buffer, bytes); | ||
1285 | } | ||
1286 | return bytes; | ||
1287 | } | ||
1288 | |||
1289 | /*************************************** | ||
1290 | * CABD_SYS_READ_BLOCK | ||
1291 | *************************************** | ||
1292 | * reads a whole data block from a cab file. the block may span more than | ||
1293 | * one cab file, if it does then the fragments will be reassembled | ||
1294 | */ | ||
1295 | static int cabd_sys_read_block(struct mspack_system *sys, | ||
1296 | struct mscabd_decompress_state *d, | ||
1297 | int *out, int ignore_cksum, | ||
1298 | int ignore_blocksize) | ||
1299 | { | ||
1300 | unsigned char hdr[cfdata_SIZEOF]; | ||
1301 | unsigned int cksum; | ||
1302 | int len, full_len; | ||
1303 | |||
1304 | /* reset the input block pointer and end of block pointer */ | ||
1305 | d->i_ptr = d->i_end = &d->input[0]; | ||
1306 | |||
1307 | do { | ||
1308 | /* read the block header */ | ||
1309 | if (sys->read(d->infh, &hdr[0], cfdata_SIZEOF) != cfdata_SIZEOF) { | ||
1310 | return MSPACK_ERR_READ; | ||
1311 | } | ||
1312 | |||
1313 | /* skip any reserved block headers */ | ||
1314 | if (d->data->cab->block_resv && | ||
1315 | sys->seek(d->infh, (off_t) d->data->cab->block_resv, | ||
1316 | MSPACK_SYS_SEEK_CUR)) | ||
1317 | { | ||
1318 | return MSPACK_ERR_SEEK; | ||
1319 | } | ||
1320 | |||
1321 | /* blocks must not be over CAB_INPUTMAX in size */ | ||
1322 | len = EndGetI16(&hdr[cfdata_CompressedSize]); | ||
1323 | full_len = (d->i_end - d->i_ptr) + len; /* include cab-spanning blocks */ | ||
1324 | if (full_len > CAB_INPUTMAX) { | ||
1325 | D(("block size %d > CAB_INPUTMAX", full_len)); | ||
1326 | /* in salvage mode, blocks can be 65535 bytes but no more than that */ | ||
1327 | if (!ignore_blocksize || full_len > CAB_INPUTMAX_SALVAGE) { | ||
1328 | return MSPACK_ERR_DATAFORMAT; | ||
1329 | } | ||
1330 | } | ||
1331 | |||
1332 | /* blocks must not expand to more than CAB_BLOCKMAX */ | ||
1333 | if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) { | ||
1334 | D(("block size > CAB_BLOCKMAX")) | ||
1335 | if (!ignore_blocksize) return MSPACK_ERR_DATAFORMAT; | ||
1336 | } | ||
1337 | |||
1338 | /* read the block data */ | ||
1339 | if (sys->read(d->infh, d->i_end, len) != len) { | ||
1340 | return MSPACK_ERR_READ; | ||
1341 | } | ||
1342 | |||
1343 | /* perform checksum test on the block (if one is stored) */ | ||
1344 | if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) { | ||
1345 | unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0); | ||
1346 | if (cabd_checksum(&hdr[4], 4, sum2) != cksum) { | ||
1347 | if (!ignore_cksum) return MSPACK_ERR_CHECKSUM; | ||
1348 | sys->message(d->infh, "WARNING; bad block checksum found"); | ||
1349 | } | ||
1350 | } | ||
1351 | |||
1352 | /* advance end of block pointer to include newly read data */ | ||
1353 | d->i_end += len; | ||
1354 | |||
1355 | /* uncompressed size == 0 means this block was part of a split block | ||
1356 | * and it continues as the first block of the next cabinet in the set. | ||
1357 | * otherwise, this is the last part of the block, and no more block | ||
1358 | * reading needs to be done. | ||
1359 | */ | ||
1360 | /* EXIT POINT OF LOOP -- uncompressed size != 0 */ | ||
1361 | if ((*out = EndGetI16(&hdr[cfdata_UncompressedSize]))) { | ||
1362 | return MSPACK_ERR_OK; | ||
1363 | } | ||
1364 | |||
1365 | /* otherwise, advance to next cabinet */ | ||
1366 | |||
1367 | /* close current file handle */ | ||
1368 | sys->close(d->infh); | ||
1369 | d->infh = NULL; | ||
1370 | |||
1371 | /* advance to next member in the cabinet set */ | ||
1372 | if (!(d->data = d->data->next)) { | ||
1373 | sys->message(d->infh, "WARNING; ran out of cabinets in set. Are any missing?"); | ||
1374 | return MSPACK_ERR_DATAFORMAT; | ||
1375 | } | ||
1376 | |||
1377 | /* open next cab file */ | ||
1378 | d->incab = d->data->cab; | ||
1379 | if (!(d->infh = sys->open(sys, d->incab->base.filename, | ||
1380 | MSPACK_SYS_OPEN_READ))) | ||
1381 | { | ||
1382 | return MSPACK_ERR_OPEN; | ||
1383 | } | ||
1384 | |||
1385 | /* seek to start of data blocks */ | ||
1386 | if (sys->seek(d->infh, d->data->offset, MSPACK_SYS_SEEK_START)) { | ||
1387 | return MSPACK_ERR_SEEK; | ||
1388 | } | ||
1389 | } while (1); | ||
1390 | |||
1391 | /* not reached */ | ||
1392 | return MSPACK_ERR_OK; | ||
1393 | } | ||
1394 | |||
1395 | static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes, | ||
1396 | unsigned int cksum) | ||
1397 | { | ||
1398 | unsigned int len, ul = 0; | ||
1399 | |||
1400 | for (len = bytes >> 2; len--; data += 4) { | ||
1401 | cksum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24)); | ||
1402 | } | ||
1403 | |||
1404 | switch (bytes & 3) { | ||
1405 | case 3: ul |= *data++ << 16; /*@fallthrough@*/ | ||
1406 | case 2: ul |= *data++ << 8; /*@fallthrough@*/ | ||
1407 | case 1: ul |= *data; | ||
1408 | } | ||
1409 | cksum ^= ul; | ||
1410 | |||
1411 | return cksum; | ||
1412 | } | ||
1413 | |||
1414 | /*************************************** | ||
1415 | * NONED_INIT, NONED_DECOMPRESS, NONED_FREE | ||
1416 | *************************************** | ||
1417 | * the "not compressed" method decompressor | ||
1418 | */ | ||
1419 | struct noned_state { | ||
1420 | struct mspack_system *sys; | ||
1421 | struct mspack_file *i; | ||
1422 | struct mspack_file *o; | ||
1423 | unsigned char *buf; | ||
1424 | int bufsize; | ||
1425 | }; | ||
1426 | |||
1427 | static struct noned_state *noned_init(struct mspack_system *sys, | ||
1428 | struct mspack_file *in, | ||
1429 | struct mspack_file *out, | ||
1430 | int bufsize) | ||
1431 | { | ||
1432 | struct noned_state *state = (struct noned_state *) sys->alloc(sys, sizeof(struct noned_state)); | ||
1433 | unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) bufsize); | ||
1434 | if (state && buf) { | ||
1435 | state->sys = sys; | ||
1436 | state->i = in; | ||
1437 | state->o = out; | ||
1438 | state->buf = buf; | ||
1439 | state->bufsize = bufsize; | ||
1440 | } | ||
1441 | else { | ||
1442 | sys->free(buf); | ||
1443 | sys->free(state); | ||
1444 | state = NULL; | ||
1445 | } | ||
1446 | return state; | ||
1447 | } | ||
1448 | |||
1449 | static int noned_decompress(struct noned_state *s, off_t bytes) { | ||
1450 | int run; | ||
1451 | while (bytes > 0) { | ||
1452 | run = (bytes > s->bufsize) ? s->bufsize : (int) bytes; | ||
1453 | if (s->sys->read(s->i, &s->buf[0], run) != run) return MSPACK_ERR_READ; | ||
1454 | if (s->sys->write(s->o, &s->buf[0], run) != run) return MSPACK_ERR_WRITE; | ||
1455 | bytes -= run; | ||
1456 | } | ||
1457 | return MSPACK_ERR_OK; | ||
1458 | } | ||
1459 | |||
1460 | static void noned_free(struct noned_state *state) { | ||
1461 | struct mspack_system *sys; | ||
1462 | if (state) { | ||
1463 | sys = state->sys; | ||
1464 | sys->free(state->buf); | ||
1465 | sys->free(state); | ||
1466 | } | ||
1467 | } | ||
1468 | |||
1469 | |||
1470 | /*************************************** | ||
1471 | * CABD_PARAM | ||
1472 | *************************************** | ||
1473 | * allows a parameter to be set | ||
1474 | */ | ||
1475 | static int cabd_param(struct mscab_decompressor *base, int param, int value) { | ||
1476 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
1477 | if (!self) return MSPACK_ERR_ARGS; | ||
1478 | |||
1479 | switch (param) { | ||
1480 | case MSCABD_PARAM_SEARCHBUF: | ||
1481 | if (value < 4) return MSPACK_ERR_ARGS; | ||
1482 | self->searchbuf_size = value; | ||
1483 | break; | ||
1484 | case MSCABD_PARAM_FIXMSZIP: | ||
1485 | self->fix_mszip = value; | ||
1486 | break; | ||
1487 | case MSCABD_PARAM_DECOMPBUF: | ||
1488 | if (value < 4) return MSPACK_ERR_ARGS; | ||
1489 | self->buf_size = value; | ||
1490 | break; | ||
1491 | case MSCABD_PARAM_SALVAGE: | ||
1492 | self->salvage = value; | ||
1493 | break; | ||
1494 | default: | ||
1495 | return MSPACK_ERR_ARGS; | ||
1496 | } | ||
1497 | return MSPACK_ERR_OK; | ||
1498 | } | ||
1499 | |||
1500 | /*************************************** | ||
1501 | * CABD_ERROR | ||
1502 | *************************************** | ||
1503 | * returns the last error that occurred | ||
1504 | */ | ||
1505 | static int cabd_error(struct mscab_decompressor *base) { | ||
1506 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
1507 | return (self) ? self->error : MSPACK_ERR_ARGS; | ||
1508 | } | ||
diff --git a/utils/rbutilqt/mspack/chm.h b/utils/rbutilqt/mspack/chm.h new file mode 100644 index 0000000000..4b19f1505a --- /dev/null +++ b/utils/rbutilqt/mspack/chm.h | |||
@@ -0,0 +1,122 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifndef MSPACK_CHM_H | ||
11 | #define MSPACK_CHM_H 1 | ||
12 | |||
13 | #include "lzx.h" | ||
14 | |||
15 | /* generic CHM definitions */ | ||
16 | |||
17 | #define chmhead_Signature (0x0000) | ||
18 | #define chmhead_Version (0x0004) | ||
19 | #define chmhead_HeaderLen (0x0008) | ||
20 | #define chmhead_Unknown1 (0x000C) | ||
21 | #define chmhead_Timestamp (0x0010) | ||
22 | #define chmhead_LanguageID (0x0014) | ||
23 | #define chmhead_GUID1 (0x0018) | ||
24 | #define chmhead_GUID2 (0x0028) | ||
25 | #define chmhead_SIZEOF (0x0038) | ||
26 | |||
27 | #define chmhst_OffsetHS0 (0x0000) | ||
28 | #define chmhst_LengthHS0 (0x0008) | ||
29 | #define chmhst_OffsetHS1 (0x0010) | ||
30 | #define chmhst_LengthHS1 (0x0018) | ||
31 | #define chmhst_SIZEOF (0x0020) | ||
32 | #define chmhst3_OffsetCS0 (0x0020) | ||
33 | #define chmhst3_SIZEOF (0x0028) | ||
34 | |||
35 | #define chmhs0_Unknown1 (0x0000) | ||
36 | #define chmhs0_Unknown2 (0x0004) | ||
37 | #define chmhs0_FileLen (0x0008) | ||
38 | #define chmhs0_Unknown3 (0x0010) | ||
39 | #define chmhs0_Unknown4 (0x0014) | ||
40 | #define chmhs0_SIZEOF (0x0018) | ||
41 | |||
42 | #define chmhs1_Signature (0x0000) | ||
43 | #define chmhs1_Version (0x0004) | ||
44 | #define chmhs1_HeaderLen (0x0008) | ||
45 | #define chmhs1_Unknown1 (0x000C) | ||
46 | #define chmhs1_ChunkSize (0x0010) | ||
47 | #define chmhs1_Density (0x0014) | ||
48 | #define chmhs1_Depth (0x0018) | ||
49 | #define chmhs1_IndexRoot (0x001C) | ||
50 | #define chmhs1_FirstPMGL (0x0020) | ||
51 | #define chmhs1_LastPMGL (0x0024) | ||
52 | #define chmhs1_Unknown2 (0x0028) | ||
53 | #define chmhs1_NumChunks (0x002C) | ||
54 | #define chmhs1_LanguageID (0x0030) | ||
55 | #define chmhs1_GUID (0x0034) | ||
56 | #define chmhs1_Unknown3 (0x0044) | ||
57 | #define chmhs1_Unknown4 (0x0048) | ||
58 | #define chmhs1_Unknown5 (0x004C) | ||
59 | #define chmhs1_Unknown6 (0x0050) | ||
60 | #define chmhs1_SIZEOF (0x0054) | ||
61 | |||
62 | #define pmgl_Signature (0x0000) | ||
63 | #define pmgl_QuickRefSize (0x0004) | ||
64 | #define pmgl_Unknown1 (0x0008) | ||
65 | #define pmgl_PrevChunk (0x000C) | ||
66 | #define pmgl_NextChunk (0x0010) | ||
67 | #define pmgl_Entries (0x0014) | ||
68 | #define pmgl_headerSIZEOF (0x0014) | ||
69 | |||
70 | #define pmgi_Signature (0x0000) | ||
71 | #define pmgi_QuickRefSize (0x0004) | ||
72 | #define pmgi_Entries (0x0008) | ||
73 | #define pmgi_headerSIZEOF (0x000C) | ||
74 | |||
75 | #define lzxcd_Length (0x0000) | ||
76 | #define lzxcd_Signature (0x0004) | ||
77 | #define lzxcd_Version (0x0008) | ||
78 | #define lzxcd_ResetInterval (0x000C) | ||
79 | #define lzxcd_WindowSize (0x0010) | ||
80 | #define lzxcd_CacheSize (0x0014) | ||
81 | #define lzxcd_Unknown1 (0x0018) | ||
82 | #define lzxcd_SIZEOF (0x001C) | ||
83 | |||
84 | #define lzxrt_Unknown1 (0x0000) | ||
85 | #define lzxrt_NumEntries (0x0004) | ||
86 | #define lzxrt_EntrySize (0x0008) | ||
87 | #define lzxrt_TableOffset (0x000C) | ||
88 | #define lzxrt_UncompLen (0x0010) | ||
89 | #define lzxrt_CompLen (0x0018) | ||
90 | #define lzxrt_FrameLen (0x0020) | ||
91 | #define lzxrt_Entries (0x0028) | ||
92 | #define lzxrt_headerSIZEOF (0x0028) | ||
93 | |||
94 | /* CHM compression definitions */ | ||
95 | |||
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/utils/rbutilqt/mspack/chmc.c b/utils/rbutilqt/mspack/chmc.c new file mode 100644 index 0000000000..b9a1d8856c --- /dev/null +++ b/utils/rbutilqt/mspack/chmc.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | /* CHM compression implementation */ | ||
11 | |||
12 | #include "system-mspack.h" | ||
13 | #include "chm.h" | ||
14 | |||
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/utils/rbutilqt/mspack/chmd.c b/utils/rbutilqt/mspack/chmd.c new file mode 100644 index 0000000000..6c8481db14 --- /dev/null +++ b/utils/rbutilqt/mspack/chmd.c | |||
@@ -0,0 +1,1377 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2018 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | /* CHM decompression implementation */ | ||
11 | |||
12 | #include "system-mspack.h" | ||
13 | #include "chm.h" | ||
14 | |||
15 | /* prototypes */ | ||
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 | unsigned 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 (memcmp(&buf[chmhead_GUID1], &guids[0], 32L) != 0) { | ||
296 | D(("incorrect GUIDs")) | ||
297 | return MSPACK_ERR_SIGNATURE; | ||
298 | } | ||
299 | |||
300 | chm->version = EndGetI32(&buf[chmhead_Version]); | ||
301 | chm->timestamp = EndGetM32(&buf[chmhead_Timestamp]); | ||
302 | chm->language = EndGetI32(&buf[chmhead_LanguageID]); | ||
303 | if (chm->version > 3) { | ||
304 | sys->message(fh, "WARNING; CHM version > 3"); | ||
305 | } | ||
306 | |||
307 | /* read the header section table */ | ||
308 | if (sys->read(fh, &buf[0], chmhst3_SIZEOF) != chmhst3_SIZEOF) { | ||
309 | return MSPACK_ERR_READ; | ||
310 | } | ||
311 | |||
312 | /* chmhst3_OffsetCS0 does not exist in version 1 or 2 CHM files. | ||
313 | * The offset will be corrected later, once HS1 is read. | ||
314 | */ | ||
315 | if (read_off64(&offset, &buf[chmhst_OffsetHS0], sys, fh) || | ||
316 | read_off64(&chm->dir_offset, &buf[chmhst_OffsetHS1], sys, fh) || | ||
317 | read_off64(&chm->sec0.offset, &buf[chmhst3_OffsetCS0], sys, fh)) | ||
318 | { | ||
319 | return MSPACK_ERR_DATAFORMAT; | ||
320 | } | ||
321 | |||
322 | /* seek to header section 0 */ | ||
323 | if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { | ||
324 | return MSPACK_ERR_SEEK; | ||
325 | } | ||
326 | |||
327 | /* read header section 0 */ | ||
328 | if (sys->read(fh, &buf[0], chmhs0_SIZEOF) != chmhs0_SIZEOF) { | ||
329 | return MSPACK_ERR_READ; | ||
330 | } | ||
331 | if (read_off64(&chm->length, &buf[chmhs0_FileLen], sys, fh)) { | ||
332 | return MSPACK_ERR_DATAFORMAT; | ||
333 | } | ||
334 | |||
335 | /* seek to header section 1 */ | ||
336 | if (sys->seek(fh, chm->dir_offset, MSPACK_SYS_SEEK_START)) { | ||
337 | return MSPACK_ERR_SEEK; | ||
338 | } | ||
339 | |||
340 | /* read header section 1 */ | ||
341 | if (sys->read(fh, &buf[0], chmhs1_SIZEOF) != chmhs1_SIZEOF) { | ||
342 | return MSPACK_ERR_READ; | ||
343 | } | ||
344 | |||
345 | chm->dir_offset = sys->tell(fh); | ||
346 | chm->chunk_size = EndGetI32(&buf[chmhs1_ChunkSize]); | ||
347 | chm->density = EndGetI32(&buf[chmhs1_Density]); | ||
348 | chm->depth = EndGetI32(&buf[chmhs1_Depth]); | ||
349 | chm->index_root = EndGetI32(&buf[chmhs1_IndexRoot]); | ||
350 | chm->num_chunks = EndGetI32(&buf[chmhs1_NumChunks]); | ||
351 | chm->first_pmgl = EndGetI32(&buf[chmhs1_FirstPMGL]); | ||
352 | chm->last_pmgl = EndGetI32(&buf[chmhs1_LastPMGL]); | ||
353 | |||
354 | if (chm->version < 3) { | ||
355 | /* versions before 3 don't have chmhst3_OffsetCS0 */ | ||
356 | chm->sec0.offset = chm->dir_offset + (chm->chunk_size * chm->num_chunks); | ||
357 | } | ||
358 | |||
359 | /* check if content offset or file size is wrong */ | ||
360 | if (chm->sec0.offset > chm->length) { | ||
361 | D(("content section begins after file has ended")) | ||
362 | return MSPACK_ERR_DATAFORMAT; | ||
363 | } | ||
364 | |||
365 | /* ensure there are chunks and that chunk size is | ||
366 | * large enough for signature and num_entries */ | ||
367 | if (chm->chunk_size < (pmgl_Entries + 2)) { | ||
368 | D(("chunk size not large enough")) | ||
369 | return MSPACK_ERR_DATAFORMAT; | ||
370 | } | ||
371 | if (chm->num_chunks == 0) { | ||
372 | D(("no chunks")) | ||
373 | return MSPACK_ERR_DATAFORMAT; | ||
374 | } | ||
375 | |||
376 | /* The chunk_cache data structure is not great; large values for num_chunks | ||
377 | * or num_chunks*chunk_size can exhaust all memory. Until a better chunk | ||
378 | * cache is implemented, put arbitrary limits on num_chunks and chunk size. | ||
379 | */ | ||
380 | if (chm->num_chunks > 100000) { | ||
381 | D(("more than 100,000 chunks")) | ||
382 | return MSPACK_ERR_DATAFORMAT; | ||
383 | } | ||
384 | if (chm->chunk_size > 8192) { | ||
385 | D(("chunk size over 8192 (get in touch if this is valid)")) | ||
386 | return MSPACK_ERR_DATAFORMAT; | ||
387 | } | ||
388 | if ((off_t)chm->chunk_size * (off_t)chm->num_chunks > chm->length) { | ||
389 | D(("chunks larger than entire file")) | ||
390 | return MSPACK_ERR_DATAFORMAT; | ||
391 | } | ||
392 | |||
393 | /* common sense checks on header section 1 fields */ | ||
394 | if (chm->chunk_size != 4096) { | ||
395 | sys->message(fh, "WARNING; chunk size is not 4096"); | ||
396 | } | ||
397 | if (chm->first_pmgl != 0) { | ||
398 | sys->message(fh, "WARNING; first PMGL chunk is not zero"); | ||
399 | } | ||
400 | if (chm->first_pmgl > chm->last_pmgl) { | ||
401 | D(("first pmgl chunk is after last pmgl chunk")) | ||
402 | return MSPACK_ERR_DATAFORMAT; | ||
403 | } | ||
404 | if (chm->index_root != 0xFFFFFFFF && chm->index_root >= chm->num_chunks) { | ||
405 | D(("index_root outside valid range")) | ||
406 | return MSPACK_ERR_DATAFORMAT; | ||
407 | } | ||
408 | |||
409 | /* if we are doing a quick read, stop here! */ | ||
410 | if (!entire) { | ||
411 | return MSPACK_ERR_OK; | ||
412 | } | ||
413 | |||
414 | /* seek to the first PMGL chunk, and reduce the number of chunks to read */ | ||
415 | if ((x = chm->first_pmgl) != 0) { | ||
416 | if (sys->seek(fh,(off_t) (x * chm->chunk_size), MSPACK_SYS_SEEK_CUR)) { | ||
417 | return MSPACK_ERR_SEEK; | ||
418 | } | ||
419 | } | ||
420 | num_chunks = chm->last_pmgl - x + 1; | ||
421 | |||
422 | if (!(chunk = (unsigned char *) sys->alloc(sys, (size_t)chm->chunk_size))) { | ||
423 | return MSPACK_ERR_NOMEMORY; | ||
424 | } | ||
425 | |||
426 | /* read and process all chunks from FirstPMGL to LastPMGL */ | ||
427 | errors = 0; | ||
428 | while (num_chunks--) { | ||
429 | /* read next chunk */ | ||
430 | if (sys->read(fh, chunk, (int)chm->chunk_size) != (int)chm->chunk_size) { | ||
431 | sys->free(chunk); | ||
432 | return MSPACK_ERR_READ; | ||
433 | } | ||
434 | |||
435 | /* process only directory (PMGL) chunks */ | ||
436 | if (EndGetI32(&chunk[pmgl_Signature]) != 0x4C474D50) continue; | ||
437 | |||
438 | if (EndGetI32(&chunk[pmgl_QuickRefSize]) < 2) { | ||
439 | sys->message(fh, "WARNING; PMGL quickref area is too small"); | ||
440 | } | ||
441 | if (EndGetI32(&chunk[pmgl_QuickRefSize]) > | ||
442 | (chm->chunk_size - pmgl_Entries)) | ||
443 | { | ||
444 | sys->message(fh, "WARNING; PMGL quickref area is too large"); | ||
445 | } | ||
446 | |||
447 | p = &chunk[pmgl_Entries]; | ||
448 | end = &chunk[chm->chunk_size - 2]; | ||
449 | num_entries = EndGetI16(end); | ||
450 | |||
451 | while (num_entries--) { | ||
452 | READ_ENCINT(name_len); | ||
453 | if (name_len > (unsigned int) (end - p)) goto chunk_end; | ||
454 | name = p; p += name_len; | ||
455 | READ_ENCINT(section); | ||
456 | READ_ENCINT(offset); | ||
457 | READ_ENCINT(length); | ||
458 | |||
459 | /* ignore blank or one-char (e.g. "/") filenames we'd return as blank */ | ||
460 | if (name_len < 2 || !name[0] || !name[1]) continue; | ||
461 | |||
462 | /* empty files and directory names are stored as a file entry at | ||
463 | * offset 0 with length 0. We want to keep empty files, but not | ||
464 | * directory names, which end with a "/" */ | ||
465 | if ((offset == 0) && (length == 0)) { | ||
466 | if ((name_len > 0) && (name[name_len-1] == '/')) continue; | ||
467 | } | ||
468 | |||
469 | if (section > 1) { | ||
470 | sys->message(fh, "invalid section number '%u'.", section); | ||
471 | continue; | ||
472 | } | ||
473 | |||
474 | if (!(fi = (struct mschmd_file *) sys->alloc(sys, sizeof(struct mschmd_file) + name_len + 1))) { | ||
475 | sys->free(chunk); | ||
476 | return MSPACK_ERR_NOMEMORY; | ||
477 | } | ||
478 | |||
479 | fi->next = NULL; | ||
480 | fi->filename = (char *) &fi[1]; | ||
481 | fi->section = ((section == 0) ? (struct mschmd_section *) (&chm->sec0) | ||
482 | : (struct mschmd_section *) (&chm->sec1)); | ||
483 | fi->offset = offset; | ||
484 | fi->length = length; | ||
485 | sys->copy(name, fi->filename, (size_t) name_len); | ||
486 | fi->filename[name_len] = '\0'; | ||
487 | |||
488 | if (name[0] == ':' && name[1] == ':') { | ||
489 | /* system file */ | ||
490 | if (name_len == 40 && memcmp(name, content_name, 40) == 0) { | ||
491 | chm->sec1.content = fi; | ||
492 | } | ||
493 | else if (name_len == 44 && memcmp(name, control_name, 44) == 0) { | ||
494 | chm->sec1.control = fi; | ||
495 | } | ||
496 | else if (name_len == 41 && memcmp(name, spaninfo_name, 41) == 0) { | ||
497 | chm->sec1.spaninfo = fi; | ||
498 | } | ||
499 | else if (name_len == 105 && memcmp(name, rtable_name, 105) == 0) { | ||
500 | chm->sec1.rtable = fi; | ||
501 | } | ||
502 | fi->next = chm->sysfiles; | ||
503 | chm->sysfiles = fi; | ||
504 | } | ||
505 | else { | ||
506 | /* normal file */ | ||
507 | if (link) link->next = fi; else chm->files = fi; | ||
508 | link = fi; | ||
509 | } | ||
510 | } | ||
511 | |||
512 | /* this is reached either when num_entries runs out, or if | ||
513 | * reading data from the chunk reached a premature end of chunk */ | ||
514 | chunk_end: | ||
515 | if (num_entries >= 0) { | ||
516 | D(("chunk ended before all entries could be read")) | ||
517 | errors++; | ||
518 | } | ||
519 | |||
520 | } | ||
521 | sys->free(chunk); | ||
522 | return (errors > 0) ? MSPACK_ERR_DATAFORMAT : MSPACK_ERR_OK; | ||
523 | } | ||
524 | |||
525 | /*************************************** | ||
526 | * CHMD_FAST_FIND | ||
527 | *************************************** | ||
528 | * uses PMGI index chunks and quickref data to quickly locate a file | ||
529 | * directly from the on-disk index. | ||
530 | * | ||
531 | * TODO: protect against infinite loops in chunks (where pgml_NextChunk | ||
532 | * or a PMGI index entry point to an already visited chunk) | ||
533 | */ | ||
534 | static int chmd_fast_find(struct mschm_decompressor *base, | ||
535 | struct mschmd_header *chm, const char *filename, | ||
536 | struct mschmd_file *f_ptr, int f_size) | ||
537 | { | ||
538 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; | ||
539 | struct mspack_system *sys; | ||
540 | struct mspack_file *fh; | ||
541 | /* p and end are initialised to prevent MSVC warning about "potentially" | ||
542 | * uninitialised usage. This is provably untrue, but MS won't fix: | ||
543 | * https://developercommunity.visualstudio.com/content/problem/363489/c4701-false-positive-warning.html */ | ||
544 | const unsigned char *chunk, *p = NULL, *end = NULL; | ||
545 | int err = MSPACK_ERR_OK, result = -1; | ||
546 | unsigned int n, sec; | ||
547 | |||
548 | if (!self || !chm || !f_ptr || (f_size != sizeof(struct mschmd_file))) { | ||
549 | return MSPACK_ERR_ARGS; | ||
550 | } | ||
551 | sys = self->system; | ||
552 | |||
553 | /* clear the results structure */ | ||
554 | memset(f_ptr, 0, f_size); | ||
555 | |||
556 | if (!(fh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ))) { | ||
557 | return MSPACK_ERR_OPEN; | ||
558 | } | ||
559 | |||
560 | /* go through PMGI chunk hierarchy to reach PMGL chunk */ | ||
561 | if (chm->index_root < chm->num_chunks) { | ||
562 | n = chm->index_root; | ||
563 | for (;;) { | ||
564 | if (!(chunk = read_chunk(self, chm, fh, n))) { | ||
565 | sys->close(fh); | ||
566 | return self->error; | ||
567 | } | ||
568 | |||
569 | /* search PMGI/PMGL chunk. exit early if no entry found */ | ||
570 | if ((result = search_chunk(chm, chunk, filename, &p, &end)) <= 0) { | ||
571 | break; | ||
572 | } | ||
573 | |||
574 | /* found result. loop around for next chunk if this is PMGI */ | ||
575 | if (chunk[3] == 0x4C) break; else READ_ENCINT(n); | ||
576 | } | ||
577 | } | ||
578 | else { | ||
579 | /* PMGL chunks only, search from first_pmgl to last_pmgl */ | ||
580 | for (n = chm->first_pmgl; n <= chm->last_pmgl; | ||
581 | n = EndGetI32(&chunk[pmgl_NextChunk])) | ||
582 | { | ||
583 | if (!(chunk = read_chunk(self, chm, fh, n))) { | ||
584 | err = self->error; | ||
585 | break; | ||
586 | } | ||
587 | |||
588 | /* search PMGL chunk. exit if file found */ | ||
589 | if ((result = search_chunk(chm, chunk, filename, &p, &end)) > 0) { | ||
590 | break; | ||
591 | } | ||
592 | |||
593 | /* stop simple infinite loops: can't visit the same chunk twice */ | ||
594 | if (n == EndGetI32(&chunk[pmgl_NextChunk])) { | ||
595 | break; | ||
596 | } | ||
597 | } | ||
598 | } | ||
599 | |||
600 | /* if we found a file, read it */ | ||
601 | if (result > 0) { | ||
602 | READ_ENCINT(sec); | ||
603 | f_ptr->section = (sec == 0) ? (struct mschmd_section *) &chm->sec0 | ||
604 | : (struct mschmd_section *) &chm->sec1; | ||
605 | READ_ENCINT(f_ptr->offset); | ||
606 | READ_ENCINT(f_ptr->length); | ||
607 | } | ||
608 | else if (result < 0) { | ||
609 | err = MSPACK_ERR_DATAFORMAT; | ||
610 | } | ||
611 | |||
612 | sys->close(fh); | ||
613 | return self->error = err; | ||
614 | |||
615 | chunk_end: | ||
616 | D(("read beyond end of chunk entries")) | ||
617 | sys->close(fh); | ||
618 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
619 | } | ||
620 | |||
621 | /* reads the given chunk into memory, storing it in a chunk cache | ||
622 | * so it doesn't need to be read from disk more than once | ||
623 | */ | ||
624 | static unsigned char *read_chunk(struct mschm_decompressor_p *self, | ||
625 | struct mschmd_header *chm, | ||
626 | struct mspack_file *fh, | ||
627 | unsigned int chunk_num) | ||
628 | { | ||
629 | struct mspack_system *sys = self->system; | ||
630 | unsigned char *buf; | ||
631 | |||
632 | /* check arguments - most are already checked by chmd_fast_find */ | ||
633 | if (chunk_num >= chm->num_chunks) return NULL; | ||
634 | |||
635 | /* ensure chunk cache is available */ | ||
636 | if (!chm->chunk_cache) { | ||
637 | size_t size = sizeof(unsigned char *) * chm->num_chunks; | ||
638 | if (!(chm->chunk_cache = (unsigned char **) sys->alloc(sys, size))) { | ||
639 | self->error = MSPACK_ERR_NOMEMORY; | ||
640 | return NULL; | ||
641 | } | ||
642 | memset(chm->chunk_cache, 0, size); | ||
643 | } | ||
644 | |||
645 | /* try to answer out of chunk cache */ | ||
646 | if (chm->chunk_cache[chunk_num]) return chm->chunk_cache[chunk_num]; | ||
647 | |||
648 | /* need to read chunk - allocate memory for it */ | ||
649 | if (!(buf = (unsigned char *) sys->alloc(sys, chm->chunk_size))) { | ||
650 | self->error = MSPACK_ERR_NOMEMORY; | ||
651 | return NULL; | ||
652 | } | ||
653 | |||
654 | /* seek to block and read it */ | ||
655 | if (sys->seek(fh, (off_t) (chm->dir_offset + (chunk_num * chm->chunk_size)), | ||
656 | MSPACK_SYS_SEEK_START)) | ||
657 | { | ||
658 | self->error = MSPACK_ERR_SEEK; | ||
659 | sys->free(buf); | ||
660 | return NULL; | ||
661 | } | ||
662 | if (sys->read(fh, buf, (int)chm->chunk_size) != (int)chm->chunk_size) { | ||
663 | self->error = MSPACK_ERR_READ; | ||
664 | sys->free(buf); | ||
665 | return NULL; | ||
666 | } | ||
667 | |||
668 | /* check the signature. Is is PMGL or PMGI? */ | ||
669 | if (!((buf[0] == 0x50) && (buf[1] == 0x4D) && (buf[2] == 0x47) && | ||
670 | ((buf[3] == 0x4C) || (buf[3] == 0x49)))) | ||
671 | { | ||
672 | self->error = MSPACK_ERR_SEEK; | ||
673 | sys->free(buf); | ||
674 | return NULL; | ||
675 | } | ||
676 | |||
677 | /* all OK. Store chunk in cache and return it */ | ||
678 | return chm->chunk_cache[chunk_num] = buf; | ||
679 | } | ||
680 | |||
681 | /* searches a PMGI/PMGL chunk for a given filename entry. Returns -1 on | ||
682 | * data format error, 0 if entry definitely not found, 1 if entry | ||
683 | * found. In the latter case, *result and *result_end are set pointing | ||
684 | * to that entry's data (either the "next chunk" ENCINT for a PMGI or | ||
685 | * the section, offset and length ENCINTs for a PMGL). | ||
686 | * | ||
687 | * In the case of PMGL chunks, the entry has definitely been | ||
688 | * found. In the case of PMGI chunks, the entry which points to the | ||
689 | * chunk that may eventually contain that entry has been found. | ||
690 | */ | ||
691 | static int search_chunk(struct mschmd_header *chm, | ||
692 | const unsigned char *chunk, | ||
693 | const char *filename, | ||
694 | const unsigned char **result, | ||
695 | const unsigned char **result_end) | ||
696 | { | ||
697 | const unsigned char *start, *end, *p; | ||
698 | unsigned int qr_size, num_entries, qr_entries, qr_density, name_len; | ||
699 | unsigned int L, R, M, fname_len, entries_off, is_pmgl; | ||
700 | int cmp; | ||
701 | |||
702 | fname_len = strlen(filename); | ||
703 | |||
704 | /* PMGL chunk or PMGI chunk? (note: read_chunk() has already | ||
705 | * checked the rest of the characters in the chunk signature) */ | ||
706 | if (chunk[3] == 0x4C) { | ||
707 | is_pmgl = 1; | ||
708 | entries_off = pmgl_Entries; | ||
709 | } | ||
710 | else { | ||
711 | is_pmgl = 0; | ||
712 | entries_off = pmgi_Entries; | ||
713 | } | ||
714 | |||
715 | /* Step 1: binary search first filename of each QR entry | ||
716 | * - target filename == entry | ||
717 | * found file | ||
718 | * - target filename < all entries | ||
719 | * file not found | ||
720 | * - target filename > all entries | ||
721 | * proceed to step 2 using final entry | ||
722 | * - target filename between two searched entries | ||
723 | * proceed to step 2 | ||
724 | */ | ||
725 | qr_size = EndGetI32(&chunk[pmgl_QuickRefSize]); | ||
726 | start = &chunk[chm->chunk_size - 2]; | ||
727 | end = &chunk[chm->chunk_size - qr_size]; | ||
728 | num_entries = EndGetI16(start); | ||
729 | qr_density = 1 + (1 << chm->density); | ||
730 | qr_entries = (num_entries + qr_density-1) / qr_density; | ||
731 | |||
732 | if (num_entries == 0) { | ||
733 | D(("chunk has no entries")) | ||
734 | return -1; | ||
735 | } | ||
736 | |||
737 | if (qr_size > chm->chunk_size) { | ||
738 | D(("quickref size > chunk size")) | ||
739 | return -1; | ||
740 | } | ||
741 | |||
742 | *result_end = end; | ||
743 | |||
744 | if (((int)qr_entries * 2) > (start - end)) { | ||
745 | D(("WARNING; more quickrefs than quickref space")) | ||
746 | qr_entries = 0; /* but we can live with it */ | ||
747 | } | ||
748 | |||
749 | if (qr_entries > 0) { | ||
750 | L = 0; | ||
751 | R = qr_entries - 1; | ||
752 | do { | ||
753 | /* pick new midpoint */ | ||
754 | M = (L + R) >> 1; | ||
755 | |||
756 | /* compare filename with entry QR points to */ | ||
757 | p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; | ||
758 | READ_ENCINT(name_len); | ||
759 | if (name_len > (unsigned int) (end - p)) goto chunk_end; | ||
760 | cmp = compare(filename, (char *)p, fname_len, name_len); | ||
761 | |||
762 | if (cmp == 0) break; | ||
763 | else if (cmp < 0) { if (M) R = M - 1; else return 0; } | ||
764 | else if (cmp > 0) L = M + 1; | ||
765 | } while (L <= R); | ||
766 | M = (L + R) >> 1; | ||
767 | |||
768 | if (cmp == 0) { | ||
769 | /* exact match! */ | ||
770 | p += name_len; | ||
771 | *result = p; | ||
772 | return 1; | ||
773 | } | ||
774 | |||
775 | /* otherwise, read the group of entries for QR entry M */ | ||
776 | p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; | ||
777 | num_entries -= (M * qr_density); | ||
778 | if (num_entries > qr_density) num_entries = qr_density; | ||
779 | } | ||
780 | else { | ||
781 | p = &chunk[entries_off]; | ||
782 | } | ||
783 | |||
784 | /* Step 2: linear search through the set of entries reached in step 1. | ||
785 | * - filename == any entry | ||
786 | * found entry | ||
787 | * - filename < all entries (PMGI) or any entry (PMGL) | ||
788 | * entry not found, stop now | ||
789 | * - filename > all entries | ||
790 | * entry not found (PMGL) / maybe found (PMGI) | ||
791 | * - | ||
792 | */ | ||
793 | *result = NULL; | ||
794 | while (num_entries-- > 0) { | ||
795 | READ_ENCINT(name_len); | ||
796 | if (name_len > (unsigned int) (end - p)) goto chunk_end; | ||
797 | cmp = compare(filename, (char *)p, fname_len, name_len); | ||
798 | p += name_len; | ||
799 | |||
800 | if (cmp == 0) { | ||
801 | /* entry found */ | ||
802 | *result = p; | ||
803 | return 1; | ||
804 | } | ||
805 | |||
806 | if (cmp < 0) { | ||
807 | /* entry not found (PMGL) / maybe found (PMGI) */ | ||
808 | break; | ||
809 | } | ||
810 | |||
811 | /* read and ignore the rest of this entry */ | ||
812 | if (is_pmgl) { | ||
813 | READ_ENCINT(R); /* skip section */ | ||
814 | READ_ENCINT(R); /* skip offset */ | ||
815 | READ_ENCINT(R); /* skip length */ | ||
816 | } | ||
817 | else { | ||
818 | *result = p; /* store potential final result */ | ||
819 | READ_ENCINT(R); /* skip chunk number */ | ||
820 | } | ||
821 | } | ||
822 | |||
823 | /* PMGL? not found. PMGI? maybe found */ | ||
824 | return (is_pmgl) ? 0 : (*result ? 1 : 0); | ||
825 | |||
826 | chunk_end: | ||
827 | D(("reached end of chunk data while searching")) | ||
828 | return -1; | ||
829 | } | ||
830 | |||
831 | #if HAVE_TOWLOWER | ||
832 | # include <wctype.h> | ||
833 | # define TOLOWER(x) towlower(x) | ||
834 | #else | ||
835 | # include <ctype.h> | ||
836 | # define TOLOWER(x) tolower(x) | ||
837 | #endif | ||
838 | |||
839 | /* decodes a UTF-8 character from s[] into c. Will not read past e. | ||
840 | * doesn't test that extension bytes are %10xxxxxx. | ||
841 | * allows some overlong encodings. | ||
842 | */ | ||
843 | #define GET_UTF8_CHAR(s, e, c) do { \ | ||
844 | unsigned char x = *s++; \ | ||
845 | if (x < 0x80) c = x; \ | ||
846 | else if (x >= 0xC2 && x < 0xE0 && s < e) { \ | ||
847 | c = (x & 0x1F) << 6 | (*s++ & 0x3F); \ | ||
848 | } \ | ||
849 | else if (x >= 0xE0 && x < 0xF0 && s+1 < e) { \ | ||
850 | c = (x & 0x0F) << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F); \ | ||
851 | s += 2; \ | ||
852 | } \ | ||
853 | else if (x >= 0xF0 && x <= 0xF5 && s+2 < e) { \ | ||
854 | c = (x & 0x07) << 18 | (s[0] & 0x3F) << 12 | \ | ||
855 | (s[1] & 0x3F) << 6 | (s[2] & 0x3F); \ | ||
856 | if (c > 0x10FFFF) c = 0xFFFD; \ | ||
857 | s += 3; \ | ||
858 | } \ | ||
859 | else c = 0xFFFD; \ | ||
860 | } while (0) | ||
861 | |||
862 | /* case-insensitively compares two UTF8 encoded strings. String length for | ||
863 | * both strings must be provided, null bytes are not terminators */ | ||
864 | static inline int compare(const char *s1, const char *s2, int l1, int l2) { | ||
865 | register const unsigned char *p1 = (const unsigned char *) s1; | ||
866 | register const unsigned char *p2 = (const unsigned char *) s2; | ||
867 | register const unsigned char *e1 = p1 + l1, *e2 = p2 + l2; | ||
868 | int c1, c2; | ||
869 | |||
870 | while (p1 < e1 && p2 < e2) { | ||
871 | GET_UTF8_CHAR(p1, e1, c1); | ||
872 | GET_UTF8_CHAR(p2, e2, c2); | ||
873 | if (c1 == c2) continue; | ||
874 | c1 = TOLOWER(c1); | ||
875 | c2 = TOLOWER(c2); | ||
876 | if (c1 != c2) return c1 - c2; | ||
877 | } | ||
878 | return l1 - l2; | ||
879 | } | ||
880 | |||
881 | |||
882 | /*************************************** | ||
883 | * CHMD_EXTRACT | ||
884 | *************************************** | ||
885 | * extracts a file from a CHM helpfile | ||
886 | */ | ||
887 | static int chmd_extract(struct mschm_decompressor *base, | ||
888 | struct mschmd_file *file, const char *filename) | ||
889 | { | ||
890 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; | ||
891 | struct mspack_system *sys; | ||
892 | struct mschmd_header *chm; | ||
893 | struct mspack_file *fh; | ||
894 | off_t bytes; | ||
895 | |||
896 | if (!self) return MSPACK_ERR_ARGS; | ||
897 | if (!file || !file->section) return self->error = MSPACK_ERR_ARGS; | ||
898 | sys = self->system; | ||
899 | chm = file->section->chm; | ||
900 | |||
901 | /* create decompression state if it doesn't exist */ | ||
902 | if (!self->d) { | ||
903 | self->d = (struct mschmd_decompress_state *) sys->alloc(sys, sizeof(struct mschmd_decompress_state)); | ||
904 | if (!self->d) return self->error = MSPACK_ERR_NOMEMORY; | ||
905 | self->d->chm = chm; | ||
906 | self->d->offset = 0; | ||
907 | self->d->state = NULL; | ||
908 | self->d->sys = *sys; | ||
909 | self->d->sys.write = &chmd_sys_write; | ||
910 | self->d->infh = NULL; | ||
911 | self->d->outfh = NULL; | ||
912 | } | ||
913 | |||
914 | /* open input chm file if not open, or the open one is a different chm */ | ||
915 | if (!self->d->infh || (self->d->chm != chm)) { | ||
916 | if (self->d->infh) sys->close(self->d->infh); | ||
917 | if (self->d->state) lzxd_free(self->d->state); | ||
918 | self->d->chm = chm; | ||
919 | self->d->offset = 0; | ||
920 | self->d->state = NULL; | ||
921 | self->d->infh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ); | ||
922 | if (!self->d->infh) return self->error = MSPACK_ERR_OPEN; | ||
923 | } | ||
924 | |||
925 | /* open file for output */ | ||
926 | if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { | ||
927 | return self->error = MSPACK_ERR_OPEN; | ||
928 | } | ||
929 | |||
930 | /* if file is empty, simply creating it is enough */ | ||
931 | if (!file->length) { | ||
932 | sys->close(fh); | ||
933 | return self->error = MSPACK_ERR_OK; | ||
934 | } | ||
935 | |||
936 | self->error = MSPACK_ERR_OK; | ||
937 | |||
938 | switch (file->section->id) { | ||
939 | case 0: /* Uncompressed section file */ | ||
940 | /* simple seek + copy */ | ||
941 | if (sys->seek(self->d->infh, file->section->chm->sec0.offset | ||
942 | + file->offset, MSPACK_SYS_SEEK_START)) | ||
943 | { | ||
944 | self->error = MSPACK_ERR_SEEK; | ||
945 | } | ||
946 | else { | ||
947 | unsigned char buf[512]; | ||
948 | off_t length = file->length; | ||
949 | while (length > 0) { | ||
950 | int run = sizeof(buf); | ||
951 | if ((off_t)run > length) run = (int)length; | ||
952 | if (sys->read(self->d->infh, &buf[0], run) != run) { | ||
953 | self->error = MSPACK_ERR_READ; | ||
954 | break; | ||
955 | } | ||
956 | if (sys->write(fh, &buf[0], run) != run) { | ||
957 | self->error = MSPACK_ERR_WRITE; | ||
958 | break; | ||
959 | } | ||
960 | length -= run; | ||
961 | } | ||
962 | } | ||
963 | break; | ||
964 | |||
965 | case 1: /* MSCompressed section file */ | ||
966 | /* (re)initialise compression state if we it is not yet initialised, | ||
967 | * or we have advanced too far and have to backtrack | ||
968 | */ | ||
969 | if (!self->d->state || (file->offset < self->d->offset)) { | ||
970 | if (self->d->state) { | ||
971 | lzxd_free(self->d->state); | ||
972 | self->d->state = NULL; | ||
973 | } | ||
974 | if (chmd_init_decomp(self, file)) break; | ||
975 | } | ||
976 | |||
977 | /* seek to input data */ | ||
978 | if (sys->seek(self->d->infh, self->d->inoffset, MSPACK_SYS_SEEK_START)) { | ||
979 | self->error = MSPACK_ERR_SEEK; | ||
980 | break; | ||
981 | } | ||
982 | |||
983 | /* get to correct offset. */ | ||
984 | self->d->outfh = NULL; | ||
985 | if ((bytes = file->offset - self->d->offset)) { | ||
986 | self->error = lzxd_decompress(self->d->state, bytes); | ||
987 | } | ||
988 | |||
989 | /* if getting to the correct offset was error free, unpack file */ | ||
990 | if (!self->error) { | ||
991 | self->d->outfh = fh; | ||
992 | self->error = lzxd_decompress(self->d->state, file->length); | ||
993 | } | ||
994 | |||
995 | /* save offset in input source stream, in case there is a section 0 | ||
996 | * file between now and the next section 1 file extracted */ | ||
997 | self->d->inoffset = sys->tell(self->d->infh); | ||
998 | |||
999 | /* if an LZX error occured, the LZX decompressor is now useless */ | ||
1000 | if (self->error) { | ||
1001 | if (self->d->state) lzxd_free(self->d->state); | ||
1002 | self->d->state = NULL; | ||
1003 | } | ||
1004 | break; | ||
1005 | } | ||
1006 | |||
1007 | sys->close(fh); | ||
1008 | return self->error; | ||
1009 | } | ||
1010 | |||
1011 | /*************************************** | ||
1012 | * CHMD_SYS_WRITE | ||
1013 | *************************************** | ||
1014 | * chmd_sys_write is the internal writer function which the decompressor | ||
1015 | * uses. If either writes data to disk (self->d->outfh) with the real | ||
1016 | * sys->write() function, or does nothing with the data when | ||
1017 | * self->d->outfh == NULL. advances self->d->offset. | ||
1018 | */ | ||
1019 | static int chmd_sys_write(struct mspack_file *file, void *buffer, int bytes) { | ||
1020 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) file; | ||
1021 | self->d->offset += bytes; | ||
1022 | if (self->d->outfh) { | ||
1023 | return self->system->write(self->d->outfh, buffer, bytes); | ||
1024 | } | ||
1025 | return bytes; | ||
1026 | } | ||
1027 | |||
1028 | /*************************************** | ||
1029 | * CHMD_INIT_DECOMP | ||
1030 | *************************************** | ||
1031 | * Initialises the LZX decompressor to decompress the compressed stream, | ||
1032 | * from the nearest reset offset and length that is needed for the given | ||
1033 | * file. | ||
1034 | */ | ||
1035 | static int chmd_init_decomp(struct mschm_decompressor_p *self, | ||
1036 | struct mschmd_file *file) | ||
1037 | { | ||
1038 | int window_size, window_bits, reset_interval, entry, err; | ||
1039 | struct mspack_system *sys = self->system; | ||
1040 | struct mschmd_sec_mscompressed *sec; | ||
1041 | unsigned char *data; | ||
1042 | off_t length, offset; | ||
1043 | |||
1044 | sec = (struct mschmd_sec_mscompressed *) file->section; | ||
1045 | |||
1046 | /* ensure we have a mscompressed content section */ | ||
1047 | err = find_sys_file(self, sec, &sec->content, content_name); | ||
1048 | if (err) return self->error = err; | ||
1049 | |||
1050 | /* ensure we have a ControlData file */ | ||
1051 | err = find_sys_file(self, sec, &sec->control, control_name); | ||
1052 | if (err) return self->error = err; | ||
1053 | |||
1054 | /* read ControlData */ | ||
1055 | if (sec->control->length < lzxcd_SIZEOF) { | ||
1056 | D(("ControlData file is too short")) | ||
1057 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1058 | } | ||
1059 | if (!(data = read_sys_file(self, sec->control))) { | ||
1060 | D(("can't read mscompressed control data file")) | ||
1061 | return self->error; | ||
1062 | } | ||
1063 | |||
1064 | /* check LZXC signature */ | ||
1065 | if (EndGetI32(&data[lzxcd_Signature]) != 0x43585A4C) { | ||
1066 | sys->free(data); | ||
1067 | return self->error = MSPACK_ERR_SIGNATURE; | ||
1068 | } | ||
1069 | |||
1070 | /* read reset_interval and window_size and validate version number */ | ||
1071 | switch (EndGetI32(&data[lzxcd_Version])) { | ||
1072 | case 1: | ||
1073 | reset_interval = EndGetI32(&data[lzxcd_ResetInterval]); | ||
1074 | window_size = EndGetI32(&data[lzxcd_WindowSize]); | ||
1075 | break; | ||
1076 | case 2: | ||
1077 | reset_interval = EndGetI32(&data[lzxcd_ResetInterval]) * LZX_FRAME_SIZE; | ||
1078 | window_size = EndGetI32(&data[lzxcd_WindowSize]) * LZX_FRAME_SIZE; | ||
1079 | break; | ||
1080 | default: | ||
1081 | D(("bad controldata version")) | ||
1082 | sys->free(data); | ||
1083 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1084 | } | ||
1085 | |||
1086 | /* free ControlData */ | ||
1087 | sys->free(data); | ||
1088 | |||
1089 | /* find window_bits from window_size */ | ||
1090 | switch (window_size) { | ||
1091 | case 0x008000: window_bits = 15; break; | ||
1092 | case 0x010000: window_bits = 16; break; | ||
1093 | case 0x020000: window_bits = 17; break; | ||
1094 | case 0x040000: window_bits = 18; break; | ||
1095 | case 0x080000: window_bits = 19; break; | ||
1096 | case 0x100000: window_bits = 20; break; | ||
1097 | case 0x200000: window_bits = 21; break; | ||
1098 | default: | ||
1099 | D(("bad controldata window size")) | ||
1100 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1101 | } | ||
1102 | |||
1103 | /* validate reset_interval */ | ||
1104 | if (reset_interval == 0 || reset_interval % LZX_FRAME_SIZE) { | ||
1105 | D(("bad controldata reset interval")) | ||
1106 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1107 | } | ||
1108 | |||
1109 | /* which reset table entry would we like? */ | ||
1110 | entry = file->offset / reset_interval; | ||
1111 | /* convert from reset interval multiple (usually 64k) to 32k frames */ | ||
1112 | entry *= reset_interval / LZX_FRAME_SIZE; | ||
1113 | |||
1114 | /* read the reset table entry */ | ||
1115 | if (read_reset_table(self, sec, entry, &length, &offset)) { | ||
1116 | /* the uncompressed length given in the reset table is dishonest. | ||
1117 | * the uncompressed data is always padded out from the given | ||
1118 | * uncompressed length up to the next reset interval */ | ||
1119 | length += reset_interval - 1; | ||
1120 | length &= -reset_interval; | ||
1121 | } | ||
1122 | else { | ||
1123 | /* if we can't read the reset table entry, just start from | ||
1124 | * the beginning. Use spaninfo to get the uncompressed length */ | ||
1125 | entry = 0; | ||
1126 | offset = 0; | ||
1127 | err = read_spaninfo(self, sec, &length); | ||
1128 | } | ||
1129 | if (err) return self->error = err; | ||
1130 | |||
1131 | /* get offset of compressed data stream: | ||
1132 | * = offset of uncompressed section from start of file | ||
1133 | * + offset of compressed stream from start of uncompressed section | ||
1134 | * + offset of chosen reset interval from start of compressed stream */ | ||
1135 | self->d->inoffset = file->section->chm->sec0.offset + sec->content->offset + offset; | ||
1136 | |||
1137 | /* set start offset and overall remaining stream length */ | ||
1138 | self->d->offset = entry * LZX_FRAME_SIZE; | ||
1139 | length -= self->d->offset; | ||
1140 | |||
1141 | /* initialise LZX stream */ | ||
1142 | self->d->state = lzxd_init(&self->d->sys, self->d->infh, | ||
1143 | (struct mspack_file *) self, window_bits, | ||
1144 | reset_interval / LZX_FRAME_SIZE, | ||
1145 | 4096, length, 0); | ||
1146 | if (!self->d->state) self->error = MSPACK_ERR_NOMEMORY; | ||
1147 | return self->error; | ||
1148 | } | ||
1149 | |||
1150 | /*************************************** | ||
1151 | * READ_RESET_TABLE | ||
1152 | *************************************** | ||
1153 | * Reads one entry out of the reset table. Also reads the uncompressed | ||
1154 | * data length. Writes these to offset_ptr and length_ptr respectively. | ||
1155 | * Returns non-zero for success, zero for failure. | ||
1156 | */ | ||
1157 | static int read_reset_table(struct mschm_decompressor_p *self, | ||
1158 | struct mschmd_sec_mscompressed *sec, | ||
1159 | unsigned int entry, | ||
1160 | off_t *length_ptr, off_t *offset_ptr) | ||
1161 | { | ||
1162 | struct mspack_system *sys = self->system; | ||
1163 | unsigned char *data; | ||
1164 | unsigned int pos, entrysize; | ||
1165 | |||
1166 | /* do we have a ResetTable file? */ | ||
1167 | int err = find_sys_file(self, sec, &sec->rtable, rtable_name); | ||
1168 | if (err) return 0; | ||
1169 | |||
1170 | /* read ResetTable file */ | ||
1171 | if (sec->rtable->length < lzxrt_headerSIZEOF) { | ||
1172 | D(("ResetTable file is too short")) | ||
1173 | return 0; | ||
1174 | } | ||
1175 | if (!(data = read_sys_file(self, sec->rtable))) { | ||
1176 | D(("can't read reset table")) | ||
1177 | return 0; | ||
1178 | } | ||
1179 | |||
1180 | /* check sanity of reset table */ | ||
1181 | if (EndGetI32(&data[lzxrt_FrameLen]) != LZX_FRAME_SIZE) { | ||
1182 | D(("bad reset table frame length")) | ||
1183 | sys->free(data); | ||
1184 | return 0; | ||
1185 | } | ||
1186 | |||
1187 | /* get the uncompressed length of the LZX stream */ | ||
1188 | if (read_off64(length_ptr, &data[lzxrt_UncompLen], sys, self->d->infh)) { | ||
1189 | sys->free(data); | ||
1190 | return 0; | ||
1191 | } | ||
1192 | |||
1193 | entrysize = EndGetI32(&data[lzxrt_EntrySize]); | ||
1194 | pos = EndGetI32(&data[lzxrt_TableOffset]) + (entry * entrysize); | ||
1195 | |||
1196 | /* ensure reset table entry for this offset exists */ | ||
1197 | if (entry < EndGetI32(&data[lzxrt_NumEntries]) && | ||
1198 | pos <= (sec->rtable->length - entrysize)) | ||
1199 | { | ||
1200 | switch (entrysize) { | ||
1201 | case 4: | ||
1202 | *offset_ptr = EndGetI32(&data[pos]); | ||
1203 | err = 0; | ||
1204 | break; | ||
1205 | case 8: | ||
1206 | err = read_off64(offset_ptr, &data[pos], sys, self->d->infh); | ||
1207 | break; | ||
1208 | default: | ||
1209 | D(("reset table entry size neither 4 nor 8")) | ||
1210 | err = 1; | ||
1211 | break; | ||
1212 | } | ||
1213 | } | ||
1214 | else { | ||
1215 | D(("bad reset interval")) | ||
1216 | err = 1; | ||
1217 | } | ||
1218 | |||
1219 | /* free the reset table */ | ||
1220 | sys->free(data); | ||
1221 | |||
1222 | /* return success */ | ||
1223 | return (err == 0); | ||
1224 | } | ||
1225 | |||
1226 | /*************************************** | ||
1227 | * READ_SPANINFO | ||
1228 | *************************************** | ||
1229 | * Reads the uncompressed data length from the spaninfo file. | ||
1230 | * Returns zero for success or a non-zero error code for failure. | ||
1231 | */ | ||
1232 | static int read_spaninfo(struct mschm_decompressor_p *self, | ||
1233 | struct mschmd_sec_mscompressed *sec, | ||
1234 | off_t *length_ptr) | ||
1235 | { | ||
1236 | struct mspack_system *sys = self->system; | ||
1237 | unsigned char *data; | ||
1238 | |||
1239 | /* find SpanInfo file */ | ||
1240 | int err = find_sys_file(self, sec, &sec->spaninfo, spaninfo_name); | ||
1241 | if (err) return MSPACK_ERR_DATAFORMAT; | ||
1242 | |||
1243 | /* check it's large enough */ | ||
1244 | if (sec->spaninfo->length != 8) { | ||
1245 | D(("SpanInfo file is wrong size")) | ||
1246 | return MSPACK_ERR_DATAFORMAT; | ||
1247 | } | ||
1248 | |||
1249 | /* read the SpanInfo file */ | ||
1250 | if (!(data = read_sys_file(self, sec->spaninfo))) { | ||
1251 | D(("can't read SpanInfo file")) | ||
1252 | return self->error; | ||
1253 | } | ||
1254 | |||
1255 | /* get the uncompressed length of the LZX stream */ | ||
1256 | err = read_off64(length_ptr, data, sys, self->d->infh); | ||
1257 | sys->free(data); | ||
1258 | if (err) return MSPACK_ERR_DATAFORMAT; | ||
1259 | |||
1260 | if (*length_ptr <= 0) { | ||
1261 | D(("output length is invalid")) | ||
1262 | return MSPACK_ERR_DATAFORMAT; | ||
1263 | } | ||
1264 | |||
1265 | return MSPACK_ERR_OK; | ||
1266 | } | ||
1267 | |||
1268 | /*************************************** | ||
1269 | * FIND_SYS_FILE | ||
1270 | *************************************** | ||
1271 | * Uses chmd_fast_find to locate a system file, and fills out that system | ||
1272 | * file's entry and links it into the list of system files. Returns zero | ||
1273 | * for success, non-zero for both failure and the file not existing. | ||
1274 | */ | ||
1275 | static int find_sys_file(struct mschm_decompressor_p *self, | ||
1276 | struct mschmd_sec_mscompressed *sec, | ||
1277 | struct mschmd_file **f_ptr, const char *name) | ||
1278 | { | ||
1279 | struct mspack_system *sys = self->system; | ||
1280 | struct mschmd_file result; | ||
1281 | |||
1282 | /* already loaded */ | ||
1283 | if (*f_ptr) return MSPACK_ERR_OK; | ||
1284 | |||
1285 | /* try using fast_find to find the file - return DATAFORMAT error if | ||
1286 | * it fails, or successfully doesn't find the file */ | ||
1287 | if (chmd_fast_find((struct mschm_decompressor *) self, sec->base.chm, | ||
1288 | name, &result, (int)sizeof(result)) || !result.section) | ||
1289 | { | ||
1290 | return MSPACK_ERR_DATAFORMAT; | ||
1291 | } | ||
1292 | |||
1293 | if (!(*f_ptr = (struct mschmd_file *) sys->alloc(sys, sizeof(result)))) { | ||
1294 | return MSPACK_ERR_NOMEMORY; | ||
1295 | } | ||
1296 | |||
1297 | /* copy result */ | ||
1298 | *(*f_ptr) = result; | ||
1299 | (*f_ptr)->filename = (char *) name; | ||
1300 | |||
1301 | /* link file into sysfiles list */ | ||
1302 | (*f_ptr)->next = sec->base.chm->sysfiles; | ||
1303 | sec->base.chm->sysfiles = *f_ptr; | ||
1304 | return MSPACK_ERR_OK; | ||
1305 | } | ||
1306 | |||
1307 | /*************************************** | ||
1308 | * READ_SYS_FILE | ||
1309 | *************************************** | ||
1310 | * Allocates memory for a section 0 (uncompressed) file and reads it into | ||
1311 | * memory. | ||
1312 | */ | ||
1313 | static unsigned char *read_sys_file(struct mschm_decompressor_p *self, | ||
1314 | struct mschmd_file *file) | ||
1315 | { | ||
1316 | struct mspack_system *sys = self->system; | ||
1317 | unsigned char *data = NULL; | ||
1318 | int len; | ||
1319 | |||
1320 | if (!file || !file->section || (file->section->id != 0)) { | ||
1321 | self->error = MSPACK_ERR_DATAFORMAT; | ||
1322 | return NULL; | ||
1323 | } | ||
1324 | |||
1325 | len = (int) file->length; | ||
1326 | |||
1327 | if (!(data = (unsigned char *) sys->alloc(sys, (size_t) len))) { | ||
1328 | self->error = MSPACK_ERR_NOMEMORY; | ||
1329 | return NULL; | ||
1330 | } | ||
1331 | if (sys->seek(self->d->infh, file->section->chm->sec0.offset | ||
1332 | + file->offset, MSPACK_SYS_SEEK_START)) | ||
1333 | { | ||
1334 | self->error = MSPACK_ERR_SEEK; | ||
1335 | sys->free(data); | ||
1336 | return NULL; | ||
1337 | } | ||
1338 | if (sys->read(self->d->infh, data, len) != len) { | ||
1339 | self->error = MSPACK_ERR_READ; | ||
1340 | sys->free(data); | ||
1341 | return NULL; | ||
1342 | } | ||
1343 | return data; | ||
1344 | } | ||
1345 | |||
1346 | /*************************************** | ||
1347 | * CHMD_ERROR | ||
1348 | *************************************** | ||
1349 | * returns the last error that occurred | ||
1350 | */ | ||
1351 | static int chmd_error(struct mschm_decompressor *base) { | ||
1352 | struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; | ||
1353 | return (self) ? self->error : MSPACK_ERR_ARGS; | ||
1354 | } | ||
1355 | |||
1356 | /*************************************** | ||
1357 | * READ_OFF64 | ||
1358 | *************************************** | ||
1359 | * Reads a 64-bit signed integer from memory in Intel byte order. | ||
1360 | * If running on a system with a 64-bit off_t, this is simply done. | ||
1361 | * If running on a system with a 32-bit off_t, offsets up to 0x7FFFFFFF | ||
1362 | * are accepted, offsets beyond that cause an error message. | ||
1363 | */ | ||
1364 | static int read_off64(off_t *var, unsigned char *mem, | ||
1365 | struct mspack_system *sys, struct mspack_file *fh) | ||
1366 | { | ||
1367 | #if LARGEFILE_SUPPORT | ||
1368 | *var = EndGetI64(mem); | ||
1369 | #else | ||
1370 | *var = EndGetI32(mem); | ||
1371 | if ((*var & 0x80000000) || EndGetI32(mem+4)) { | ||
1372 | sys->message(fh, (char *)largefile_msg); | ||
1373 | return 1; | ||
1374 | } | ||
1375 | #endif | ||
1376 | return 0; | ||
1377 | } | ||
diff --git a/utils/rbutilqt/mspack/des.h b/utils/rbutilqt/mspack/des.h new file mode 100644 index 0000000000..64a1ed27e4 --- /dev/null +++ b/utils/rbutilqt/mspack/des.h | |||
@@ -0,0 +1,15 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifndef MSPACK_DES_H | ||
11 | #define MSPACK_DES_H 1 | ||
12 | |||
13 | /* DES encryption / decryption definitions */ | ||
14 | |||
15 | #endif | ||
diff --git a/utils/rbutilqt/mspack/hlp.h b/utils/rbutilqt/mspack/hlp.h new file mode 100644 index 0000000000..a6e3abc7ff --- /dev/null +++ b/utils/rbutilqt/mspack/hlp.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifndef MSPACK_HLP_H | ||
11 | #define MSPACK_HLP_H 1 | ||
12 | |||
13 | #include "lzss.h" | ||
14 | |||
15 | /* generic HLP definitions */ | ||
16 | |||
17 | /* HLP compression definitions */ | ||
18 | |||
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/utils/rbutilqt/mspack/hlpc.c b/utils/rbutilqt/mspack/hlpc.c new file mode 100644 index 0000000000..b33e499f65 --- /dev/null +++ b/utils/rbutilqt/mspack/hlpc.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | /* HLP compression implementation */ | ||
11 | |||
12 | #include "system-mspack.h" | ||
13 | #include "hlp.h" | ||
14 | |||
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/utils/rbutilqt/mspack/hlpd.c b/utils/rbutilqt/mspack/hlpd.c new file mode 100644 index 0000000000..ae1f2efba8 --- /dev/null +++ b/utils/rbutilqt/mspack/hlpd.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | /* HLP decompression implementation */ | ||
11 | |||
12 | #include "system-mspack.h" | ||
13 | #include "hlp.h" | ||
14 | |||
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/utils/rbutilqt/mspack/kwaj.h b/utils/rbutilqt/mspack/kwaj.h new file mode 100644 index 0000000000..75425d958a --- /dev/null +++ b/utils/rbutilqt/mspack/kwaj.h | |||
@@ -0,0 +1,118 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2010 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifndef MSPACK_KWAJ_H | ||
11 | #define MSPACK_KWAJ_H 1 | ||
12 | |||
13 | #include "lzss.h" | ||
14 | |||
15 | /* generic KWAJ definitions */ | ||
16 | #define kwajh_Signature1 (0x00) | ||
17 | #define kwajh_Signature2 (0x04) | ||
18 | #define kwajh_CompMethod (0x08) | ||
19 | #define kwajh_DataOffset (0x0a) | ||
20 | #define kwajh_Flags (0x0c) | ||
21 | #define kwajh_SIZEOF (0x0e) | ||
22 | |||
23 | /* KWAJ compression definitions */ | ||
24 | |||
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/utils/rbutilqt/mspack/kwajc.c b/utils/rbutilqt/mspack/kwajc.c new file mode 100644 index 0000000000..52ca29432a --- /dev/null +++ b/utils/rbutilqt/mspack/kwajc.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | /* KWAJ compression implementation */ | ||
11 | |||
12 | #include "system-mspack.h" | ||
13 | #include "kwaj.h" | ||
14 | |||
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/utils/rbutilqt/mspack/kwajd.c b/utils/rbutilqt/mspack/kwajd.c new file mode 100644 index 0000000000..24e0b0613b --- /dev/null +++ b/utils/rbutilqt/mspack/kwajd.c | |||
@@ -0,0 +1,566 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2011 Stuart Caie. | ||
3 | * | ||
4 | * KWAJ is a format very similar to SZDD. KWAJ method 3 (LZH) was | ||
5 | * written by Jeff Johnson. | ||
6 | * | ||
7 | * libmspack is free software; you can redistribute it and/or modify it under | ||
8 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
9 | * | ||
10 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
11 | */ | ||
12 | |||
13 | /* KWAJ decompression implementation */ | ||
14 | |||
15 | #include "system-mspack.h" | ||
16 | #include "kwaj.h" | ||
17 | #include "mszip.h" | ||
18 | |||
19 | /* prototypes */ | ||
20 | static struct mskwajd_header *kwajd_open( | ||
21 | struct mskwaj_decompressor *base, const char *filename); | ||
22 | static void kwajd_close( | ||
23 | struct mskwaj_decompressor *base, struct mskwajd_header *hdr); | ||
24 | static int kwajd_read_headers( | ||
25 | struct mspack_system *sys, struct mspack_file *fh, | ||
26 | struct mskwajd_header *hdr); | ||
27 | static int kwajd_extract( | ||
28 | struct mskwaj_decompressor *base, struct mskwajd_header *hdr, | ||
29 | const char *filename); | ||
30 | static int kwajd_decompress( | ||
31 | struct mskwaj_decompressor *base, const char *input, const char *output); | ||
32 | static int kwajd_error( | ||
33 | struct mskwaj_decompressor *base); | ||
34 | |||
35 | static struct kwajd_stream *lzh_init( | ||
36 | struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out); | ||
37 | static int lzh_decompress( | ||
38 | struct kwajd_stream *kwaj); | ||
39 | static void lzh_free( | ||
40 | struct kwajd_stream *kwaj); | ||
41 | static int lzh_read_lens( | ||
42 | struct kwajd_stream *kwaj, | ||
43 | unsigned int type, unsigned int numsyms, | ||
44 | unsigned char *lens); | ||
45 | static int lzh_read_input( | ||
46 | struct kwajd_stream *kwaj); | ||
47 | |||
48 | |||
49 | /*************************************** | ||
50 | * MSPACK_CREATE_KWAJ_DECOMPRESSOR | ||
51 | *************************************** | ||
52 | * constructor | ||
53 | */ | ||
54 | struct mskwaj_decompressor * | ||
55 | mspack_create_kwaj_decompressor(struct mspack_system *sys) | ||
56 | { | ||
57 | struct mskwaj_decompressor_p *self = NULL; | ||
58 | |||
59 | if (!sys) sys = mspack_default_system; | ||
60 | if (!mspack_valid_system(sys)) return NULL; | ||
61 | |||
62 | if ((self = (struct mskwaj_decompressor_p *) sys->alloc(sys, sizeof(struct mskwaj_decompressor_p)))) { | ||
63 | self->base.open = &kwajd_open; | ||
64 | self->base.close = &kwajd_close; | ||
65 | self->base.extract = &kwajd_extract; | ||
66 | self->base.decompress = &kwajd_decompress; | ||
67 | self->base.last_error = &kwajd_error; | ||
68 | self->system = sys; | ||
69 | self->error = MSPACK_ERR_OK; | ||
70 | } | ||
71 | return (struct mskwaj_decompressor *) self; | ||
72 | } | ||
73 | |||
74 | /*************************************** | ||
75 | * MSPACK_DESTROY_KWAJ_DECOMPRESSOR | ||
76 | *************************************** | ||
77 | * destructor | ||
78 | */ | ||
79 | void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *base) | ||
80 | { | ||
81 | struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; | ||
82 | if (self) { | ||
83 | struct mspack_system *sys = self->system; | ||
84 | sys->free(self); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | /*************************************** | ||
89 | * KWAJD_OPEN | ||
90 | *************************************** | ||
91 | * opens a KWAJ file without decompressing, reads header | ||
92 | */ | ||
93 | static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base, | ||
94 | const char *filename) | ||
95 | { | ||
96 | struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; | ||
97 | struct mskwajd_header *hdr; | ||
98 | struct mspack_system *sys; | ||
99 | struct mspack_file *fh; | ||
100 | |||
101 | if (!self) return NULL; | ||
102 | sys = self->system; | ||
103 | |||
104 | fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ); | ||
105 | hdr = (struct mskwajd_header *) sys->alloc(sys, sizeof(struct mskwajd_header_p)); | ||
106 | if (fh && hdr) { | ||
107 | ((struct mskwajd_header_p *) hdr)->fh = fh; | ||
108 | self->error = kwajd_read_headers(sys, fh, hdr); | ||
109 | } | ||
110 | else { | ||
111 | if (!fh) self->error = MSPACK_ERR_OPEN; | ||
112 | if (!hdr) self->error = MSPACK_ERR_NOMEMORY; | ||
113 | } | ||
114 | |||
115 | if (self->error) { | ||
116 | if (fh) sys->close(fh); | ||
117 | sys->free(hdr); | ||
118 | hdr = NULL; | ||
119 | } | ||
120 | |||
121 | return hdr; | ||
122 | } | ||
123 | |||
124 | /*************************************** | ||
125 | * KWAJD_CLOSE | ||
126 | *************************************** | ||
127 | * closes a KWAJ file | ||
128 | */ | ||
129 | static void kwajd_close(struct mskwaj_decompressor *base, | ||
130 | struct mskwajd_header *hdr) | ||
131 | { | ||
132 | struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; | ||
133 | struct mskwajd_header_p *hdr_p = (struct mskwajd_header_p *) hdr; | ||
134 | |||
135 | if (!self || !self->system) return; | ||
136 | |||
137 | /* close the file handle associated */ | ||
138 | self->system->close(hdr_p->fh); | ||
139 | |||
140 | /* free the memory associated */ | ||
141 | self->system->free(hdr); | ||
142 | |||
143 | self->error = MSPACK_ERR_OK; | ||
144 | } | ||
145 | |||
146 | /*************************************** | ||
147 | * KWAJD_READ_HEADERS | ||
148 | *************************************** | ||
149 | * reads the headers of a KWAJ format file | ||
150 | */ | ||
151 | static int kwajd_read_headers(struct mspack_system *sys, | ||
152 | struct mspack_file *fh, | ||
153 | struct mskwajd_header *hdr) | ||
154 | { | ||
155 | unsigned char buf[16]; | ||
156 | int i; | ||
157 | |||
158 | /* read in the header */ | ||
159 | if (sys->read(fh, &buf[0], kwajh_SIZEOF) != kwajh_SIZEOF) { | ||
160 | return MSPACK_ERR_READ; | ||
161 | } | ||
162 | |||
163 | /* check for "KWAJ" signature */ | ||
164 | if (((unsigned int) EndGetI32(&buf[kwajh_Signature1]) != 0x4A41574B) || | ||
165 | ((unsigned int) EndGetI32(&buf[kwajh_Signature2]) != 0xD127F088)) | ||
166 | { | ||
167 | return MSPACK_ERR_SIGNATURE; | ||
168 | } | ||
169 | |||
170 | /* basic header fields */ | ||
171 | hdr->comp_type = EndGetI16(&buf[kwajh_CompMethod]); | ||
172 | hdr->data_offset = EndGetI16(&buf[kwajh_DataOffset]); | ||
173 | hdr->headers = EndGetI16(&buf[kwajh_Flags]); | ||
174 | hdr->length = 0; | ||
175 | hdr->filename = NULL; | ||
176 | hdr->extra = NULL; | ||
177 | hdr->extra_length = 0; | ||
178 | |||
179 | /* optional headers */ | ||
180 | |||
181 | /* 4 bytes: length of unpacked file */ | ||
182 | if (hdr->headers & MSKWAJ_HDR_HASLENGTH) { | ||
183 | if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ; | ||
184 | hdr->length = EndGetI32(&buf[0]); | ||
185 | } | ||
186 | |||
187 | /* 2 bytes: unknown purpose */ | ||
188 | if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN1) { | ||
189 | if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; | ||
190 | } | ||
191 | |||
192 | /* 2 bytes: length of section, then [length] bytes: unknown purpose */ | ||
193 | if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN2) { | ||
194 | if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; | ||
195 | i = EndGetI16(&buf[0]); | ||
196 | if (sys->seek(fh, (off_t)i, MSPACK_SYS_SEEK_CUR)) return MSPACK_ERR_SEEK; | ||
197 | } | ||
198 | |||
199 | /* filename and extension */ | ||
200 | if (hdr->headers & (MSKWAJ_HDR_HASFILENAME | MSKWAJ_HDR_HASFILEEXT)) { | ||
201 | int len; | ||
202 | /* allocate memory for maximum length filename */ | ||
203 | char *fn = (char *) sys->alloc(sys, (size_t) 13); | ||
204 | if (!(hdr->filename = fn)) return MSPACK_ERR_NOMEMORY; | ||
205 | |||
206 | /* copy filename if present */ | ||
207 | if (hdr->headers & MSKWAJ_HDR_HASFILENAME) { | ||
208 | /* read and copy up to 9 bytes of a null terminated string */ | ||
209 | if ((len = sys->read(fh, &buf[0], 9)) < 2) return MSPACK_ERR_READ; | ||
210 | for (i = 0; i < len; i++) if (!(*fn++ = buf[i])) break; | ||
211 | /* if string was 9 bytes with no null terminator, reject it */ | ||
212 | if (i == 9 && buf[8] != '\0') return MSPACK_ERR_DATAFORMAT; | ||
213 | /* seek to byte after string ended in file */ | ||
214 | if (sys->seek(fh, (off_t)(i + 1 - len), MSPACK_SYS_SEEK_CUR)) | ||
215 | return MSPACK_ERR_SEEK; | ||
216 | fn--; /* remove the null terminator */ | ||
217 | } | ||
218 | |||
219 | /* copy extension if present */ | ||
220 | if (hdr->headers & MSKWAJ_HDR_HASFILEEXT) { | ||
221 | *fn++ = '.'; | ||
222 | /* read and copy up to 4 bytes of a null terminated string */ | ||
223 | if ((len = sys->read(fh, &buf[0], 4)) < 2) return MSPACK_ERR_READ; | ||
224 | for (i = 0; i < len; i++) if (!(*fn++ = buf[i])) break; | ||
225 | /* if string was 4 bytes with no null terminator, reject it */ | ||
226 | if (i == 4 && buf[3] != '\0') return MSPACK_ERR_DATAFORMAT; | ||
227 | /* seek to byte after string ended in file */ | ||
228 | if (sys->seek(fh, (off_t)(i + 1 - len), MSPACK_SYS_SEEK_CUR)) | ||
229 | return MSPACK_ERR_SEEK; | ||
230 | fn--; /* remove the null terminator */ | ||
231 | } | ||
232 | *fn = '\0'; | ||
233 | } | ||
234 | |||
235 | /* 2 bytes: extra text length then [length] bytes of extra text data */ | ||
236 | if (hdr->headers & MSKWAJ_HDR_HASEXTRATEXT) { | ||
237 | if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; | ||
238 | i = EndGetI16(&buf[0]); | ||
239 | hdr->extra = (char *) sys->alloc(sys, (size_t)i+1); | ||
240 | if (! hdr->extra) return MSPACK_ERR_NOMEMORY; | ||
241 | if (sys->read(fh, hdr->extra, i) != i) return MSPACK_ERR_READ; | ||
242 | hdr->extra[i] = '\0'; | ||
243 | hdr->extra_length = i; | ||
244 | } | ||
245 | return MSPACK_ERR_OK; | ||
246 | } | ||
247 | |||
248 | /*************************************** | ||
249 | * KWAJD_EXTRACT | ||
250 | *************************************** | ||
251 | * decompresses a KWAJ file | ||
252 | */ | ||
253 | static int kwajd_extract(struct mskwaj_decompressor *base, | ||
254 | struct mskwajd_header *hdr, const char *filename) | ||
255 | { | ||
256 | struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; | ||
257 | struct mspack_system *sys; | ||
258 | struct mspack_file *fh, *outfh; | ||
259 | |||
260 | if (!self) return MSPACK_ERR_ARGS; | ||
261 | if (!hdr) return self->error = MSPACK_ERR_ARGS; | ||
262 | |||
263 | sys = self->system; | ||
264 | fh = ((struct mskwajd_header_p *) hdr)->fh; | ||
265 | |||
266 | /* seek to the compressed data */ | ||
267 | if (sys->seek(fh, hdr->data_offset, MSPACK_SYS_SEEK_START)) { | ||
268 | return self->error = MSPACK_ERR_SEEK; | ||
269 | } | ||
270 | |||
271 | /* open file for output */ | ||
272 | if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { | ||
273 | return self->error = MSPACK_ERR_OPEN; | ||
274 | } | ||
275 | |||
276 | self->error = MSPACK_ERR_OK; | ||
277 | |||
278 | /* decompress based on format */ | ||
279 | if (hdr->comp_type == MSKWAJ_COMP_NONE || | ||
280 | hdr->comp_type == MSKWAJ_COMP_XOR) | ||
281 | { | ||
282 | /* NONE is a straight copy. XOR is a copy xored with 0xFF */ | ||
283 | unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) KWAJ_INPUT_SIZE); | ||
284 | if (buf) { | ||
285 | int read, i; | ||
286 | while ((read = sys->read(fh, buf, KWAJ_INPUT_SIZE)) > 0) { | ||
287 | if (hdr->comp_type == MSKWAJ_COMP_XOR) { | ||
288 | for (i = 0; i < read; i++) buf[i] ^= 0xFF; | ||
289 | } | ||
290 | if (sys->write(outfh, buf, read) != read) { | ||
291 | self->error = MSPACK_ERR_WRITE; | ||
292 | break; | ||
293 | } | ||
294 | } | ||
295 | if (read < 0) self->error = MSPACK_ERR_READ; | ||
296 | sys->free(buf); | ||
297 | } | ||
298 | else { | ||
299 | self->error = MSPACK_ERR_NOMEMORY; | ||
300 | } | ||
301 | } | ||
302 | else if (hdr->comp_type == MSKWAJ_COMP_SZDD) { | ||
303 | self->error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE, | ||
304 | LZSS_MODE_EXPAND); | ||
305 | } | ||
306 | else if (hdr->comp_type == MSKWAJ_COMP_LZH) { | ||
307 | struct kwajd_stream *lzh = lzh_init(sys, fh, outfh); | ||
308 | self->error = (lzh) ? lzh_decompress(lzh) : MSPACK_ERR_NOMEMORY; | ||
309 | lzh_free(lzh); | ||
310 | } | ||
311 | else if (hdr->comp_type == MSKWAJ_COMP_MSZIP) { | ||
312 | struct mszipd_stream *zip = mszipd_init(sys,fh,outfh,KWAJ_INPUT_SIZE,0); | ||
313 | self->error = (zip) ? mszipd_decompress_kwaj(zip) : MSPACK_ERR_NOMEMORY; | ||
314 | mszipd_free(zip); | ||
315 | } | ||
316 | else { | ||
317 | self->error = MSPACK_ERR_DATAFORMAT; | ||
318 | } | ||
319 | |||
320 | /* close output file */ | ||
321 | sys->close(outfh); | ||
322 | |||
323 | return self->error; | ||
324 | } | ||
325 | |||
326 | /*************************************** | ||
327 | * KWAJD_DECOMPRESS | ||
328 | *************************************** | ||
329 | * unpacks directly from input to output | ||
330 | */ | ||
331 | static int kwajd_decompress(struct mskwaj_decompressor *base, | ||
332 | const char *input, const char *output) | ||
333 | { | ||
334 | struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; | ||
335 | struct mskwajd_header *hdr; | ||
336 | int error; | ||
337 | |||
338 | if (!self) return MSPACK_ERR_ARGS; | ||
339 | |||
340 | if (!(hdr = kwajd_open(base, input))) return self->error; | ||
341 | error = kwajd_extract(base, hdr, output); | ||
342 | kwajd_close(base, hdr); | ||
343 | return self->error = error; | ||
344 | } | ||
345 | |||
346 | /*************************************** | ||
347 | * KWAJD_ERROR | ||
348 | *************************************** | ||
349 | * returns the last error that occurred | ||
350 | */ | ||
351 | static int kwajd_error(struct mskwaj_decompressor *base) | ||
352 | { | ||
353 | struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; | ||
354 | return (self) ? self->error : MSPACK_ERR_ARGS; | ||
355 | } | ||
356 | |||
357 | /*************************************** | ||
358 | * LZH_INIT, LZH_DECOMPRESS, LZH_FREE | ||
359 | *************************************** | ||
360 | * unpacks KWAJ method 3 files | ||
361 | */ | ||
362 | |||
363 | /* import bit-reading macros and code */ | ||
364 | #define BITS_TYPE struct kwajd_stream | ||
365 | #define BITS_VAR lzh | ||
366 | #define BITS_ORDER_MSB | ||
367 | #define BITS_NO_READ_INPUT | ||
368 | #define READ_BYTES do { \ | ||
369 | if (i_ptr >= i_end) { \ | ||
370 | if ((err = lzh_read_input(lzh))) return err; \ | ||
371 | i_ptr = lzh->i_ptr; \ | ||
372 | i_end = lzh->i_end; \ | ||
373 | } \ | ||
374 | INJECT_BITS(*i_ptr++, 8); \ | ||
375 | } while (0) | ||
376 | #include <readbits.h> | ||
377 | |||
378 | /* import huffman-reading macros and code */ | ||
379 | #define TABLEBITS(tbl) KWAJ_TABLEBITS | ||
380 | #define MAXSYMBOLS(tbl) KWAJ_##tbl##_SYMS | ||
381 | #define HUFF_TABLE(tbl,idx) lzh->tbl##_table[idx] | ||
382 | #define HUFF_LEN(tbl,idx) lzh->tbl##_len[idx] | ||
383 | #define HUFF_ERROR return MSPACK_ERR_DATAFORMAT | ||
384 | #include <readhuff.h> | ||
385 | |||
386 | /* In the KWAJ LZH format, there is no special 'eof' marker, it just | ||
387 | * ends. Depending on how many bits are left in the final byte when | ||
388 | * the stream ends, that might be enough to start another literal or | ||
389 | * match. The only easy way to detect that we've come to an end is to | ||
390 | * guard all bit-reading. We allow fake bits to be read once we reach | ||
391 | * the end of the stream, but we check if we then consumed any of | ||
392 | * those fake bits, after doing the READ_BITS / READ_HUFFSYM. This | ||
393 | * isn't how the default readbits.h read_input() works (it simply lets | ||
394 | * 2 fake bytes in then stops), so we implement our own. | ||
395 | */ | ||
396 | #define READ_BITS_SAFE(val, n) do { \ | ||
397 | READ_BITS(val, n); \ | ||
398 | if (lzh->input_end && bits_left < lzh->input_end) \ | ||
399 | return MSPACK_ERR_OK; \ | ||
400 | } while (0) | ||
401 | |||
402 | #define READ_HUFFSYM_SAFE(tbl, val) do { \ | ||
403 | READ_HUFFSYM(tbl, val); \ | ||
404 | if (lzh->input_end && bits_left < lzh->input_end) \ | ||
405 | return MSPACK_ERR_OK; \ | ||
406 | } while (0) | ||
407 | |||
408 | #define BUILD_TREE(tbl, type) \ | ||
409 | STORE_BITS; \ | ||
410 | err = lzh_read_lens(lzh, type, MAXSYMBOLS(tbl), &HUFF_LEN(tbl,0)); \ | ||
411 | if (err) return err; \ | ||
412 | RESTORE_BITS; \ | ||
413 | if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ | ||
414 | &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ | ||
415 | return MSPACK_ERR_DATAFORMAT; | ||
416 | |||
417 | #define WRITE_BYTE do { \ | ||
418 | if (lzh->sys->write(lzh->output, &lzh->window[pos], 1) != 1) \ | ||
419 | return MSPACK_ERR_WRITE; \ | ||
420 | } while (0) | ||
421 | |||
422 | static struct kwajd_stream *lzh_init(struct mspack_system *sys, | ||
423 | struct mspack_file *in, struct mspack_file *out) | ||
424 | { | ||
425 | struct kwajd_stream *lzh; | ||
426 | |||
427 | if (!sys || !in || !out) return NULL; | ||
428 | if (!(lzh = (struct kwajd_stream *) sys->alloc(sys, sizeof(struct kwajd_stream)))) return NULL; | ||
429 | |||
430 | lzh->sys = sys; | ||
431 | lzh->input = in; | ||
432 | lzh->output = out; | ||
433 | return lzh; | ||
434 | } | ||
435 | |||
436 | static int lzh_decompress(struct kwajd_stream *lzh) | ||
437 | { | ||
438 | register unsigned int bit_buffer; | ||
439 | register int bits_left, i; | ||
440 | register unsigned short sym; | ||
441 | unsigned char *i_ptr, *i_end, lit_run = 0; | ||
442 | int j, pos = 0, len, offset, err; | ||
443 | unsigned int types[6]; | ||
444 | |||
445 | /* reset global state */ | ||
446 | INIT_BITS; | ||
447 | RESTORE_BITS; | ||
448 | memset(&lzh->window[0], LZSS_WINDOW_FILL, (size_t) LZSS_WINDOW_SIZE); | ||
449 | |||
450 | /* read 6 encoding types (for byte alignment) but only 5 are needed */ | ||
451 | for (i = 0; i < 6; i++) READ_BITS_SAFE(types[i], 4); | ||
452 | |||
453 | /* read huffman table symbol lengths and build huffman trees */ | ||
454 | BUILD_TREE(MATCHLEN1, types[0]); | ||
455 | BUILD_TREE(MATCHLEN2, types[1]); | ||
456 | BUILD_TREE(LITLEN, types[2]); | ||
457 | BUILD_TREE(OFFSET, types[3]); | ||
458 | BUILD_TREE(LITERAL, types[4]); | ||
459 | |||
460 | while (!lzh->input_end) { | ||
461 | if (lit_run) READ_HUFFSYM_SAFE(MATCHLEN2, len); | ||
462 | else READ_HUFFSYM_SAFE(MATCHLEN1, len); | ||
463 | |||
464 | if (len > 0) { | ||
465 | len += 2; | ||
466 | lit_run = 0; /* not the end of a literal run */ | ||
467 | READ_HUFFSYM_SAFE(OFFSET, j); offset = j << 6; | ||
468 | READ_BITS_SAFE(j, 6); offset |= j; | ||
469 | |||
470 | /* copy match as output and into the ring buffer */ | ||
471 | while (len-- > 0) { | ||
472 | lzh->window[pos] = lzh->window[(pos+4096-offset) & 4095]; | ||
473 | WRITE_BYTE; | ||
474 | pos++; pos &= 4095; | ||
475 | } | ||
476 | } | ||
477 | else { | ||
478 | READ_HUFFSYM_SAFE(LITLEN, len); len++; | ||
479 | lit_run = (len == 32) ? 0 : 1; /* end of a literal run? */ | ||
480 | while (len-- > 0) { | ||
481 | READ_HUFFSYM_SAFE(LITERAL, j); | ||
482 | /* copy as output and into the ring buffer */ | ||
483 | lzh->window[pos] = j; | ||
484 | WRITE_BYTE; | ||
485 | pos++; pos &= 4095; | ||
486 | } | ||
487 | } | ||
488 | } | ||
489 | return MSPACK_ERR_OK; | ||
490 | } | ||
491 | |||
492 | static void lzh_free(struct kwajd_stream *lzh) | ||
493 | { | ||
494 | struct mspack_system *sys; | ||
495 | if (!lzh || !lzh->sys) return; | ||
496 | sys = lzh->sys; | ||
497 | sys->free(lzh); | ||
498 | } | ||
499 | |||
500 | static int lzh_read_lens(struct kwajd_stream *lzh, | ||
501 | unsigned int type, unsigned int numsyms, | ||
502 | unsigned char *lens) | ||
503 | { | ||
504 | register unsigned int bit_buffer; | ||
505 | register int bits_left; | ||
506 | unsigned char *i_ptr, *i_end; | ||
507 | unsigned int i, c, sel; | ||
508 | int err; | ||
509 | |||
510 | RESTORE_BITS; | ||
511 | switch (type) { | ||
512 | case 0: | ||
513 | i = numsyms; c = (i==16)?4: (i==32)?5: (i==64)?6: (i==256)?8 :0; | ||
514 | for (i = 0; i < numsyms; i++) lens[i] = c; | ||
515 | break; | ||
516 | |||
517 | case 1: | ||
518 | READ_BITS_SAFE(c, 4); lens[0] = c; | ||
519 | for (i = 1; i < numsyms; i++) { | ||
520 | READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = c; | ||
521 | else { READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = ++c; | ||
522 | else { READ_BITS_SAFE(c, 4); lens[i] = c; }} | ||
523 | } | ||
524 | break; | ||
525 | |||
526 | case 2: | ||
527 | READ_BITS_SAFE(c, 4); lens[0] = c; | ||
528 | for (i = 1; i < numsyms; i++) { | ||
529 | READ_BITS_SAFE(sel, 2); | ||
530 | if (sel == 3) READ_BITS_SAFE(c, 4); else c += (char) sel-1; | ||
531 | lens[i] = c; | ||
532 | } | ||
533 | break; | ||
534 | |||
535 | case 3: | ||
536 | for (i = 0; i < numsyms; i++) { | ||
537 | READ_BITS_SAFE(c, 4); lens[i] = c; | ||
538 | } | ||
539 | break; | ||
540 | } | ||
541 | STORE_BITS; | ||
542 | return MSPACK_ERR_OK; | ||
543 | } | ||
544 | |||
545 | static int lzh_read_input(struct kwajd_stream *lzh) { | ||
546 | int read; | ||
547 | if (lzh->input_end) { | ||
548 | lzh->input_end += 8; | ||
549 | lzh->inbuf[0] = 0; | ||
550 | read = 1; | ||
551 | } | ||
552 | else { | ||
553 | read = lzh->sys->read(lzh->input, &lzh->inbuf[0], KWAJ_INPUT_SIZE); | ||
554 | if (read < 0) return MSPACK_ERR_READ; | ||
555 | if (read == 0) { | ||
556 | lzh->input_end = 8; | ||
557 | lzh->inbuf[0] = 0; | ||
558 | read = 1; | ||
559 | } | ||
560 | } | ||
561 | |||
562 | /* update i_ptr and i_end */ | ||
563 | lzh->i_ptr = &lzh->inbuf[0]; | ||
564 | lzh->i_end = &lzh->inbuf[read]; | ||
565 | return MSPACK_ERR_OK; | ||
566 | } | ||
diff --git a/utils/rbutilqt/mspack/lit.h b/utils/rbutilqt/mspack/lit.h new file mode 100644 index 0000000000..2ccc7dd7f3 --- /dev/null +++ b/utils/rbutilqt/mspack/lit.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifndef MSPACK_LIT_H | ||
11 | #define MSPACK_LIT_H 1 | ||
12 | |||
13 | #include "lzx.h" | ||
14 | #include "des.h" | ||
15 | #include "sha.h" | ||
16 | |||
17 | /* generic LIT definitions */ | ||
18 | |||
19 | /* LIT compression definitions */ | ||
20 | |||
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/utils/rbutilqt/mspack/litc.c b/utils/rbutilqt/mspack/litc.c new file mode 100644 index 0000000000..4959a81d36 --- /dev/null +++ b/utils/rbutilqt/mspack/litc.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | /* LIT compression implementation */ | ||
11 | |||
12 | #include "system-mspack.h" | ||
13 | #include "lit.h" | ||
14 | |||
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/utils/rbutilqt/mspack/litd.c b/utils/rbutilqt/mspack/litd.c new file mode 100644 index 0000000000..93cbf1765a --- /dev/null +++ b/utils/rbutilqt/mspack/litd.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | /* LIT decompression implementation */ | ||
11 | |||
12 | #include "system-mspack.h" | ||
13 | #include "lit.h" | ||
14 | |||
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/utils/rbutilqt/mspack/lzss.h b/utils/rbutilqt/mspack/lzss.h new file mode 100644 index 0000000000..aa946e52ae --- /dev/null +++ b/utils/rbutilqt/mspack/lzss.h | |||
@@ -0,0 +1,66 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifndef MSPACK_LZSS_H | ||
11 | #define MSPACK_LZSS_H 1 | ||
12 | |||
13 | #ifdef __cplusplus | ||
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/utils/rbutilqt/mspack/lzssd.c b/utils/rbutilqt/mspack/lzssd.c new file mode 100644 index 0000000000..63716d414a --- /dev/null +++ b/utils/rbutilqt/mspack/lzssd.c | |||
@@ -0,0 +1,93 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2010 Stuart Caie. | ||
3 | * | ||
4 | * LZSS is a derivative of LZ77 and was created by James Storer and | ||
5 | * Thomas Szymanski in 1982. Haruhiko Okumura wrote a very popular C | ||
6 | * implementation. | ||
7 | * | ||
8 | * libmspack is free software; you can redistribute it and/or modify it under | ||
9 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
10 | * | ||
11 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
12 | */ | ||
13 | |||
14 | #include "system-mspack.h" | ||
15 | #include "lzss.h" | ||
16 | |||
17 | #define ENSURE_BYTES do { \ | ||
18 | if (i_ptr >= i_end) { \ | ||
19 | read = system->read(input, &inbuf[0], \ | ||
20 | input_buffer_size); \ | ||
21 | if (read <= 0) { \ | ||
22 | system->free(window); \ | ||
23 | return (read < 0) ? MSPACK_ERR_READ \ | ||
24 | : MSPACK_ERR_OK; \ | ||
25 | } \ | ||
26 | i_ptr = &inbuf[0]; i_end = &inbuf[read]; \ | ||
27 | } \ | ||
28 | } while (0) | ||
29 | |||
30 | #define WRITE_BYTE do { \ | ||
31 | if (system->write(output, &window[pos], 1) != 1) { \ | ||
32 | system->free(window); \ | ||
33 | return MSPACK_ERR_WRITE; \ | ||
34 | } \ | ||
35 | } while (0) | ||
36 | |||
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/utils/rbutilqt/mspack/lzx.h b/utils/rbutilqt/mspack/lzx.h new file mode 100644 index 0000000000..a6152f622b --- /dev/null +++ b/utils/rbutilqt/mspack/lzx.h | |||
@@ -0,0 +1,221 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2013 Stuart Caie. | ||
3 | * | ||
4 | * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted | ||
5 | * by Microsoft Corporation. | ||
6 | * | ||
7 | * libmspack is free software; you can redistribute it and/or modify it under | ||
8 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
9 | * | ||
10 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
11 | */ | ||
12 | |||
13 | #ifndef MSPACK_LZX_H | ||
14 | #define MSPACK_LZX_H 1 | ||
15 | |||
16 | #ifdef __cplusplus | ||
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 + 290*8) | ||
39 | #define LZX_MAINTREE_TABLEBITS (12) | ||
40 | #define LZX_LENGTH_MAXSYMBOLS (LZX_NUM_SECONDARY_LENGTHS+1) | ||
41 | #define LZX_LENGTH_TABLEBITS (12) | ||
42 | #define LZX_ALIGNED_MAXSYMBOLS (LZX_ALIGNED_NUM_ELEMENTS) | ||
43 | #define LZX_ALIGNED_TABLEBITS (7) | ||
44 | #define LZX_LENTABLE_SAFETY (64) /* table decoding overruns are allowed */ | ||
45 | |||
46 | #define LZX_FRAME_SIZE (32768) /* the size of a frame in LZX */ | ||
47 | |||
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 ref_data_size; /* LZX DELTA reference data size */ | ||
59 | unsigned int num_offsets; /* number of match_offset entries in table */ | ||
60 | unsigned int window_posn; /* decompression offset within window */ | ||
61 | unsigned int frame_posn; /* current frame offset within in window */ | ||
62 | unsigned int frame; /* the number of 32kb frames processed */ | ||
63 | unsigned int reset_interval; /* which frame do we reset the compressor? */ | ||
64 | |||
65 | unsigned int R0, R1, R2; /* for the LRU offset system */ | ||
66 | unsigned int block_length; /* uncompressed length of this LZX block */ | ||
67 | unsigned int block_remaining; /* uncompressed bytes still left to decode */ | ||
68 | |||
69 | signed int intel_filesize; /* magic header value used for transform */ | ||
70 | signed int intel_curpos; /* current offset in transform space */ | ||
71 | |||
72 | unsigned char intel_started; /* has intel E8 decoding started? */ | ||
73 | unsigned char block_type; /* type of the current block */ | ||
74 | unsigned char header_read; /* have we started decoding at all yet? */ | ||
75 | unsigned char input_end; /* have we reached the end of input? */ | ||
76 | unsigned char is_delta; /* does stream follow LZX DELTA spec? */ | ||
77 | |||
78 | int error; | ||
79 | |||
80 | /* I/O buffering */ | ||
81 | unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end; | ||
82 | unsigned int bit_buffer, bits_left, inbuf_size; | ||
83 | |||
84 | /* huffman code lengths */ | ||
85 | unsigned char PRETREE_len [LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; | ||
86 | unsigned char MAINTREE_len [LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; | ||
87 | unsigned char LENGTH_len [LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; | ||
88 | unsigned char ALIGNED_len [LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; | ||
89 | |||
90 | /* huffman decoding tables */ | ||
91 | unsigned short PRETREE_table [(1 << LZX_PRETREE_TABLEBITS) + | ||
92 | (LZX_PRETREE_MAXSYMBOLS * 2)]; | ||
93 | unsigned short MAINTREE_table[(1 << LZX_MAINTREE_TABLEBITS) + | ||
94 | (LZX_MAINTREE_MAXSYMBOLS * 2)]; | ||
95 | unsigned short LENGTH_table [(1 << LZX_LENGTH_TABLEBITS) + | ||
96 | (LZX_LENGTH_MAXSYMBOLS * 2)]; | ||
97 | unsigned short ALIGNED_table [(1 << LZX_ALIGNED_TABLEBITS) + | ||
98 | (LZX_ALIGNED_MAXSYMBOLS * 2)]; | ||
99 | unsigned char LENGTH_empty; | ||
100 | |||
101 | /* this is used purely for doing the intel E8 transform */ | ||
102 | unsigned char e8_buf[LZX_FRAME_SIZE]; | ||
103 | }; | ||
104 | |||
105 | /** | ||
106 | * Allocates and initialises LZX decompression state for decoding an LZX | ||
107 | * stream. | ||
108 | * | ||
109 | * This routine uses system->alloc() to allocate memory. If memory | ||
110 | * allocation fails, or the parameters to this function are invalid, | ||
111 | * NULL is returned. | ||
112 | * | ||
113 | * @param system an mspack_system structure used to read from | ||
114 | * the input stream and write to the output | ||
115 | * stream, also to allocate and free memory. | ||
116 | * @param input an input stream with the LZX data. | ||
117 | * @param output an output stream to write the decoded data to. | ||
118 | * @param window_bits the size of the decoding window, which must be | ||
119 | * between 15 and 21 inclusive for regular LZX | ||
120 | * data, or between 17 and 25 inclusive for | ||
121 | * LZX DELTA data. | ||
122 | * @param reset_interval the interval at which the LZX bitstream is | ||
123 | * reset, in multiples of LZX frames (32678 | ||
124 | * bytes), e.g. a value of 2 indicates the input | ||
125 | * stream resets after every 65536 output bytes. | ||
126 | * A value of 0 indicates that the bitstream never | ||
127 | * resets, such as in CAB LZX streams. | ||
128 | * @param input_buffer_size the number of bytes to use as an input | ||
129 | * bitstream buffer. | ||
130 | * @param output_length the length in bytes of the entirely | ||
131 | * decompressed output stream, if known in | ||
132 | * advance. It is used to correctly perform the | ||
133 | * Intel E8 transformation, which must stop 6 | ||
134 | * bytes before the very end of the | ||
135 | * decompressed stream. It is not otherwise used | ||
136 | * or adhered to. If the full decompressed | ||
137 | * length is known in advance, set it here. | ||
138 | * If it is NOT known, use the value 0, and call | ||
139 | * lzxd_set_output_length() once it is | ||
140 | * known. If never set, 4 of the final 6 bytes | ||
141 | * of the output stream may be incorrect. | ||
142 | * @param is_delta should be zero for all regular LZX data, | ||
143 | * non-zero for LZX DELTA encoded data. | ||
144 | * @return a pointer to an initialised lzxd_stream structure, or NULL if | ||
145 | * there was not enough memory or parameters to the function were wrong. | ||
146 | */ | ||
147 | extern struct lzxd_stream *lzxd_init(struct mspack_system *system, | ||
148 | struct mspack_file *input, | ||
149 | struct mspack_file *output, | ||
150 | int window_bits, | ||
151 | int reset_interval, | ||
152 | int input_buffer_size, | ||
153 | off_t output_length, | ||
154 | char is_delta); | ||
155 | |||
156 | /* see description of output_length in lzxd_init() */ | ||
157 | extern void lzxd_set_output_length(struct lzxd_stream *lzx, | ||
158 | off_t output_length); | ||
159 | |||
160 | /** | ||
161 | * Reads LZX DELTA reference data into the window and allows | ||
162 | * lzxd_decompress() to reference it. | ||
163 | * | ||
164 | * Call this before the first call to lzxd_decompress(). | ||
165 | |||
166 | * @param lzx the LZX stream to apply this reference data to | ||
167 | * @param system an mspack_system implementation to use with the | ||
168 | * input param. Only read() will be called. | ||
169 | * @param input an input file handle to read reference data using | ||
170 | * system->read(). | ||
171 | * @param length the length of the reference data. Cannot be longer | ||
172 | * than the LZX window size. | ||
173 | * @return an error code, or MSPACK_ERR_OK if successful | ||
174 | */ | ||
175 | extern int lzxd_set_reference_data(struct lzxd_stream *lzx, | ||
176 | struct mspack_system *system, | ||
177 | struct mspack_file *input, | ||
178 | unsigned int length); | ||
179 | |||
180 | /** | ||
181 | * Decompresses entire or partial LZX streams. | ||
182 | * | ||
183 | * The number of bytes of data that should be decompressed is given as the | ||
184 | * out_bytes parameter. If more bytes are decoded than are needed, they | ||
185 | * will be kept over for a later invocation. | ||
186 | * | ||
187 | * The output bytes will be passed to the system->write() function given in | ||
188 | * lzxd_init(), using the output file handle given in lzxd_init(). More than | ||
189 | * one call may be made to system->write(). | ||
190 | |||
191 | * Input bytes will be read in as necessary using the system->read() | ||
192 | * function given in lzxd_init(), using the input file handle given in | ||
193 | * lzxd_init(). This will continue until system->read() returns 0 bytes, | ||
194 | * or an error. Errors will be passed out of the function as | ||
195 | * MSPACK_ERR_READ errors. Input streams should convey an "end of input | ||
196 | * stream" by refusing to supply all the bytes that LZX asks for when they | ||
197 | * reach the end of the stream, rather than return an error code. | ||
198 | * | ||
199 | * If any error code other than MSPACK_ERR_OK is returned, the stream | ||
200 | * should be considered unusable and lzxd_decompress() should not be | ||
201 | * called again on this stream. | ||
202 | * | ||
203 | * @param lzx LZX decompression state, as allocated by lzxd_init(). | ||
204 | * @param out_bytes the number of bytes of data to decompress. | ||
205 | * @return an error code, or MSPACK_ERR_OK if successful | ||
206 | */ | ||
207 | extern int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes); | ||
208 | |||
209 | /** | ||
210 | * Frees all state associated with an LZX data stream. This will call | ||
211 | * system->free() using the system pointer given in lzxd_init(). | ||
212 | * | ||
213 | * @param lzx LZX decompression state to free. | ||
214 | */ | ||
215 | void lzxd_free(struct lzxd_stream *lzx); | ||
216 | |||
217 | #ifdef __cplusplus | ||
218 | } | ||
219 | #endif | ||
220 | |||
221 | #endif | ||
diff --git a/utils/rbutilqt/mspack/lzxc.c b/utils/rbutilqt/mspack/lzxc.c new file mode 100644 index 0000000000..89d8c610ea --- /dev/null +++ b/utils/rbutilqt/mspack/lzxc.c | |||
@@ -0,0 +1,18 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted | ||
5 | * by Microsoft Corporation. | ||
6 | * | ||
7 | * libmspack is free software; you can redistribute it and/or modify it under | ||
8 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
9 | * | ||
10 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
11 | */ | ||
12 | |||
13 | /* LZX compression implementation */ | ||
14 | |||
15 | #include "system-mspack.h" | ||
16 | #include "lzx.h" | ||
17 | |||
18 | /* todo */ | ||
diff --git a/utils/rbutilqt/mspack/lzxd.c b/utils/rbutilqt/mspack/lzxd.c new file mode 100644 index 0000000000..88cfd90c2a --- /dev/null +++ b/utils/rbutilqt/mspack/lzxd.c | |||
@@ -0,0 +1,905 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2013 Stuart Caie. | ||
3 | * | ||
4 | * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted | ||
5 | * by Microsoft Corporation. | ||
6 | * | ||
7 | * libmspack is free software; you can redistribute it and/or modify it under | ||
8 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
9 | * | ||
10 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
11 | */ | ||
12 | |||
13 | /* LZX decompression implementation */ | ||
14 | |||
15 | #include "system-mspack.h" | ||
16 | #include "lzx.h" | ||
17 | |||
18 | /* Microsoft's LZX document (in cab-sdk.exe) and their implementation | ||
19 | * of the com.ms.util.cab Java package do not concur. | ||
20 | * | ||
21 | * In the LZX document, there is a table showing the correlation between | ||
22 | * window size and the number of position slots. It states that the 1MB | ||
23 | * window = 40 slots and the 2MB window = 42 slots. In the implementation, | ||
24 | * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the | ||
25 | * first slot whose position base is equal to or more than the required | ||
26 | * window size'. This would explain why other tables in the document refer | ||
27 | * to 50 slots rather than 42. | ||
28 | * | ||
29 | * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode | ||
30 | * is not defined in the specification. | ||
31 | * | ||
32 | * The LZX document does not state the uncompressed block has an | ||
33 | * uncompressed length field. Where does this length field come from, so | ||
34 | * we can know how large the block is? The implementation has it as the 24 | ||
35 | * bits following after the 3 blocktype bits, before the alignment | ||
36 | * padding. | ||
37 | * | ||
38 | * The LZX document states that aligned offset blocks have their aligned | ||
39 | * offset huffman tree AFTER the main and length trees. The implementation | ||
40 | * suggests that the aligned offset tree is BEFORE the main and length | ||
41 | * trees. | ||
42 | * | ||
43 | * The LZX document decoding algorithm states that, in an aligned offset | ||
44 | * block, if an extra_bits value is 1, 2 or 3, then that number of bits | ||
45 | * should be read and the result added to the match offset. This is | ||
46 | * correct for 1 and 2, but not 3, where just a huffman symbol (using the | ||
47 | * aligned tree) should be read. | ||
48 | * | ||
49 | * Regarding the E8 preprocessing, the LZX document states 'No translation | ||
50 | * may be performed on the last 6 bytes of the input block'. This is | ||
51 | * correct. However, the pseudocode provided checks for the *E8 leader* | ||
52 | * up to the last 6 bytes. If the leader appears between -10 and -7 bytes | ||
53 | * from the end, this would cause the next four bytes to be modified, at | ||
54 | * least one of which would be in the last 6 bytes, which is not allowed | ||
55 | * according to the spec. | ||
56 | * | ||
57 | * The specification states that the huffman trees must always contain at | ||
58 | * least one element. However, many CAB files contain blocks where the | ||
59 | * length tree is completely empty (because there are no matches), and | ||
60 | * this is expected to succeed. | ||
61 | * | ||
62 | * The errors in LZX documentation appear have been corrected in the | ||
63 | * new documentation for the LZX DELTA format. | ||
64 | * | ||
65 | * http://msdn.microsoft.com/en-us/library/cc483133.aspx | ||
66 | * | ||
67 | * However, this is a different format, an extension of regular LZX. | ||
68 | * I have noticed the following differences, there may be more: | ||
69 | * | ||
70 | * The maximum window size has increased from 2MB to 32MB. This also | ||
71 | * increases the maximum number of position slots, etc. | ||
72 | * | ||
73 | * If the match length is 257 (the maximum possible), this signals | ||
74 | * a further length decoding step, that allows for matches up to | ||
75 | * 33024 bytes long. | ||
76 | * | ||
77 | * The format now allows for "reference data", supplied by the caller. | ||
78 | * If match offsets go further back than the number of bytes | ||
79 | * decompressed so far, that is them accessing the reference data. | ||
80 | */ | ||
81 | |||
82 | /* import bit-reading macros and code */ | ||
83 | #define BITS_TYPE struct lzxd_stream | ||
84 | #define BITS_VAR lzx | ||
85 | #define BITS_ORDER_MSB | ||
86 | #define READ_BYTES do { \ | ||
87 | unsigned char b0, b1; \ | ||
88 | READ_IF_NEEDED; b0 = *i_ptr++; \ | ||
89 | READ_IF_NEEDED; b1 = *i_ptr++; \ | ||
90 | INJECT_BITS((b1 << 8) | b0, 16); \ | ||
91 | } while (0) | ||
92 | #include "readbits.h" | ||
93 | |||
94 | /* import huffman-reading macros and code */ | ||
95 | #define TABLEBITS(tbl) LZX_##tbl##_TABLEBITS | ||
96 | #define MAXSYMBOLS(tbl) LZX_##tbl##_MAXSYMBOLS | ||
97 | #define HUFF_TABLE(tbl,idx) lzx->tbl##_table[idx] | ||
98 | #define HUFF_LEN(tbl,idx) lzx->tbl##_len[idx] | ||
99 | #define HUFF_ERROR return lzx->error = MSPACK_ERR_DECRUNCH | ||
100 | #include "readhuff.h" | ||
101 | |||
102 | /* BUILD_TABLE(tbl) builds a huffman lookup table from code lengths */ | ||
103 | #define BUILD_TABLE(tbl) \ | ||
104 | if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ | ||
105 | &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ | ||
106 | { \ | ||
107 | D(("failed to build %s table", #tbl)) \ | ||
108 | return lzx->error = MSPACK_ERR_DECRUNCH; \ | ||
109 | } | ||
110 | |||
111 | #define BUILD_TABLE_MAYBE_EMPTY(tbl) do { \ | ||
112 | lzx->tbl##_empty = 0; \ | ||
113 | if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ | ||
114 | &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ | ||
115 | { \ | ||
116 | for (i = 0; i < MAXSYMBOLS(tbl); i++) { \ | ||
117 | if (HUFF_LEN(tbl, i) > 0) { \ | ||
118 | D(("failed to build %s table", #tbl)) \ | ||
119 | return lzx->error = MSPACK_ERR_DECRUNCH; \ | ||
120 | } \ | ||
121 | } \ | ||
122 | /* empty tree - allow it, but don't decode symbols with it */ \ | ||
123 | lzx->tbl##_empty = 1; \ | ||
124 | } \ | ||
125 | } while (0) | ||
126 | |||
127 | /* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols | ||
128 | * first to last in the given table. The code lengths are stored in their | ||
129 | * own special LZX way. | ||
130 | */ | ||
131 | #define READ_LENGTHS(tbl, first, last) do { \ | ||
132 | STORE_BITS; \ | ||
133 | if (lzxd_read_lens(lzx, &HUFF_LEN(tbl, 0), (first), \ | ||
134 | (unsigned int)(last))) return lzx->error; \ | ||
135 | RESTORE_BITS; \ | ||
136 | } while (0) | ||
137 | |||
138 | static int lzxd_read_lens(struct lzxd_stream *lzx, unsigned char *lens, | ||
139 | unsigned int first, unsigned int last) | ||
140 | { | ||
141 | /* bit buffer and huffman symbol decode variables */ | ||
142 | register unsigned int bit_buffer; | ||
143 | register int bits_left, i; | ||
144 | register unsigned short sym; | ||
145 | unsigned char *i_ptr, *i_end; | ||
146 | |||
147 | unsigned int x, y; | ||
148 | int z; | ||
149 | |||
150 | RESTORE_BITS; | ||
151 | |||
152 | /* read lengths for pretree (20 symbols, lengths stored in fixed 4 bits) */ | ||
153 | for (x = 0; x < 20; x++) { | ||
154 | READ_BITS(y, 4); | ||
155 | lzx->PRETREE_len[x] = y; | ||
156 | } | ||
157 | BUILD_TABLE(PRETREE); | ||
158 | |||
159 | for (x = first; x < last; ) { | ||
160 | READ_HUFFSYM(PRETREE, z); | ||
161 | if (z == 17) { | ||
162 | /* code = 17, run of ([read 4 bits]+4) zeros */ | ||
163 | READ_BITS(y, 4); y += 4; | ||
164 | while (y--) lens[x++] = 0; | ||
165 | } | ||
166 | else if (z == 18) { | ||
167 | /* code = 18, run of ([read 5 bits]+20) zeros */ | ||
168 | READ_BITS(y, 5); y += 20; | ||
169 | while (y--) lens[x++] = 0; | ||
170 | } | ||
171 | else if (z == 19) { | ||
172 | /* code = 19, run of ([read 1 bit]+4) [read huffman symbol] */ | ||
173 | READ_BITS(y, 1); y += 4; | ||
174 | READ_HUFFSYM(PRETREE, z); | ||
175 | z = lens[x] - z; if (z < 0) z += 17; | ||
176 | while (y--) lens[x++] = z; | ||
177 | } | ||
178 | else { | ||
179 | /* code = 0 to 16, delta current length entry */ | ||
180 | z = lens[x] - z; if (z < 0) z += 17; | ||
181 | lens[x++] = z; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | STORE_BITS; | ||
186 | |||
187 | return MSPACK_ERR_OK; | ||
188 | } | ||
189 | |||
190 | /* LZX static data tables: | ||
191 | * | ||
192 | * LZX uses 'position slots' to represent match offsets. For every match, | ||
193 | * a small 'position slot' number and a small offset from that slot are | ||
194 | * encoded instead of one large offset. | ||
195 | * | ||
196 | * The number of slots is decided by how many are needed to encode the | ||
197 | * largest offset for a given window size. This is easy when the gap between | ||
198 | * slots is less than 128Kb, it's a linear relationship. But when extra_bits | ||
199 | * reaches its limit of 17 (because LZX can only ensure reading 17 bits of | ||
200 | * data at a time), we can only jump 128Kb at a time and have to start | ||
201 | * using more and more position slots as each window size doubles. | ||
202 | * | ||
203 | * position_base[] is an index to the position slot bases | ||
204 | * | ||
205 | * extra_bits[] states how many bits of offset-from-base data is needed. | ||
206 | * | ||
207 | * They are calculated as follows: | ||
208 | * extra_bits[i] = 0 where i < 4 | ||
209 | * extra_bits[i] = floor(i/2)-1 where i >= 4 && i < 36 | ||
210 | * extra_bits[i] = 17 where i >= 36 | ||
211 | * position_base[0] = 0 | ||
212 | * position_base[i] = position_base[i-1] + (1 << extra_bits[i-1]) | ||
213 | */ | ||
214 | static const unsigned int position_slots[11] = { | ||
215 | 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290 | ||
216 | }; | ||
217 | static const unsigned char extra_bits[36] = { | ||
218 | 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, | ||
219 | 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16 | ||
220 | }; | ||
221 | static const unsigned int position_base[290] = { | ||
222 | 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, | ||
223 | 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, | ||
224 | 49152, 65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360, | ||
225 | 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936, | ||
226 | 1835008, 1966080, 2097152, 2228224, 2359296, 2490368, 2621440, 2752512, | ||
227 | 2883584, 3014656, 3145728, 3276800, 3407872, 3538944, 3670016, 3801088, | ||
228 | 3932160, 4063232, 4194304, 4325376, 4456448, 4587520, 4718592, 4849664, | ||
229 | 4980736, 5111808, 5242880, 5373952, 5505024, 5636096, 5767168, 5898240, | ||
230 | 6029312, 6160384, 6291456, 6422528, 6553600, 6684672, 6815744, 6946816, | ||
231 | 7077888, 7208960, 7340032, 7471104, 7602176, 7733248, 7864320, 7995392, | ||
232 | 8126464, 8257536, 8388608, 8519680, 8650752, 8781824, 8912896, 9043968, | ||
233 | 9175040, 9306112, 9437184, 9568256, 9699328, 9830400, 9961472, 10092544, | ||
234 | 10223616, 10354688, 10485760, 10616832, 10747904, 10878976, 11010048, | ||
235 | 11141120, 11272192, 11403264, 11534336, 11665408, 11796480, 11927552, | ||
236 | 12058624, 12189696, 12320768, 12451840, 12582912, 12713984, 12845056, | ||
237 | 12976128, 13107200, 13238272, 13369344, 13500416, 13631488, 13762560, | ||
238 | 13893632, 14024704, 14155776, 14286848, 14417920, 14548992, 14680064, | ||
239 | 14811136, 14942208, 15073280, 15204352, 15335424, 15466496, 15597568, | ||
240 | 15728640, 15859712, 15990784, 16121856, 16252928, 16384000, 16515072, | ||
241 | 16646144, 16777216, 16908288, 17039360, 17170432, 17301504, 17432576, | ||
242 | 17563648, 17694720, 17825792, 17956864, 18087936, 18219008, 18350080, | ||
243 | 18481152, 18612224, 18743296, 18874368, 19005440, 19136512, 19267584, | ||
244 | 19398656, 19529728, 19660800, 19791872, 19922944, 20054016, 20185088, | ||
245 | 20316160, 20447232, 20578304, 20709376, 20840448, 20971520, 21102592, | ||
246 | 21233664, 21364736, 21495808, 21626880, 21757952, 21889024, 22020096, | ||
247 | 22151168, 22282240, 22413312, 22544384, 22675456, 22806528, 22937600, | ||
248 | 23068672, 23199744, 23330816, 23461888, 23592960, 23724032, 23855104, | ||
249 | 23986176, 24117248, 24248320, 24379392, 24510464, 24641536, 24772608, | ||
250 | 24903680, 25034752, 25165824, 25296896, 25427968, 25559040, 25690112, | ||
251 | 25821184, 25952256, 26083328, 26214400, 26345472, 26476544, 26607616, | ||
252 | 26738688, 26869760, 27000832, 27131904, 27262976, 27394048, 27525120, | ||
253 | 27656192, 27787264, 27918336, 28049408, 28180480, 28311552, 28442624, | ||
254 | 28573696, 28704768, 28835840, 28966912, 29097984, 29229056, 29360128, | ||
255 | 29491200, 29622272, 29753344, 29884416, 30015488, 30146560, 30277632, | ||
256 | 30408704, 30539776, 30670848, 30801920, 30932992, 31064064, 31195136, | ||
257 | 31326208, 31457280, 31588352, 31719424, 31850496, 31981568, 32112640, | ||
258 | 32243712, 32374784, 32505856, 32636928, 32768000, 32899072, 33030144, | ||
259 | 33161216, 33292288, 33423360 | ||
260 | }; | ||
261 | |||
262 | static void lzxd_reset_state(struct lzxd_stream *lzx) { | ||
263 | int i; | ||
264 | |||
265 | lzx->R0 = 1; | ||
266 | lzx->R1 = 1; | ||
267 | lzx->R2 = 1; | ||
268 | lzx->header_read = 0; | ||
269 | lzx->block_remaining = 0; | ||
270 | lzx->block_type = LZX_BLOCKTYPE_INVALID; | ||
271 | |||
272 | /* initialise tables to 0 (because deltas will be applied to them) */ | ||
273 | for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) lzx->MAINTREE_len[i] = 0; | ||
274 | for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) lzx->LENGTH_len[i] = 0; | ||
275 | } | ||
276 | |||
277 | /*-------- main LZX code --------*/ | ||
278 | |||
279 | struct lzxd_stream *lzxd_init(struct mspack_system *system, | ||
280 | struct mspack_file *input, | ||
281 | struct mspack_file *output, | ||
282 | int window_bits, | ||
283 | int reset_interval, | ||
284 | int input_buffer_size, | ||
285 | off_t output_length, | ||
286 | char is_delta) | ||
287 | { | ||
288 | unsigned int window_size = 1 << window_bits; | ||
289 | struct lzxd_stream *lzx; | ||
290 | |||
291 | if (!system) return NULL; | ||
292 | |||
293 | /* LZX DELTA window sizes are between 2^17 (128KiB) and 2^25 (32MiB), | ||
294 | * regular LZX windows are between 2^15 (32KiB) and 2^21 (2MiB) | ||
295 | */ | ||
296 | if (is_delta) { | ||
297 | if (window_bits < 17 || window_bits > 25) return NULL; | ||
298 | } | ||
299 | else { | ||
300 | if (window_bits < 15 || window_bits > 21) return NULL; | ||
301 | } | ||
302 | |||
303 | if (reset_interval < 0 || output_length < 0) { | ||
304 | D(("reset interval or output length < 0")) | ||
305 | return NULL; | ||
306 | } | ||
307 | |||
308 | /* round up input buffer size to multiple of two */ | ||
309 | input_buffer_size = (input_buffer_size + 1) & -2; | ||
310 | if (input_buffer_size < 2) return NULL; | ||
311 | |||
312 | /* allocate decompression state */ | ||
313 | if (!(lzx = (struct lzxd_stream *) system->alloc(system, sizeof(struct lzxd_stream)))) { | ||
314 | return NULL; | ||
315 | } | ||
316 | |||
317 | /* allocate decompression window and input buffer */ | ||
318 | lzx->window = (unsigned char *) system->alloc(system, (size_t) window_size); | ||
319 | lzx->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size); | ||
320 | if (!lzx->window || !lzx->inbuf) { | ||
321 | system->free(lzx->window); | ||
322 | system->free(lzx->inbuf); | ||
323 | system->free(lzx); | ||
324 | return NULL; | ||
325 | } | ||
326 | |||
327 | /* initialise decompression state */ | ||
328 | lzx->sys = system; | ||
329 | lzx->input = input; | ||
330 | lzx->output = output; | ||
331 | lzx->offset = 0; | ||
332 | lzx->length = output_length; | ||
333 | |||
334 | lzx->inbuf_size = input_buffer_size; | ||
335 | lzx->window_size = 1 << window_bits; | ||
336 | lzx->ref_data_size = 0; | ||
337 | lzx->window_posn = 0; | ||
338 | lzx->frame_posn = 0; | ||
339 | lzx->frame = 0; | ||
340 | lzx->reset_interval = reset_interval; | ||
341 | lzx->intel_filesize = 0; | ||
342 | lzx->intel_curpos = 0; | ||
343 | lzx->intel_started = 0; | ||
344 | lzx->error = MSPACK_ERR_OK; | ||
345 | lzx->num_offsets = position_slots[window_bits - 15] << 3; | ||
346 | lzx->is_delta = is_delta; | ||
347 | |||
348 | lzx->o_ptr = lzx->o_end = &lzx->e8_buf[0]; | ||
349 | lzxd_reset_state(lzx); | ||
350 | INIT_BITS; | ||
351 | return lzx; | ||
352 | } | ||
353 | |||
354 | int lzxd_set_reference_data(struct lzxd_stream *lzx, | ||
355 | struct mspack_system *system, | ||
356 | struct mspack_file *input, | ||
357 | unsigned int length) | ||
358 | { | ||
359 | if (!lzx) return MSPACK_ERR_ARGS; | ||
360 | |||
361 | if (!lzx->is_delta) { | ||
362 | D(("only LZX DELTA streams support reference data")) | ||
363 | return MSPACK_ERR_ARGS; | ||
364 | } | ||
365 | if (lzx->offset) { | ||
366 | D(("too late to set reference data after decoding starts")) | ||
367 | return MSPACK_ERR_ARGS; | ||
368 | } | ||
369 | if (length > lzx->window_size) { | ||
370 | D(("reference length (%u) is longer than the window", length)) | ||
371 | return MSPACK_ERR_ARGS; | ||
372 | } | ||
373 | if (length > 0 && (!system || !input)) { | ||
374 | D(("length > 0 but no system or input")) | ||
375 | return MSPACK_ERR_ARGS; | ||
376 | } | ||
377 | |||
378 | lzx->ref_data_size = length; | ||
379 | if (length > 0) { | ||
380 | /* copy reference data */ | ||
381 | unsigned char *pos = &lzx->window[lzx->window_size - length]; | ||
382 | int bytes = system->read(input, pos, length); | ||
383 | /* length can't be more than 2^25, so no signedness problem */ | ||
384 | if (bytes < (int)length) return MSPACK_ERR_READ; | ||
385 | } | ||
386 | lzx->ref_data_size = length; | ||
387 | return MSPACK_ERR_OK; | ||
388 | } | ||
389 | |||
390 | void lzxd_set_output_length(struct lzxd_stream *lzx, off_t out_bytes) { | ||
391 | if (lzx && out_bytes > 0) lzx->length = out_bytes; | ||
392 | } | ||
393 | |||
394 | int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { | ||
395 | /* bitstream and huffman reading variables */ | ||
396 | register unsigned int bit_buffer; | ||
397 | register int bits_left, i=0; | ||
398 | unsigned char *i_ptr, *i_end; | ||
399 | register unsigned short sym; | ||
400 | |||
401 | int match_length, length_footer, extra, verbatim_bits, bytes_todo; | ||
402 | int this_run, main_element, aligned_bits, j, warned = 0; | ||
403 | unsigned char *window, *runsrc, *rundest, buf[12]; | ||
404 | unsigned int frame_size=0, end_frame, match_offset, window_posn; | ||
405 | unsigned int R0, R1, R2; | ||
406 | |||
407 | /* easy answers */ | ||
408 | if (!lzx || (out_bytes < 0)) return MSPACK_ERR_ARGS; | ||
409 | if (lzx->error) return lzx->error; | ||
410 | |||
411 | /* flush out any stored-up bytes before we begin */ | ||
412 | i = lzx->o_end - lzx->o_ptr; | ||
413 | if ((off_t) i > out_bytes) i = (int) out_bytes; | ||
414 | if (i) { | ||
415 | if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) { | ||
416 | return lzx->error = MSPACK_ERR_WRITE; | ||
417 | } | ||
418 | lzx->o_ptr += i; | ||
419 | lzx->offset += i; | ||
420 | out_bytes -= i; | ||
421 | } | ||
422 | if (out_bytes == 0) return MSPACK_ERR_OK; | ||
423 | |||
424 | /* restore local state */ | ||
425 | RESTORE_BITS; | ||
426 | window = lzx->window; | ||
427 | window_posn = lzx->window_posn; | ||
428 | R0 = lzx->R0; | ||
429 | R1 = lzx->R1; | ||
430 | R2 = lzx->R2; | ||
431 | |||
432 | end_frame = (unsigned int)((lzx->offset + out_bytes) / LZX_FRAME_SIZE) + 1; | ||
433 | |||
434 | while (lzx->frame < end_frame) { | ||
435 | /* have we reached the reset interval? (if there is one?) */ | ||
436 | if (lzx->reset_interval && ((lzx->frame % lzx->reset_interval) == 0)) { | ||
437 | if (lzx->block_remaining) { | ||
438 | /* this is a file format error, we can make a best effort to extract what we can */ | ||
439 | D(("%d bytes remaining at reset interval", lzx->block_remaining)) | ||
440 | if (!warned) { | ||
441 | lzx->sys->message(NULL, "WARNING; invalid reset interval detected during LZX decompression"); | ||
442 | warned++; | ||
443 | } | ||
444 | } | ||
445 | |||
446 | /* re-read the intel header and reset the huffman lengths */ | ||
447 | lzxd_reset_state(lzx); | ||
448 | R0 = lzx->R0; | ||
449 | R1 = lzx->R1; | ||
450 | R2 = lzx->R2; | ||
451 | } | ||
452 | |||
453 | /* LZX DELTA format has chunk_size, not present in LZX format */ | ||
454 | if (lzx->is_delta) { | ||
455 | ENSURE_BITS(16); | ||
456 | REMOVE_BITS(16); | ||
457 | } | ||
458 | |||
459 | /* read header if necessary */ | ||
460 | if (!lzx->header_read) { | ||
461 | /* read 1 bit. if bit=0, intel filesize = 0. | ||
462 | * if bit=1, read intel filesize (32 bits) */ | ||
463 | j = 0; READ_BITS(i, 1); if (i) { READ_BITS(i, 16); READ_BITS(j, 16); } | ||
464 | lzx->intel_filesize = (i << 16) | j; | ||
465 | lzx->header_read = 1; | ||
466 | } | ||
467 | |||
468 | /* calculate size of frame: all frames are 32k except the final frame | ||
469 | * which is 32kb or less. this can only be calculated when lzx->length | ||
470 | * has been filled in. */ | ||
471 | frame_size = LZX_FRAME_SIZE; | ||
472 | if (lzx->length && (lzx->length - lzx->offset) < (off_t)frame_size) { | ||
473 | frame_size = lzx->length - lzx->offset; | ||
474 | } | ||
475 | |||
476 | /* decode until one more frame is available */ | ||
477 | bytes_todo = lzx->frame_posn + frame_size - window_posn; | ||
478 | while (bytes_todo > 0) { | ||
479 | /* initialise new block, if one is needed */ | ||
480 | if (lzx->block_remaining == 0) { | ||
481 | /* realign if previous block was an odd-sized UNCOMPRESSED block */ | ||
482 | if ((lzx->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) && | ||
483 | (lzx->block_length & 1)) | ||
484 | { | ||
485 | READ_IF_NEEDED; | ||
486 | i_ptr++; | ||
487 | } | ||
488 | |||
489 | /* read block type (3 bits) and block length (24 bits) */ | ||
490 | READ_BITS(lzx->block_type, 3); | ||
491 | READ_BITS(i, 16); READ_BITS(j, 8); | ||
492 | lzx->block_remaining = lzx->block_length = (i << 8) | j; | ||
493 | /*D(("new block t%d len %u", lzx->block_type, lzx->block_length))*/ | ||
494 | |||
495 | /* read individual block headers */ | ||
496 | switch (lzx->block_type) { | ||
497 | case LZX_BLOCKTYPE_ALIGNED: | ||
498 | /* read lengths of and build aligned huffman decoding tree */ | ||
499 | for (i = 0; i < 8; i++) { READ_BITS(j, 3); lzx->ALIGNED_len[i] = j; } | ||
500 | BUILD_TABLE(ALIGNED); | ||
501 | /* rest of aligned header is same as verbatim */ /*@fallthrough@*/ | ||
502 | case LZX_BLOCKTYPE_VERBATIM: | ||
503 | /* read lengths of and build main huffman decoding tree */ | ||
504 | READ_LENGTHS(MAINTREE, 0, 256); | ||
505 | READ_LENGTHS(MAINTREE, 256, LZX_NUM_CHARS + lzx->num_offsets); | ||
506 | BUILD_TABLE(MAINTREE); | ||
507 | /* if the literal 0xE8 is anywhere in the block... */ | ||
508 | if (lzx->MAINTREE_len[0xE8] != 0) lzx->intel_started = 1; | ||
509 | /* read lengths of and build lengths huffman decoding tree */ | ||
510 | READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS); | ||
511 | BUILD_TABLE_MAYBE_EMPTY(LENGTH); | ||
512 | break; | ||
513 | |||
514 | case LZX_BLOCKTYPE_UNCOMPRESSED: | ||
515 | /* because we can't assume otherwise */ | ||
516 | lzx->intel_started = 1; | ||
517 | |||
518 | /* read 1-16 (not 0-15) bits to align to bytes */ | ||
519 | if (bits_left == 0) ENSURE_BITS(16); | ||
520 | bits_left = 0; bit_buffer = 0; | ||
521 | |||
522 | /* read 12 bytes of stored R0 / R1 / R2 values */ | ||
523 | for (rundest = &buf[0], i = 0; i < 12; i++) { | ||
524 | READ_IF_NEEDED; | ||
525 | *rundest++ = *i_ptr++; | ||
526 | } | ||
527 | R0 = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); | ||
528 | R1 = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); | ||
529 | R2 = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24); | ||
530 | break; | ||
531 | |||
532 | default: | ||
533 | D(("bad block type")) | ||
534 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
535 | } | ||
536 | } | ||
537 | |||
538 | /* decode more of the block: | ||
539 | * run = min(what's available, what's needed) */ | ||
540 | this_run = lzx->block_remaining; | ||
541 | if (this_run > bytes_todo) this_run = bytes_todo; | ||
542 | |||
543 | /* assume we decode exactly this_run bytes, for now */ | ||
544 | bytes_todo -= this_run; | ||
545 | lzx->block_remaining -= this_run; | ||
546 | |||
547 | /* decode at least this_run bytes */ | ||
548 | switch (lzx->block_type) { | ||
549 | case LZX_BLOCKTYPE_VERBATIM: | ||
550 | while (this_run > 0) { | ||
551 | READ_HUFFSYM(MAINTREE, main_element); | ||
552 | if (main_element < LZX_NUM_CHARS) { | ||
553 | /* literal: 0 to LZX_NUM_CHARS-1 */ | ||
554 | window[window_posn++] = main_element; | ||
555 | this_run--; | ||
556 | } | ||
557 | else { | ||
558 | /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ | ||
559 | main_element -= LZX_NUM_CHARS; | ||
560 | |||
561 | /* get match length */ | ||
562 | match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; | ||
563 | if (match_length == LZX_NUM_PRIMARY_LENGTHS) { | ||
564 | if (lzx->LENGTH_empty) { | ||
565 | D(("LENGTH symbol needed but tree is empty")) | ||
566 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
567 | } | ||
568 | READ_HUFFSYM(LENGTH, length_footer); | ||
569 | match_length += length_footer; | ||
570 | } | ||
571 | match_length += LZX_MIN_MATCH; | ||
572 | |||
573 | /* get match offset */ | ||
574 | switch ((match_offset = (main_element >> 3))) { | ||
575 | case 0: match_offset = R0; break; | ||
576 | case 1: match_offset = R1; R1=R0; R0 = match_offset; break; | ||
577 | case 2: match_offset = R2; R2=R0; R0 = match_offset; break; | ||
578 | case 3: match_offset = 1; R2=R1; R1=R0; R0 = match_offset; break; | ||
579 | default: | ||
580 | extra = (match_offset >= 36) ? 17 : extra_bits[match_offset]; | ||
581 | READ_BITS(verbatim_bits, extra); | ||
582 | match_offset = position_base[match_offset] - 2 + verbatim_bits; | ||
583 | R2 = R1; R1 = R0; R0 = match_offset; | ||
584 | } | ||
585 | |||
586 | /* LZX DELTA uses max match length to signal even longer match */ | ||
587 | if (match_length == LZX_MAX_MATCH && lzx->is_delta) { | ||
588 | int extra_len = 0; | ||
589 | ENSURE_BITS(3); /* 4 entry huffman tree */ | ||
590 | if (PEEK_BITS(1) == 0) { | ||
591 | REMOVE_BITS(1); /* '0' -> 8 extra length bits */ | ||
592 | READ_BITS(extra_len, 8); | ||
593 | } | ||
594 | else if (PEEK_BITS(2) == 2) { | ||
595 | REMOVE_BITS(2); /* '10' -> 10 extra length bits + 0x100 */ | ||
596 | READ_BITS(extra_len, 10); | ||
597 | extra_len += 0x100; | ||
598 | } | ||
599 | else if (PEEK_BITS(3) == 6) { | ||
600 | REMOVE_BITS(3); /* '110' -> 12 extra length bits + 0x500 */ | ||
601 | READ_BITS(extra_len, 12); | ||
602 | extra_len += 0x500; | ||
603 | } | ||
604 | else { | ||
605 | REMOVE_BITS(3); /* '111' -> 15 extra length bits */ | ||
606 | READ_BITS(extra_len, 15); | ||
607 | } | ||
608 | match_length += extra_len; | ||
609 | } | ||
610 | |||
611 | if ((window_posn + match_length) > lzx->window_size) { | ||
612 | D(("match ran over window wrap")) | ||
613 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
614 | } | ||
615 | |||
616 | /* copy match */ | ||
617 | rundest = &window[window_posn]; | ||
618 | i = match_length; | ||
619 | /* does match offset wrap the window? */ | ||
620 | if (match_offset > window_posn) { | ||
621 | if (match_offset > lzx->offset && | ||
622 | (match_offset - window_posn) > lzx->ref_data_size) | ||
623 | { | ||
624 | D(("match offset beyond LZX stream")) | ||
625 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
626 | } | ||
627 | /* j = length from match offset to end of window */ | ||
628 | j = match_offset - window_posn; | ||
629 | if (j > (int) lzx->window_size) { | ||
630 | D(("match offset beyond window boundaries")) | ||
631 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
632 | } | ||
633 | runsrc = &window[lzx->window_size - j]; | ||
634 | if (j < i) { | ||
635 | /* if match goes over the window edge, do two copy runs */ | ||
636 | i -= j; while (j-- > 0) *rundest++ = *runsrc++; | ||
637 | runsrc = window; | ||
638 | } | ||
639 | while (i-- > 0) *rundest++ = *runsrc++; | ||
640 | } | ||
641 | else { | ||
642 | runsrc = rundest - match_offset; | ||
643 | while (i-- > 0) *rundest++ = *runsrc++; | ||
644 | } | ||
645 | |||
646 | this_run -= match_length; | ||
647 | window_posn += match_length; | ||
648 | } | ||
649 | } /* while (this_run > 0) */ | ||
650 | break; | ||
651 | |||
652 | case LZX_BLOCKTYPE_ALIGNED: | ||
653 | while (this_run > 0) { | ||
654 | READ_HUFFSYM(MAINTREE, main_element); | ||
655 | if (main_element < LZX_NUM_CHARS) { | ||
656 | /* literal: 0 to LZX_NUM_CHARS-1 */ | ||
657 | window[window_posn++] = main_element; | ||
658 | this_run--; | ||
659 | } | ||
660 | else { | ||
661 | /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ | ||
662 | main_element -= LZX_NUM_CHARS; | ||
663 | |||
664 | /* get match length */ | ||
665 | match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; | ||
666 | if (match_length == LZX_NUM_PRIMARY_LENGTHS) { | ||
667 | if (lzx->LENGTH_empty) { | ||
668 | D(("LENGTH symbol needed but tree is empty")) | ||
669 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
670 | } | ||
671 | READ_HUFFSYM(LENGTH, length_footer); | ||
672 | match_length += length_footer; | ||
673 | } | ||
674 | match_length += LZX_MIN_MATCH; | ||
675 | |||
676 | /* get match offset */ | ||
677 | switch ((match_offset = (main_element >> 3))) { | ||
678 | case 0: match_offset = R0; break; | ||
679 | case 1: match_offset = R1; R1 = R0; R0 = match_offset; break; | ||
680 | case 2: match_offset = R2; R2 = R0; R0 = match_offset; break; | ||
681 | default: | ||
682 | extra = (match_offset >= 36) ? 17 : extra_bits[match_offset]; | ||
683 | match_offset = position_base[match_offset] - 2; | ||
684 | if (extra > 3) { | ||
685 | /* verbatim and aligned bits */ | ||
686 | extra -= 3; | ||
687 | READ_BITS(verbatim_bits, extra); | ||
688 | match_offset += (verbatim_bits << 3); | ||
689 | READ_HUFFSYM(ALIGNED, aligned_bits); | ||
690 | match_offset += aligned_bits; | ||
691 | } | ||
692 | else if (extra == 3) { | ||
693 | /* aligned bits only */ | ||
694 | READ_HUFFSYM(ALIGNED, aligned_bits); | ||
695 | match_offset += aligned_bits; | ||
696 | } | ||
697 | else if (extra > 0) { /* extra==1, extra==2 */ | ||
698 | /* verbatim bits only */ | ||
699 | READ_BITS(verbatim_bits, extra); | ||
700 | match_offset += verbatim_bits; | ||
701 | } | ||
702 | else /* extra == 0 */ { | ||
703 | /* ??? not defined in LZX specification! */ | ||
704 | match_offset = 1; | ||
705 | } | ||
706 | /* update repeated offset LRU queue */ | ||
707 | R2 = R1; R1 = R0; R0 = match_offset; | ||
708 | } | ||
709 | |||
710 | /* LZX DELTA uses max match length to signal even longer match */ | ||
711 | if (match_length == LZX_MAX_MATCH && lzx->is_delta) { | ||
712 | int extra_len = 0; | ||
713 | ENSURE_BITS(3); /* 4 entry huffman tree */ | ||
714 | if (PEEK_BITS(1) == 0) { | ||
715 | REMOVE_BITS(1); /* '0' -> 8 extra length bits */ | ||
716 | READ_BITS(extra_len, 8); | ||
717 | } | ||
718 | else if (PEEK_BITS(2) == 2) { | ||
719 | REMOVE_BITS(2); /* '10' -> 10 extra length bits + 0x100 */ | ||
720 | READ_BITS(extra_len, 10); | ||
721 | extra_len += 0x100; | ||
722 | } | ||
723 | else if (PEEK_BITS(3) == 6) { | ||
724 | REMOVE_BITS(3); /* '110' -> 12 extra length bits + 0x500 */ | ||
725 | READ_BITS(extra_len, 12); | ||
726 | extra_len += 0x500; | ||
727 | } | ||
728 | else { | ||
729 | REMOVE_BITS(3); /* '111' -> 15 extra length bits */ | ||
730 | READ_BITS(extra_len, 15); | ||
731 | } | ||
732 | match_length += extra_len; | ||
733 | } | ||
734 | |||
735 | if ((window_posn + match_length) > lzx->window_size) { | ||
736 | D(("match ran over window wrap")) | ||
737 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
738 | } | ||
739 | |||
740 | /* copy match */ | ||
741 | rundest = &window[window_posn]; | ||
742 | i = match_length; | ||
743 | /* does match offset wrap the window? */ | ||
744 | if (match_offset > window_posn) { | ||
745 | if (match_offset > lzx->offset && | ||
746 | (match_offset - window_posn) > lzx->ref_data_size) | ||
747 | { | ||
748 | D(("match offset beyond LZX stream")) | ||
749 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
750 | } | ||
751 | /* j = length from match offset to end of window */ | ||
752 | j = match_offset - window_posn; | ||
753 | if (j > (int) lzx->window_size) { | ||
754 | D(("match offset beyond window boundaries")) | ||
755 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
756 | } | ||
757 | runsrc = &window[lzx->window_size - j]; | ||
758 | if (j < i) { | ||
759 | /* if match goes over the window edge, do two copy runs */ | ||
760 | i -= j; while (j-- > 0) *rundest++ = *runsrc++; | ||
761 | runsrc = window; | ||
762 | } | ||
763 | while (i-- > 0) *rundest++ = *runsrc++; | ||
764 | } | ||
765 | else { | ||
766 | runsrc = rundest - match_offset; | ||
767 | while (i-- > 0) *rundest++ = *runsrc++; | ||
768 | } | ||
769 | |||
770 | this_run -= match_length; | ||
771 | window_posn += match_length; | ||
772 | } | ||
773 | } /* while (this_run > 0) */ | ||
774 | break; | ||
775 | |||
776 | case LZX_BLOCKTYPE_UNCOMPRESSED: | ||
777 | /* as this_run is limited not to wrap a frame, this also means it | ||
778 | * won't wrap the window (as the window is a multiple of 32k) */ | ||
779 | rundest = &window[window_posn]; | ||
780 | window_posn += this_run; | ||
781 | while (this_run > 0) { | ||
782 | if ((i = i_end - i_ptr) == 0) { | ||
783 | READ_IF_NEEDED; | ||
784 | } | ||
785 | else { | ||
786 | if (i > this_run) i = this_run; | ||
787 | lzx->sys->copy(i_ptr, rundest, (size_t) i); | ||
788 | rundest += i; | ||
789 | i_ptr += i; | ||
790 | this_run -= i; | ||
791 | } | ||
792 | } | ||
793 | break; | ||
794 | |||
795 | default: | ||
796 | return lzx->error = MSPACK_ERR_DECRUNCH; /* might as well */ | ||
797 | } | ||
798 | |||
799 | /* did the final match overrun our desired this_run length? */ | ||
800 | if (this_run < 0) { | ||
801 | if ((unsigned int)(-this_run) > lzx->block_remaining) { | ||
802 | D(("overrun went past end of block by %d (%d remaining)", | ||
803 | -this_run, lzx->block_remaining )) | ||
804 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
805 | } | ||
806 | lzx->block_remaining -= -this_run; | ||
807 | } | ||
808 | } /* while (bytes_todo > 0) */ | ||
809 | |||
810 | /* streams don't extend over frame boundaries */ | ||
811 | if ((window_posn - lzx->frame_posn) != frame_size) { | ||
812 | D(("decode beyond output frame limits! %d != %d", | ||
813 | window_posn - lzx->frame_posn, frame_size)) | ||
814 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
815 | } | ||
816 | |||
817 | /* re-align input bitstream */ | ||
818 | if (bits_left > 0) ENSURE_BITS(16); | ||
819 | if (bits_left & 15) REMOVE_BITS(bits_left & 15); | ||
820 | |||
821 | /* check that we've used all of the previous frame first */ | ||
822 | if (lzx->o_ptr != lzx->o_end) { | ||
823 | D(("%ld avail bytes, new %d frame", | ||
824 | (long)(lzx->o_end - lzx->o_ptr), frame_size)) | ||
825 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
826 | } | ||
827 | |||
828 | /* does this intel block _really_ need decoding? */ | ||
829 | if (lzx->intel_started && lzx->intel_filesize && | ||
830 | (lzx->frame <= 32768) && (frame_size > 10)) | ||
831 | { | ||
832 | unsigned char *data = &lzx->e8_buf[0]; | ||
833 | unsigned char *dataend = &lzx->e8_buf[frame_size - 10]; | ||
834 | signed int curpos = lzx->intel_curpos; | ||
835 | signed int filesize = lzx->intel_filesize; | ||
836 | signed int abs_off, rel_off; | ||
837 | |||
838 | /* copy e8 block to the e8 buffer and tweak if needed */ | ||
839 | lzx->o_ptr = data; | ||
840 | lzx->sys->copy(&lzx->window[lzx->frame_posn], data, frame_size); | ||
841 | |||
842 | while (data < dataend) { | ||
843 | if (*data++ != 0xE8) { curpos++; continue; } | ||
844 | abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); | ||
845 | if ((abs_off >= -curpos) && (abs_off < filesize)) { | ||
846 | rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; | ||
847 | data[0] = (unsigned char) rel_off; | ||
848 | data[1] = (unsigned char) (rel_off >> 8); | ||
849 | data[2] = (unsigned char) (rel_off >> 16); | ||
850 | data[3] = (unsigned char) (rel_off >> 24); | ||
851 | } | ||
852 | data += 4; | ||
853 | curpos += 5; | ||
854 | } | ||
855 | lzx->intel_curpos += frame_size; | ||
856 | } | ||
857 | else { | ||
858 | lzx->o_ptr = &lzx->window[lzx->frame_posn]; | ||
859 | if (lzx->intel_filesize) lzx->intel_curpos += frame_size; | ||
860 | } | ||
861 | lzx->o_end = &lzx->o_ptr[frame_size]; | ||
862 | |||
863 | /* write a frame */ | ||
864 | i = (out_bytes < (off_t)frame_size) ? (unsigned int)out_bytes : frame_size; | ||
865 | if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) { | ||
866 | return lzx->error = MSPACK_ERR_WRITE; | ||
867 | } | ||
868 | lzx->o_ptr += i; | ||
869 | lzx->offset += i; | ||
870 | out_bytes -= i; | ||
871 | |||
872 | /* advance frame start position */ | ||
873 | lzx->frame_posn += frame_size; | ||
874 | lzx->frame++; | ||
875 | |||
876 | /* wrap window / frame position pointers */ | ||
877 | if (window_posn == lzx->window_size) window_posn = 0; | ||
878 | if (lzx->frame_posn == lzx->window_size) lzx->frame_posn = 0; | ||
879 | |||
880 | } /* while (lzx->frame < end_frame) */ | ||
881 | |||
882 | if (out_bytes) { | ||
883 | D(("bytes left to output")) | ||
884 | return lzx->error = MSPACK_ERR_DECRUNCH; | ||
885 | } | ||
886 | |||
887 | /* store local state */ | ||
888 | STORE_BITS; | ||
889 | lzx->window_posn = window_posn; | ||
890 | lzx->R0 = R0; | ||
891 | lzx->R1 = R1; | ||
892 | lzx->R2 = R2; | ||
893 | |||
894 | return MSPACK_ERR_OK; | ||
895 | } | ||
896 | |||
897 | void lzxd_free(struct lzxd_stream *lzx) { | ||
898 | struct mspack_system *sys; | ||
899 | if (lzx) { | ||
900 | sys = lzx->sys; | ||
901 | sys->free(lzx->inbuf); | ||
902 | sys->free(lzx->window); | ||
903 | sys->free(lzx); | ||
904 | } | ||
905 | } | ||
diff --git a/utils/rbutilqt/mspack/mspack.h b/utils/rbutilqt/mspack/mspack.h new file mode 100644 index 0000000000..3e99624463 --- /dev/null +++ b/utils/rbutilqt/mspack/mspack.h | |||
@@ -0,0 +1,2385 @@ | |||
1 | /* libmspack -- a library for working with Microsoft compression formats. | ||
2 | * (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk> | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU Lesser General Public License for more details. | ||
11 | * | ||
12 | * You should have received a copy of the GNU Lesser General Public License | ||
13 | * along with this program; if not, write to the Free Software | ||
14 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
15 | */ | ||
16 | |||
17 | /** \mainpage | ||
18 | * | ||
19 | * \section intro Introduction | ||
20 | * | ||
21 | * libmspack is a library which provides compressors and decompressors, | ||
22 | * archivers and dearchivers for Microsoft compression formats. | ||
23 | * | ||
24 | * \section formats Formats supported | ||
25 | * | ||
26 | * The following file formats are supported: | ||
27 | * - SZDD files, which use LZSS compression | ||
28 | * - KWAJ files, which use LZSS, LZSS+Huffman or deflate compression | ||
29 | * - .HLP (MS Help) files, which use LZSS compression | ||
30 | * - .CAB (MS Cabinet) files, which use deflate, LZX or Quantum compression | ||
31 | * - .CHM (HTML Help) files, which use LZX compression | ||
32 | * - .LIT (MS EBook) files, which use LZX compression and DES encryption | ||
33 | * - .LZX (Exchange Offline Addressbook) files, which use LZX compression | ||
34 | * | ||
35 | * To determine the capabilities of the library, and the binary | ||
36 | * compatibility version of any particular compressor or decompressor, use | ||
37 | * the mspack_version() function. The UNIX library interface version is | ||
38 | * defined as the highest-versioned library component. | ||
39 | * | ||
40 | * \section starting Getting started | ||
41 | * | ||
42 | * The macro MSPACK_SYS_SELFTEST() should be used to ensure the library can | ||
43 | * be used. In particular, it checks if the caller is using 32-bit file I/O | ||
44 | * when the library is compiled for 64-bit file I/O and vice versa. | ||
45 | * | ||
46 | * If compiled normally, the library includes basic file I/O and memory | ||
47 | * management functionality using the standard C library. This can be | ||
48 | * customised and replaced entirely by creating a mspack_system structure. | ||
49 | * | ||
50 | * A compressor or decompressor for the required format must be | ||
51 | * instantiated before it can be used. Each construction function takes | ||
52 | * one parameter, which is either a pointer to a custom mspack_system | ||
53 | * structure, or NULL to use the default. The instantiation returned, if | ||
54 | * not NULL, contains function pointers (methods) to work with the given | ||
55 | * file format. | ||
56 | * | ||
57 | * For compression: | ||
58 | * - mspack_create_cab_compressor() creates a mscab_compressor | ||
59 | * - mspack_create_chm_compressor() creates a mschm_compressor | ||
60 | * - mspack_create_lit_compressor() creates a mslit_compressor | ||
61 | * - mspack_create_hlp_compressor() creates a mshlp_compressor | ||
62 | * - mspack_create_szdd_compressor() creates a msszdd_compressor | ||
63 | * - mspack_create_kwaj_compressor() creates a mskwaj_compressor | ||
64 | * - mspack_create_oab_compressor() creates a msoab_compressor | ||
65 | * | ||
66 | * For decompression: | ||
67 | * - mspack_create_cab_decompressor() creates a mscab_decompressor | ||
68 | * - mspack_create_chm_decompressor() creates a mschm_decompressor | ||
69 | * - mspack_create_lit_decompressor() creates a mslit_decompressor | ||
70 | * - mspack_create_hlp_decompressor() creates a mshlp_decompressor | ||
71 | * - mspack_create_szdd_decompressor() creates a msszdd_decompressor | ||
72 | * - mspack_create_kwaj_decompressor() creates a mskwaj_decompressor | ||
73 | * - mspack_create_oab_decompressor() creates a msoab_decompressor | ||
74 | * | ||
75 | * Once finished working with a format, each kind of | ||
76 | * compressor/decompressor has its own specific destructor: | ||
77 | * - mspack_destroy_cab_compressor() | ||
78 | * - mspack_destroy_cab_decompressor() | ||
79 | * - mspack_destroy_chm_compressor() | ||
80 | * - mspack_destroy_chm_decompressor() | ||
81 | * - mspack_destroy_lit_compressor() | ||
82 | * - mspack_destroy_lit_decompressor() | ||
83 | * - mspack_destroy_hlp_compressor() | ||
84 | * - mspack_destroy_hlp_decompressor() | ||
85 | * - mspack_destroy_szdd_compressor() | ||
86 | * - mspack_destroy_szdd_decompressor() | ||
87 | * - mspack_destroy_kwaj_compressor() | ||
88 | * - mspack_destroy_kwaj_decompressor() | ||
89 | * - mspack_destroy_oab_compressor() | ||
90 | * - mspack_destroy_oab_decompressor() | ||
91 | * | ||
92 | * Destroying a compressor or decompressor does not destroy any objects, | ||
93 | * structures or handles that have been created using that compressor or | ||
94 | * decompressor. Ensure that everything created or opened is destroyed or | ||
95 | * closed before compressor/decompressor is itself destroyed. | ||
96 | * | ||
97 | * \section errors Error codes | ||
98 | * | ||
99 | * All compressors and decompressors use the same set of error codes. Most | ||
100 | * methods return an error code directly. For methods which do not | ||
101 | * return error codes directly, the error code can be obtained with the | ||
102 | * last_error() method. | ||
103 | * | ||
104 | * - #MSPACK_ERR_OK is used to indicate success. This error code is defined | ||
105 | * as zero, all other code are non-zero. | ||
106 | * - #MSPACK_ERR_ARGS indicates that a method was called with inappropriate | ||
107 | * arguments. | ||
108 | * - #MSPACK_ERR_OPEN indicates that mspack_system::open() failed. | ||
109 | * - #MSPACK_ERR_READ indicates that mspack_system::read() failed. | ||
110 | * - #MSPACK_ERR_WRITE indicates that mspack_system::write() failed. | ||
111 | * - #MSPACK_ERR_SEEK indicates that mspack_system::seek() failed. | ||
112 | * - #MSPACK_ERR_NOMEMORY indicates that mspack_system::alloc() failed. | ||
113 | * - #MSPACK_ERR_SIGNATURE indicates that the file being read does not | ||
114 | * have the correct "signature". It is probably not a valid file for | ||
115 | * whatever format is being read. | ||
116 | * - #MSPACK_ERR_DATAFORMAT indicates that the file being used or read | ||
117 | * is corrupt. | ||
118 | * - #MSPACK_ERR_CHECKSUM indicates that a data checksum has failed. | ||
119 | * - #MSPACK_ERR_CRUNCH indicates an error occured during compression. | ||
120 | * - #MSPACK_ERR_DECRUNCH indicates an error occured during decompression. | ||
121 | * | ||
122 | * \section threading Multi-threading | ||
123 | * | ||
124 | * libmspack methods are reentrant and multithreading-safe when each | ||
125 | * thread has its own compressor or decompressor. | ||
126 | |||
127 | * You should not call multiple methods simultaneously on a single | ||
128 | * compressor or decompressor instance. | ||
129 | * | ||
130 | * If this may happen, you can either use one compressor or | ||
131 | * decompressor per thread, or you can use your preferred lock, | ||
132 | * semaphore or mutex library to ensure no more than one method on a | ||
133 | * compressor/decompressor is called simultaneously. libmspack will | ||
134 | * not do this locking for you. | ||
135 | * | ||
136 | * Example of incorrect behaviour: | ||
137 | * - thread 1 calls mspack_create_cab_decompressor() | ||
138 | * - thread 1 calls open() | ||
139 | * - thread 1 calls extract() for one file | ||
140 | * - thread 2 simultaneously calls extract() for another file | ||
141 | * | ||
142 | * Correct behaviour: | ||
143 | * - thread 1 calls mspack_create_cab_decompressor() | ||
144 | * - thread 2 calls mspack_create_cab_decompressor() | ||
145 | * - thread 1 calls its own open() / extract() | ||
146 | * - thread 2 simultaneously calls its own open() / extract() | ||
147 | * | ||
148 | * Also correct behaviour: | ||
149 | * - thread 1 calls mspack_create_cab_decompressor() | ||
150 | * - thread 1 locks a mutex for with the decompressor before | ||
151 | * calling any methods on it, and unlocks the mutex after each | ||
152 | * method returns. | ||
153 | * - thread 1 can share the results of open() with thread 2, and both | ||
154 | * can call extract(), provided they both guard against simultaneous | ||
155 | * use of extract(), and any other methods, with the mutex | ||
156 | */ | ||
157 | |||
158 | #ifndef LIB_MSPACK_H | ||
159 | #define LIB_MSPACK_H 1 | ||
160 | |||
161 | #ifdef __cplusplus | ||
162 | extern "C" { | ||
163 | #endif | ||
164 | |||
165 | #include <sys/types.h> | ||
166 | #include <stdlib.h> | ||
167 | |||
168 | /** | ||
169 | * System self-test function, to ensure both library and calling program | ||
170 | * can use one another. | ||
171 | * | ||
172 | * A result of MSPACK_ERR_OK means the library and caller are | ||
173 | * compatible. Any other result indicates that the library and caller are | ||
174 | * not compatible and should not be used. In particular, a value of | ||
175 | * MSPACK_ERR_SEEK means the library and caller use different off_t | ||
176 | * datatypes. | ||
177 | * | ||
178 | * It should be used like so: | ||
179 | * | ||
180 | * @code | ||
181 | * int selftest_result; | ||
182 | * MSPACK_SYS_SELFTEST(selftest_result); | ||
183 | * if (selftest_result != MSPACK_ERR_OK) { | ||
184 | * fprintf(stderr, "incompatible with this build of libmspack\n"); | ||
185 | * exit(0); | ||
186 | * } | ||
187 | * @endcode | ||
188 | * | ||
189 | * @param result an int variable to store the result of the self-test | ||
190 | */ | ||
191 | #define MSPACK_SYS_SELFTEST(result) do { \ | ||
192 | (result) = mspack_sys_selftest_internal(sizeof(off_t)); \ | ||
193 | } while (0) | ||
194 | |||
195 | /** Part of the MSPACK_SYS_SELFTEST() macro, must not be used directly. */ | ||
196 | extern int mspack_sys_selftest_internal(int); | ||
197 | |||
198 | /** | ||
199 | * Enquire about the binary compatibility version of a specific interface in | ||
200 | * the library. Currently, the following interfaces are defined: | ||
201 | * | ||
202 | * - #MSPACK_VER_LIBRARY: the overall library | ||
203 | * - #MSPACK_VER_SYSTEM: the mspack_system interface | ||
204 | * - #MSPACK_VER_MSCABD: the mscab_decompressor interface | ||
205 | * - #MSPACK_VER_MSCABC: the mscab_compressor interface | ||
206 | * - #MSPACK_VER_MSCHMD: the mschm_decompressor interface | ||
207 | * - #MSPACK_VER_MSCHMC: the mschm_compressor interface | ||
208 | * - #MSPACK_VER_MSLITD: the mslit_decompressor interface | ||
209 | * - #MSPACK_VER_MSLITC: the mslit_compressor interface | ||
210 | * - #MSPACK_VER_MSHLPD: the mshlp_decompressor interface | ||
211 | * - #MSPACK_VER_MSHLPC: the mshlp_compressor interface | ||
212 | * - #MSPACK_VER_MSSZDDD: the msszdd_decompressor interface | ||
213 | * - #MSPACK_VER_MSSZDDC: the msszdd_compressor interface | ||
214 | * - #MSPACK_VER_MSKWAJD: the mskwaj_decompressor interface | ||
215 | * - #MSPACK_VER_MSKWAJC: the mskwaj_compressor interface | ||
216 | * - #MSPACK_VER_MSOABD: the msoab_decompressor interface | ||
217 | * - #MSPACK_VER_MSOABC: the msoab_compressor interface | ||
218 | * | ||
219 | * The result of the function should be interpreted as follows: | ||
220 | * - -1: this interface is completely unknown to the library | ||
221 | * - 0: this interface is known, but non-functioning | ||
222 | * - 1: this interface has all basic functionality | ||
223 | * - 2, 3, ...: this interface has additional functionality, clearly marked | ||
224 | * in the documentation as "version 2", "version 3" and so on. | ||
225 | * | ||
226 | * @param entity the interface to request current version of | ||
227 | * @return the version of the requested interface | ||
228 | */ | ||
229 | extern int mspack_version(int entity); | ||
230 | |||
231 | /** Pass to mspack_version() to get the overall library version */ | ||
232 | #define MSPACK_VER_LIBRARY (0) | ||
233 | /** Pass to mspack_version() to get the mspack_system version */ | ||
234 | #define MSPACK_VER_SYSTEM (1) | ||
235 | /** Pass to mspack_version() to get the mscab_decompressor version */ | ||
236 | #define MSPACK_VER_MSCABD (2) | ||
237 | /** Pass to mspack_version() to get the mscab_compressor version */ | ||
238 | #define MSPACK_VER_MSCABC (3) | ||
239 | /** Pass to mspack_version() to get the mschm_decompressor version */ | ||
240 | #define MSPACK_VER_MSCHMD (4) | ||
241 | /** Pass to mspack_version() to get the mschm_compressor version */ | ||
242 | #define MSPACK_VER_MSCHMC (5) | ||
243 | /** Pass to mspack_version() to get the mslit_decompressor version */ | ||
244 | #define MSPACK_VER_MSLITD (6) | ||
245 | /** Pass to mspack_version() to get the mslit_compressor version */ | ||
246 | #define MSPACK_VER_MSLITC (7) | ||
247 | /** Pass to mspack_version() to get the mshlp_decompressor version */ | ||
248 | #define MSPACK_VER_MSHLPD (8) | ||
249 | /** Pass to mspack_version() to get the mshlp_compressor version */ | ||
250 | #define MSPACK_VER_MSHLPC (9) | ||
251 | /** Pass to mspack_version() to get the msszdd_decompressor version */ | ||
252 | #define MSPACK_VER_MSSZDDD (10) | ||
253 | /** Pass to mspack_version() to get the msszdd_compressor version */ | ||
254 | #define MSPACK_VER_MSSZDDC (11) | ||
255 | /** Pass to mspack_version() to get the mskwaj_decompressor version */ | ||
256 | #define MSPACK_VER_MSKWAJD (12) | ||
257 | /** Pass to mspack_version() to get the mskwaj_compressor version */ | ||
258 | #define MSPACK_VER_MSKWAJC (13) | ||
259 | /** Pass to mspack_version() to get the msoab_decompressor version */ | ||
260 | #define MSPACK_VER_MSOABD (14) | ||
261 | /** Pass to mspack_version() to get the msoab_compressor version */ | ||
262 | #define MSPACK_VER_MSOABC (15) | ||
263 | |||
264 | /* --- file I/O abstraction ------------------------------------------------ */ | ||
265 | |||
266 | /** | ||
267 | * A structure which abstracts file I/O and memory management. | ||
268 | * | ||
269 | * The library always uses the mspack_system structure for interaction | ||
270 | * with the file system and to allocate, free and copy all memory. It also | ||
271 | * uses it to send literal messages to the library user. | ||
272 | * | ||
273 | * When the library is compiled normally, passing NULL to a compressor or | ||
274 | * decompressor constructor will result in a default mspack_system being | ||
275 | * used, where all methods are implemented with the standard C library. | ||
276 | * However, all constructors support being given a custom created | ||
277 | * mspack_system structure, with the library user's own methods. This | ||
278 | * allows for more abstract interaction, such as reading and writing files | ||
279 | * directly to memory, or from a network socket or pipe. | ||
280 | * | ||
281 | * Implementors of an mspack_system structure should read all | ||
282 | * documentation entries for every structure member, and write methods | ||
283 | * which conform to those standards. | ||
284 | */ | ||
285 | struct mspack_system { | ||
286 | /** | ||
287 | * Opens a file for reading, writing, appending or updating. | ||
288 | * | ||
289 | * @param self a self-referential pointer to the mspack_system | ||
290 | * structure whose open() method is being called. If | ||
291 | * this pointer is required by close(), read(), write(), | ||
292 | * seek() or tell(), it should be stored in the result | ||
293 | * structure at this time. | ||
294 | * @param filename the file to be opened. It is passed directly from the | ||
295 | * library caller without being modified, so it is up to | ||
296 | * the caller what this parameter actually represents. | ||
297 | * @param mode one of #MSPACK_SYS_OPEN_READ (open an existing file | ||
298 | * for reading), #MSPACK_SYS_OPEN_WRITE (open a new file | ||
299 | * for writing), #MSPACK_SYS_OPEN_UPDATE (open an existing | ||
300 | * file for reading/writing from the start of the file) or | ||
301 | * #MSPACK_SYS_OPEN_APPEND (open an existing file for | ||
302 | * reading/writing from the end of the file) | ||
303 | * @return a pointer to a mspack_file structure. This structure officially | ||
304 | * contains no members, its true contents are up to the | ||
305 | * mspack_system implementor. It should contain whatever is needed | ||
306 | * for other mspack_system methods to operate. Returning the NULL | ||
307 | * pointer indicates an error condition. | ||
308 | * @see close(), read(), write(), seek(), tell(), message() | ||
309 | */ | ||
310 | struct mspack_file * (*open)(struct mspack_system *self, | ||
311 | const char *filename, | ||
312 | int mode); | ||
313 | |||
314 | /** | ||
315 | * Closes a previously opened file. If any memory was allocated for this | ||
316 | * particular file handle, it should be freed at this time. | ||
317 | * | ||
318 | * @param file the file to close | ||
319 | * @see open() | ||
320 | */ | ||
321 | void (*close)(struct mspack_file *file); | ||
322 | |||
323 | /** | ||
324 | * Reads a given number of bytes from an open file. | ||
325 | * | ||
326 | * @param file the file to read from | ||
327 | * @param buffer the location where the read bytes should be stored | ||
328 | * @param bytes the number of bytes to read from the file. | ||
329 | * @return the number of bytes successfully read (this can be less than | ||
330 | * the number requested), zero to mark the end of file, or less | ||
331 | * than zero to indicate an error. The library does not "retry" | ||
332 | * reads and assumes short reads are due to EOF, so you should | ||
333 | * avoid returning short reads because of transient errors. | ||
334 | * @see open(), write() | ||
335 | */ | ||
336 | int (*read)(struct mspack_file *file, | ||
337 | void *buffer, | ||
338 | int bytes); | ||
339 | |||
340 | /** | ||
341 | * Writes a given number of bytes to an open file. | ||
342 | * | ||
343 | * @param file the file to write to | ||
344 | * @param buffer the location where the written bytes should be read from | ||
345 | * @param bytes the number of bytes to write to the file. | ||
346 | * @return the number of bytes successfully written, this can be less | ||
347 | * than the number requested. Zero or less can indicate an error | ||
348 | * where no bytes at all could be written. All cases where less | ||
349 | * bytes were written than requested are considered by the library | ||
350 | * to be an error. | ||
351 | * @see open(), read() | ||
352 | */ | ||
353 | int (*write)(struct mspack_file *file, | ||
354 | void *buffer, | ||
355 | int bytes); | ||
356 | |||
357 | /** | ||
358 | * Seeks to a specific file offset within an open file. | ||
359 | * | ||
360 | * Sometimes the library needs to know the length of a file. It does | ||
361 | * this by seeking to the end of the file with seek(file, 0, | ||
362 | * MSPACK_SYS_SEEK_END), then calling tell(). Implementations may want | ||
363 | * to make a special case for this. | ||
364 | * | ||
365 | * Due to the potentially varying 32/64 bit datatype off_t on some | ||
366 | * architectures, the #MSPACK_SYS_SELFTEST macro MUST be used before | ||
367 | * using the library. If not, the error caused by the library passing an | ||
368 | * inappropriate stackframe to seek() is subtle and hard to trace. | ||
369 | * | ||
370 | * @param file the file to be seeked | ||
371 | * @param offset an offset to seek, measured in bytes | ||
372 | * @param mode one of #MSPACK_SYS_SEEK_START (the offset should be | ||
373 | * measured from the start of the file), #MSPACK_SYS_SEEK_CUR | ||
374 | * (the offset should be measured from the current file offset) | ||
375 | * or #MSPACK_SYS_SEEK_END (the offset should be measured from | ||
376 | * the end of the file) | ||
377 | * @return zero for success, non-zero for an error | ||
378 | * @see open(), tell() | ||
379 | */ | ||
380 | int (*seek)(struct mspack_file *file, | ||
381 | off_t offset, | ||
382 | int mode); | ||
383 | |||
384 | /** | ||
385 | * Returns the current file position (in bytes) of the given file. | ||
386 | * | ||
387 | * @param file the file whose file position is wanted | ||
388 | * @return the current file position of the file | ||
389 | * @see open(), seek() | ||
390 | */ | ||
391 | off_t (*tell)(struct mspack_file *file); | ||
392 | |||
393 | /** | ||
394 | * Used to send messages from the library to the user. | ||
395 | * | ||
396 | * Occasionally, the library generates warnings or other messages in | ||
397 | * plain english to inform the human user. These are informational only | ||
398 | * and can be ignored if not wanted. | ||
399 | * | ||
400 | * @param file may be a file handle returned from open() if this message | ||
401 | * pertains to a specific open file, or NULL if not related to | ||
402 | * a specific file. | ||
403 | * @param format a printf() style format string. It does NOT include a | ||
404 | * trailing newline. | ||
405 | * @see open() | ||
406 | */ | ||
407 | void (*message)(struct mspack_file *file, | ||
408 | const char *format, | ||
409 | ...); | ||
410 | |||
411 | /** | ||
412 | * Allocates memory. | ||
413 | * | ||
414 | * @param self a self-referential pointer to the mspack_system | ||
415 | * structure whose alloc() method is being called. | ||
416 | * @param bytes the number of bytes to allocate | ||
417 | * @result a pointer to the requested number of bytes, or NULL if | ||
418 | * not enough memory is available | ||
419 | * @see free() | ||
420 | */ | ||
421 | void * (*alloc)(struct mspack_system *self, | ||
422 | size_t bytes); | ||
423 | |||
424 | /** | ||
425 | * Frees memory. | ||
426 | * | ||
427 | * @param ptr the memory to be freed. NULL is accepted and ignored. | ||
428 | * @see alloc() | ||
429 | */ | ||
430 | void (*free)(void *ptr); | ||
431 | |||
432 | /** | ||
433 | * Copies from one region of memory to another. | ||
434 | * | ||
435 | * The regions of memory are guaranteed not to overlap, are usually less | ||
436 | * than 256 bytes, and may not be aligned. Please note that the source | ||
437 | * parameter comes before the destination parameter, unlike the standard | ||
438 | * C function memcpy(). | ||
439 | * | ||
440 | * @param src the region of memory to copy from | ||
441 | * @param dest the region of memory to copy to | ||
442 | * @param bytes the size of the memory region, in bytes | ||
443 | */ | ||
444 | void (*copy)(void *src, | ||
445 | void *dest, | ||
446 | size_t bytes); | ||
447 | |||
448 | /** | ||
449 | * A null pointer to mark the end of mspack_system. It must equal NULL. | ||
450 | * | ||
451 | * Should the mspack_system structure extend in the future, this NULL | ||
452 | * will be seen, rather than have an invalid method pointer called. | ||
453 | */ | ||
454 | void *null_ptr; | ||
455 | }; | ||
456 | |||
457 | /** mspack_system::open() mode: open existing file for reading. */ | ||
458 | #define MSPACK_SYS_OPEN_READ (0) | ||
459 | /** mspack_system::open() mode: open new file for writing */ | ||
460 | #define MSPACK_SYS_OPEN_WRITE (1) | ||
461 | /** mspack_system::open() mode: open existing file for writing */ | ||
462 | #define MSPACK_SYS_OPEN_UPDATE (2) | ||
463 | /** mspack_system::open() mode: open existing file for writing */ | ||
464 | #define MSPACK_SYS_OPEN_APPEND (3) | ||
465 | |||
466 | /** mspack_system::seek() mode: seek relative to start of file */ | ||
467 | #define MSPACK_SYS_SEEK_START (0) | ||
468 | /** mspack_system::seek() mode: seek relative to current offset */ | ||
469 | #define MSPACK_SYS_SEEK_CUR (1) | ||
470 | /** mspack_system::seek() mode: seek relative to end of file */ | ||
471 | #define MSPACK_SYS_SEEK_END (2) | ||
472 | |||
473 | /** | ||
474 | * A structure which represents an open file handle. The contents of this | ||
475 | * structure are determined by the implementation of the | ||
476 | * mspack_system::open() method. | ||
477 | */ | ||
478 | struct mspack_file { | ||
479 | int dummy; | ||
480 | }; | ||
481 | |||
482 | /* --- error codes --------------------------------------------------------- */ | ||
483 | |||
484 | /** Error code: no error */ | ||
485 | #define MSPACK_ERR_OK (0) | ||
486 | /** Error code: bad arguments to method */ | ||
487 | #define MSPACK_ERR_ARGS (1) | ||
488 | /** Error code: error opening file */ | ||
489 | #define MSPACK_ERR_OPEN (2) | ||
490 | /** Error code: error reading file */ | ||
491 | #define MSPACK_ERR_READ (3) | ||
492 | /** Error code: error writing file */ | ||
493 | #define MSPACK_ERR_WRITE (4) | ||
494 | /** Error code: seek error */ | ||
495 | #define MSPACK_ERR_SEEK (5) | ||
496 | /** Error code: out of memory */ | ||
497 | #define MSPACK_ERR_NOMEMORY (6) | ||
498 | /** Error code: bad "magic id" in file */ | ||
499 | #define MSPACK_ERR_SIGNATURE (7) | ||
500 | /** Error code: bad or corrupt file format */ | ||
501 | #define MSPACK_ERR_DATAFORMAT (8) | ||
502 | /** Error code: bad checksum or CRC */ | ||
503 | #define MSPACK_ERR_CHECKSUM (9) | ||
504 | /** Error code: error during compression */ | ||
505 | #define MSPACK_ERR_CRUNCH (10) | ||
506 | /** Error code: error during decompression */ | ||
507 | #define MSPACK_ERR_DECRUNCH (11) | ||
508 | |||
509 | /* --- functions available in library -------------------------------------- */ | ||
510 | |||
511 | /** Creates a new CAB compressor. | ||
512 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
513 | * @return a #mscab_compressor or NULL | ||
514 | */ | ||
515 | extern struct mscab_compressor * | ||
516 | mspack_create_cab_compressor(struct mspack_system *sys); | ||
517 | |||
518 | /** Creates a new CAB decompressor. | ||
519 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
520 | * @return a #mscab_decompressor or NULL | ||
521 | */ | ||
522 | extern struct mscab_decompressor * | ||
523 | mspack_create_cab_decompressor(struct mspack_system *sys); | ||
524 | |||
525 | /** Destroys an existing CAB compressor. | ||
526 | * @param self the #mscab_compressor to destroy | ||
527 | */ | ||
528 | extern void mspack_destroy_cab_compressor(struct mscab_compressor *self); | ||
529 | |||
530 | /** Destroys an existing CAB decompressor. | ||
531 | * @param self the #mscab_decompressor to destroy | ||
532 | */ | ||
533 | extern void mspack_destroy_cab_decompressor(struct mscab_decompressor *self); | ||
534 | |||
535 | |||
536 | /** Creates a new CHM compressor. | ||
537 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
538 | * @return a #mschm_compressor or NULL | ||
539 | */ | ||
540 | extern struct mschm_compressor * | ||
541 | mspack_create_chm_compressor(struct mspack_system *sys); | ||
542 | |||
543 | /** Creates a new CHM decompressor. | ||
544 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
545 | * @return a #mschm_decompressor or NULL | ||
546 | */ | ||
547 | extern struct mschm_decompressor * | ||
548 | mspack_create_chm_decompressor(struct mspack_system *sys); | ||
549 | |||
550 | /** Destroys an existing CHM compressor. | ||
551 | * @param self the #mschm_compressor to destroy | ||
552 | */ | ||
553 | extern void mspack_destroy_chm_compressor(struct mschm_compressor *self); | ||
554 | |||
555 | /** Destroys an existing CHM decompressor. | ||
556 | * @param self the #mschm_decompressor to destroy | ||
557 | */ | ||
558 | extern void mspack_destroy_chm_decompressor(struct mschm_decompressor *self); | ||
559 | |||
560 | |||
561 | /** Creates a new LIT compressor. | ||
562 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
563 | * @return a #mslit_compressor or NULL | ||
564 | */ | ||
565 | extern struct mslit_compressor * | ||
566 | mspack_create_lit_compressor(struct mspack_system *sys); | ||
567 | |||
568 | /** Creates a new LIT decompressor. | ||
569 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
570 | * @return a #mslit_decompressor or NULL | ||
571 | */ | ||
572 | extern struct mslit_decompressor * | ||
573 | mspack_create_lit_decompressor(struct mspack_system *sys); | ||
574 | |||
575 | /** Destroys an existing LIT compressor. | ||
576 | * @param self the #mslit_compressor to destroy | ||
577 | */ | ||
578 | extern void mspack_destroy_lit_compressor(struct mslit_compressor *self); | ||
579 | |||
580 | /** Destroys an existing LIT decompressor. | ||
581 | * @param self the #mslit_decompressor to destroy | ||
582 | */ | ||
583 | extern void mspack_destroy_lit_decompressor(struct mslit_decompressor *self); | ||
584 | |||
585 | |||
586 | /** Creates a new HLP compressor. | ||
587 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
588 | * @return a #mshlp_compressor or NULL | ||
589 | */ | ||
590 | extern struct mshlp_compressor * | ||
591 | mspack_create_hlp_compressor(struct mspack_system *sys); | ||
592 | |||
593 | /** Creates a new HLP decompressor. | ||
594 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
595 | * @return a #mshlp_decompressor or NULL | ||
596 | */ | ||
597 | extern struct mshlp_decompressor * | ||
598 | mspack_create_hlp_decompressor(struct mspack_system *sys); | ||
599 | |||
600 | /** Destroys an existing hlp compressor. | ||
601 | * @param self the #mshlp_compressor to destroy | ||
602 | */ | ||
603 | extern void mspack_destroy_hlp_compressor(struct mshlp_compressor *self); | ||
604 | |||
605 | /** Destroys an existing hlp decompressor. | ||
606 | * @param self the #mshlp_decompressor to destroy | ||
607 | */ | ||
608 | extern void mspack_destroy_hlp_decompressor(struct mshlp_decompressor *self); | ||
609 | |||
610 | |||
611 | /** Creates a new SZDD compressor. | ||
612 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
613 | * @return a #msszdd_compressor or NULL | ||
614 | */ | ||
615 | extern struct msszdd_compressor * | ||
616 | mspack_create_szdd_compressor(struct mspack_system *sys); | ||
617 | |||
618 | /** Creates a new SZDD decompressor. | ||
619 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
620 | * @return a #msszdd_decompressor or NULL | ||
621 | */ | ||
622 | extern struct msszdd_decompressor * | ||
623 | mspack_create_szdd_decompressor(struct mspack_system *sys); | ||
624 | |||
625 | /** Destroys an existing SZDD compressor. | ||
626 | * @param self the #msszdd_compressor to destroy | ||
627 | */ | ||
628 | extern void mspack_destroy_szdd_compressor(struct msszdd_compressor *self); | ||
629 | |||
630 | /** Destroys an existing SZDD decompressor. | ||
631 | * @param self the #msszdd_decompressor to destroy | ||
632 | */ | ||
633 | extern void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *self); | ||
634 | |||
635 | |||
636 | /** Creates a new KWAJ compressor. | ||
637 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
638 | * @return a #mskwaj_compressor or NULL | ||
639 | */ | ||
640 | extern struct mskwaj_compressor * | ||
641 | mspack_create_kwaj_compressor(struct mspack_system *sys); | ||
642 | |||
643 | /** Creates a new KWAJ decompressor. | ||
644 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
645 | * @return a #mskwaj_decompressor or NULL | ||
646 | */ | ||
647 | extern struct mskwaj_decompressor * | ||
648 | mspack_create_kwaj_decompressor(struct mspack_system *sys); | ||
649 | |||
650 | /** Destroys an existing KWAJ compressor. | ||
651 | * @param self the #mskwaj_compressor to destroy | ||
652 | */ | ||
653 | extern void mspack_destroy_kwaj_compressor(struct mskwaj_compressor *self); | ||
654 | |||
655 | /** Destroys an existing KWAJ decompressor. | ||
656 | * @param self the #mskwaj_decompressor to destroy | ||
657 | */ | ||
658 | extern void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *self); | ||
659 | |||
660 | |||
661 | /** Creates a new OAB compressor. | ||
662 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
663 | * @return a #msoab_compressor or NULL | ||
664 | */ | ||
665 | extern struct msoab_compressor * | ||
666 | mspack_create_oab_compressor(struct mspack_system *sys); | ||
667 | |||
668 | /** Creates a new OAB decompressor. | ||
669 | * @param sys a custom mspack_system structure, or NULL to use the default | ||
670 | * @return a #msoab_decompressor or NULL | ||
671 | */ | ||
672 | extern struct msoab_decompressor * | ||
673 | mspack_create_oab_decompressor(struct mspack_system *sys); | ||
674 | |||
675 | /** Destroys an existing OAB compressor. | ||
676 | * @param self the #msoab_compressor to destroy | ||
677 | */ | ||
678 | extern void mspack_destroy_oab_compressor(struct msoab_compressor *self); | ||
679 | |||
680 | /** Destroys an existing OAB decompressor. | ||
681 | * @param self the #msoab_decompressor to destroy | ||
682 | */ | ||
683 | extern void mspack_destroy_oab_decompressor(struct msoab_decompressor *self); | ||
684 | |||
685 | |||
686 | /* --- support for .CAB (MS Cabinet) file format --------------------------- */ | ||
687 | |||
688 | /** | ||
689 | * A structure which represents a single cabinet file. | ||
690 | * | ||
691 | * All fields are READ ONLY. | ||
692 | * | ||
693 | * If this cabinet is part of a merged cabinet set, the #files and #folders | ||
694 | * fields are common to all cabinets in the set, and will be identical. | ||
695 | * | ||
696 | * @see mscab_decompressor::open(), mscab_decompressor::close(), | ||
697 | * mscab_decompressor::search() | ||
698 | */ | ||
699 | struct mscabd_cabinet { | ||
700 | /** | ||
701 | * The next cabinet in a chained list, if this cabinet was opened with | ||
702 | * mscab_decompressor::search(). May be NULL to mark the end of the | ||
703 | * list. | ||
704 | */ | ||
705 | struct mscabd_cabinet *next; | ||
706 | |||
707 | /** | ||
708 | * The filename of the cabinet. More correctly, the filename of the | ||
709 | * physical file that the cabinet resides in. This is given by the | ||
710 | * library user and may be in any format. | ||
711 | */ | ||
712 | const char *filename; | ||
713 | |||
714 | /** The file offset of cabinet within the physical file it resides in. */ | ||
715 | off_t base_offset; | ||
716 | |||
717 | /** The length of the cabinet file in bytes. */ | ||
718 | unsigned int length; | ||
719 | |||
720 | /** The previous cabinet in a cabinet set, or NULL. */ | ||
721 | struct mscabd_cabinet *prevcab; | ||
722 | |||
723 | /** The next cabinet in a cabinet set, or NULL. */ | ||
724 | struct mscabd_cabinet *nextcab; | ||
725 | |||
726 | /** The filename of the previous cabinet in a cabinet set, or NULL. */ | ||
727 | char *prevname; | ||
728 | |||
729 | /** The filename of the next cabinet in a cabinet set, or NULL. */ | ||
730 | char *nextname; | ||
731 | |||
732 | /** The name of the disk containing the previous cabinet in a cabinet | ||
733 | * set, or NULL. | ||
734 | */ | ||
735 | char *previnfo; | ||
736 | |||
737 | /** The name of the disk containing the next cabinet in a cabinet set, | ||
738 | * or NULL. | ||
739 | */ | ||
740 | char *nextinfo; | ||
741 | |||
742 | /** A list of all files in the cabinet or cabinet set. */ | ||
743 | struct mscabd_file *files; | ||
744 | |||
745 | /** A list of all folders in the cabinet or cabinet set. */ | ||
746 | struct mscabd_folder *folders; | ||
747 | |||
748 | /** | ||
749 | * The set ID of the cabinet. All cabinets in the same set should have | ||
750 | * the same set ID. | ||
751 | */ | ||
752 | unsigned short set_id; | ||
753 | |||
754 | /** | ||
755 | * The index number of the cabinet within the set. Numbering should | ||
756 | * start from 0 for the first cabinet in the set, and increment by 1 for | ||
757 | * each following cabinet. | ||
758 | */ | ||
759 | unsigned short set_index; | ||
760 | |||
761 | /** | ||
762 | * The number of bytes reserved in the header area of the cabinet. | ||
763 | * | ||
764 | * If this is non-zero and flags has MSCAB_HDR_RESV set, this data can | ||
765 | * be read by the calling application. It is of the given length, | ||
766 | * located at offset (base_offset + MSCAB_HDR_RESV_OFFSET) in the | ||
767 | * cabinet file. | ||
768 | * | ||
769 | * @see flags | ||
770 | */ | ||
771 | unsigned short header_resv; | ||
772 | |||
773 | /** | ||
774 | * Header flags. | ||
775 | * | ||
776 | * - MSCAB_HDR_PREVCAB indicates the cabinet is part of a cabinet set, and | ||
777 | * has a predecessor cabinet. | ||
778 | * - MSCAB_HDR_NEXTCAB indicates the cabinet is part of a cabinet set, and | ||
779 | * has a successor cabinet. | ||
780 | * - MSCAB_HDR_RESV indicates the cabinet has reserved header space. | ||
781 | * | ||
782 | * @see prevname, previnfo, nextname, nextinfo, header_resv | ||
783 | */ | ||
784 | int flags; | ||
785 | }; | ||
786 | |||
787 | /** Offset from start of cabinet to the reserved header data (if present). */ | ||
788 | #define MSCAB_HDR_RESV_OFFSET (0x28) | ||
789 | |||
790 | /** Cabinet header flag: cabinet has a predecessor */ | ||
791 | #define MSCAB_HDR_PREVCAB (0x01) | ||
792 | /** Cabinet header flag: cabinet has a successor */ | ||
793 | #define MSCAB_HDR_NEXTCAB (0x02) | ||
794 | /** Cabinet header flag: cabinet has reserved header space */ | ||
795 | #define MSCAB_HDR_RESV (0x04) | ||
796 | |||
797 | /** | ||
798 | * A structure which represents a single folder in a cabinet or cabinet set. | ||
799 | * | ||
800 | * All fields are READ ONLY. | ||
801 | * | ||
802 | * A folder is a single compressed stream of data. When uncompressed, it | ||
803 | * holds the data of one or more files. A folder may be split across more | ||
804 | * than one cabinet. | ||
805 | */ | ||
806 | struct mscabd_folder { | ||
807 | /** | ||
808 | * A pointer to the next folder in this cabinet or cabinet set, or NULL | ||
809 | * if this is the final folder. | ||
810 | */ | ||
811 | struct mscabd_folder *next; | ||
812 | |||
813 | /** | ||
814 | * The compression format used by this folder. | ||
815 | * | ||
816 | * The macro MSCABD_COMP_METHOD() should be used on this field to get | ||
817 | * the algorithm used. The macro MSCABD_COMP_LEVEL() should be used to get | ||
818 | * the "compression level". | ||
819 | * | ||
820 | * @see MSCABD_COMP_METHOD(), MSCABD_COMP_LEVEL() | ||
821 | */ | ||
822 | int comp_type; | ||
823 | |||
824 | /** | ||
825 | * The total number of data blocks used by this folder. This includes | ||
826 | * data blocks present in other files, if this folder spans more than | ||
827 | * one cabinet. | ||
828 | */ | ||
829 | unsigned int num_blocks; | ||
830 | }; | ||
831 | |||
832 | /** | ||
833 | * Returns the compression method used by a folder. | ||
834 | * | ||
835 | * @param comp_type a mscabd_folder::comp_type value | ||
836 | * @return one of #MSCAB_COMP_NONE, #MSCAB_COMP_MSZIP, #MSCAB_COMP_QUANTUM | ||
837 | * or #MSCAB_COMP_LZX | ||
838 | */ | ||
839 | #define MSCABD_COMP_METHOD(comp_type) ((comp_type) & 0x0F) | ||
840 | /** | ||
841 | * Returns the compression level used by a folder. | ||
842 | * | ||
843 | * @param comp_type a mscabd_folder::comp_type value | ||
844 | * @return the compression level. This is only defined by LZX and Quantum | ||
845 | * compression | ||
846 | */ | ||
847 | #define MSCABD_COMP_LEVEL(comp_type) (((comp_type) >> 8) & 0x1F) | ||
848 | |||
849 | /** Compression mode: no compression. */ | ||
850 | #define MSCAB_COMP_NONE (0) | ||
851 | /** Compression mode: MSZIP (deflate) compression. */ | ||
852 | #define MSCAB_COMP_MSZIP (1) | ||
853 | /** Compression mode: Quantum compression */ | ||
854 | #define MSCAB_COMP_QUANTUM (2) | ||
855 | /** Compression mode: LZX compression */ | ||
856 | #define MSCAB_COMP_LZX (3) | ||
857 | |||
858 | /** | ||
859 | * A structure which represents a single file in a cabinet or cabinet set. | ||
860 | * | ||
861 | * All fields are READ ONLY. | ||
862 | */ | ||
863 | struct mscabd_file { | ||
864 | /** | ||
865 | * The next file in the cabinet or cabinet set, or NULL if this is the | ||
866 | * final file. | ||
867 | */ | ||
868 | struct mscabd_file *next; | ||
869 | |||
870 | /** | ||
871 | * The filename of the file. | ||
872 | * | ||
873 | * A null terminated string of up to 255 bytes in length, it may be in | ||
874 | * either ISO-8859-1 or UTF8 format, depending on the file attributes. | ||
875 | * | ||
876 | * @see attribs | ||
877 | */ | ||
878 | char *filename; | ||
879 | |||
880 | /** The uncompressed length of the file, in bytes. */ | ||
881 | unsigned int length; | ||
882 | |||
883 | /** | ||
884 | * File attributes. | ||
885 | * | ||
886 | * The following attributes are defined: | ||
887 | * - #MSCAB_ATTRIB_RDONLY indicates the file is write protected. | ||
888 | * - #MSCAB_ATTRIB_HIDDEN indicates the file is hidden. | ||
889 | * - #MSCAB_ATTRIB_SYSTEM indicates the file is a operating system file. | ||
890 | * - #MSCAB_ATTRIB_ARCH indicates the file is "archived". | ||
891 | * - #MSCAB_ATTRIB_EXEC indicates the file is an executable program. | ||
892 | * - #MSCAB_ATTRIB_UTF_NAME indicates the filename is in UTF8 format rather | ||
893 | * than ISO-8859-1. | ||
894 | */ | ||
895 | int attribs; | ||
896 | |||
897 | /** File's last modified time, hour field. */ | ||
898 | char time_h; | ||
899 | /** File's last modified time, minute field. */ | ||
900 | char time_m; | ||
901 | /** File's last modified time, second field. */ | ||
902 | char time_s; | ||
903 | |||
904 | /** File's last modified date, day field. */ | ||
905 | char date_d; | ||
906 | /** File's last modified date, month field. */ | ||
907 | char date_m; | ||
908 | /** File's last modified date, year field. */ | ||
909 | int date_y; | ||
910 | |||
911 | /** A pointer to the folder that contains this file. */ | ||
912 | struct mscabd_folder *folder; | ||
913 | |||
914 | /** The uncompressed offset of this file in its folder. */ | ||
915 | unsigned int offset; | ||
916 | }; | ||
917 | |||
918 | /** mscabd_file::attribs attribute: file is read-only. */ | ||
919 | #define MSCAB_ATTRIB_RDONLY (0x01) | ||
920 | /** mscabd_file::attribs attribute: file is hidden. */ | ||
921 | #define MSCAB_ATTRIB_HIDDEN (0x02) | ||
922 | /** mscabd_file::attribs attribute: file is an operating system file. */ | ||
923 | #define MSCAB_ATTRIB_SYSTEM (0x04) | ||
924 | /** mscabd_file::attribs attribute: file is "archived". */ | ||
925 | #define MSCAB_ATTRIB_ARCH (0x20) | ||
926 | /** mscabd_file::attribs attribute: file is an executable program. */ | ||
927 | #define MSCAB_ATTRIB_EXEC (0x40) | ||
928 | /** mscabd_file::attribs attribute: filename is UTF8, not ISO-8859-1. */ | ||
929 | #define MSCAB_ATTRIB_UTF_NAME (0x80) | ||
930 | |||
931 | /** mscab_decompressor::set_param() parameter: search buffer size. */ | ||
932 | #define MSCABD_PARAM_SEARCHBUF (0) | ||
933 | /** mscab_decompressor::set_param() parameter: repair MS-ZIP streams? */ | ||
934 | #define MSCABD_PARAM_FIXMSZIP (1) | ||
935 | /** mscab_decompressor::set_param() parameter: size of decompression buffer */ | ||
936 | #define MSCABD_PARAM_DECOMPBUF (2) | ||
937 | /** mscab_decompressor::set_param() parameter: salvage data from bad cabinets? | ||
938 | * If enabled, open() will skip file with bad folder indices or filenames | ||
939 | * rather than reject the whole cabinet, and extract() will limit rather than | ||
940 | * reject files with invalid offsets and lengths, and bad data block checksums | ||
941 | * will be ignored. Available only in CAB decoder version 2 and above. | ||
942 | */ | ||
943 | #define MSCABD_PARAM_SALVAGE (3) | ||
944 | |||
945 | /** TODO */ | ||
946 | struct mscab_compressor { | ||
947 | int dummy; | ||
948 | }; | ||
949 | |||
950 | /** | ||
951 | * A decompressor for .CAB (Microsoft Cabinet) files | ||
952 | * | ||
953 | * All fields are READ ONLY. | ||
954 | * | ||
955 | * @see mspack_create_cab_decompressor(), mspack_destroy_cab_decompressor() | ||
956 | */ | ||
957 | struct mscab_decompressor { | ||
958 | /** | ||
959 | * Opens a cabinet file and reads its contents. | ||
960 | * | ||
961 | * If the file opened is a valid cabinet file, all headers will be read | ||
962 | * and a mscabd_cabinet structure will be returned, with a full list of | ||
963 | * folders and files. | ||
964 | * | ||
965 | * In the case of an error occuring, NULL is returned and the error code | ||
966 | * is available from last_error(). | ||
967 | * | ||
968 | * The filename pointer should be considered "in use" until close() is | ||
969 | * called on the cabinet. | ||
970 | * | ||
971 | * @param self a self-referential pointer to the mscab_decompressor | ||
972 | * instance being called | ||
973 | * @param filename the filename of the cabinet file. This is passed | ||
974 | * directly to mspack_system::open(). | ||
975 | * @return a pointer to a mscabd_cabinet structure, or NULL on failure | ||
976 | * @see close(), search(), last_error() | ||
977 | */ | ||
978 | struct mscabd_cabinet * (*open) (struct mscab_decompressor *self, | ||
979 | const char *filename); | ||
980 | |||
981 | /** | ||
982 | * Closes a previously opened cabinet or cabinet set. | ||
983 | * | ||
984 | * This closes a cabinet, all cabinets associated with it via the | ||
985 | * mscabd_cabinet::next, mscabd_cabinet::prevcab and | ||
986 | * mscabd_cabinet::nextcab pointers, and all folders and files. All | ||
987 | * memory used by these entities is freed. | ||
988 | * | ||
989 | * The cabinet pointer is now invalid and cannot be used again. All | ||
990 | * mscabd_folder and mscabd_file pointers from that cabinet or cabinet | ||
991 | * set are also now invalid, and cannot be used again. | ||
992 | * | ||
993 | * If the cabinet pointer given was created using search(), it MUST be | ||
994 | * the cabinet pointer returned by search() and not one of the later | ||
995 | * cabinet pointers further along the mscabd_cabinet::next chain. | ||
996 | |||
997 | * If extra cabinets have been added using append() or prepend(), these | ||
998 | * will all be freed, even if the cabinet pointer given is not the first | ||
999 | * cabinet in the set. Do NOT close() more than one cabinet in the set. | ||
1000 | * | ||
1001 | * The mscabd_cabinet::filename is not freed by the library, as it is | ||
1002 | * not allocated by the library. The caller should free this itself if | ||
1003 | * necessary, before it is lost forever. | ||
1004 | * | ||
1005 | * @param self a self-referential pointer to the mscab_decompressor | ||
1006 | * instance being called | ||
1007 | * @param cab the cabinet to close | ||
1008 | * @see open(), search(), append(), prepend() | ||
1009 | */ | ||
1010 | void (*close)(struct mscab_decompressor *self, | ||
1011 | struct mscabd_cabinet *cab); | ||
1012 | |||
1013 | /** | ||
1014 | * Searches a regular file for embedded cabinets. | ||
1015 | * | ||
1016 | * This opens a normal file with the given filename and will search the | ||
1017 | * entire file for embedded cabinet files | ||
1018 | * | ||
1019 | * If any cabinets are found, the equivalent of open() is called on each | ||
1020 | * potential cabinet file at the offset it was found. All successfully | ||
1021 | * open()ed cabinets are kept in a list. | ||
1022 | * | ||
1023 | * The first cabinet found will be returned directly as the result of | ||
1024 | * this method. Any further cabinets found will be chained in a list | ||
1025 | * using the mscabd_cabinet::next field. | ||
1026 | * | ||
1027 | * In the case of an error occuring anywhere other than the simulated | ||
1028 | * open(), NULL is returned and the error code is available from | ||
1029 | * last_error(). | ||
1030 | * | ||
1031 | * If no error occurs, but no cabinets can be found in the file, NULL is | ||
1032 | * returned and last_error() returns MSPACK_ERR_OK. | ||
1033 | * | ||
1034 | * The filename pointer should be considered in use until close() is | ||
1035 | * called on the cabinet. | ||
1036 | * | ||
1037 | * close() should only be called on the result of search(), not on any | ||
1038 | * subsequent cabinets in the mscabd_cabinet::next chain. | ||
1039 | * | ||
1040 | * @param self a self-referential pointer to the mscab_decompressor | ||
1041 | * instance being called | ||
1042 | * @param filename the filename of the file to search for cabinets. This | ||
1043 | * is passed directly to mspack_system::open(). | ||
1044 | * @return a pointer to a mscabd_cabinet structure, or NULL | ||
1045 | * @see close(), open(), last_error() | ||
1046 | */ | ||
1047 | struct mscabd_cabinet * (*search) (struct mscab_decompressor *self, | ||
1048 | const char *filename); | ||
1049 | |||
1050 | /** | ||
1051 | * Appends one mscabd_cabinet to another, forming or extending a cabinet | ||
1052 | * set. | ||
1053 | * | ||
1054 | * This will attempt to append one cabinet to another such that | ||
1055 | * <tt>(cab->nextcab == nextcab) && (nextcab->prevcab == cab)</tt> and | ||
1056 | * any folders split between the two cabinets are merged. | ||
1057 | * | ||
1058 | * The cabinets MUST be part of a cabinet set -- a cabinet set is a | ||
1059 | * cabinet that spans more than one physical cabinet file on disk -- and | ||
1060 | * must be appropriately matched. | ||
1061 | * | ||
1062 | * It can be determined if a cabinet has further parts to load by | ||
1063 | * examining the mscabd_cabinet::flags field: | ||
1064 | * | ||
1065 | * - if <tt>(flags & MSCAB_HDR_PREVCAB)</tt> is non-zero, there is a | ||
1066 | * predecessor cabinet to open() and prepend(). Its MS-DOS | ||
1067 | * case-insensitive filename is mscabd_cabinet::prevname | ||
1068 | * - if <tt>(flags & MSCAB_HDR_NEXTCAB)</tt> is non-zero, there is a | ||
1069 | * successor cabinet to open() and append(). Its MS-DOS case-insensitive | ||
1070 | * filename is mscabd_cabinet::nextname | ||
1071 | * | ||
1072 | * If the cabinets do not match, an error code will be returned. Neither | ||
1073 | * cabinet has been altered, and both should be closed seperately. | ||
1074 | * | ||
1075 | * Files and folders in a cabinet set are a single entity. All cabinets | ||
1076 | * in a set use the same file list, which is updated as cabinets in the | ||
1077 | * set are added. All pointers to mscabd_folder and mscabd_file | ||
1078 | * structures in either cabinet must be discarded and re-obtained after | ||
1079 | * merging. | ||
1080 | * | ||
1081 | * @param self a self-referential pointer to the mscab_decompressor | ||
1082 | * instance being called | ||
1083 | * @param cab the cabinet which will be appended to, | ||
1084 | * predecessor of nextcab | ||
1085 | * @param nextcab the cabinet which will be appended, | ||
1086 | * successor of cab | ||
1087 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1088 | * @see prepend(), open(), close() | ||
1089 | */ | ||
1090 | int (*append) (struct mscab_decompressor *self, | ||
1091 | struct mscabd_cabinet *cab, | ||
1092 | struct mscabd_cabinet *nextcab); | ||
1093 | |||
1094 | /** | ||
1095 | * Prepends one mscabd_cabinet to another, forming or extending a | ||
1096 | * cabinet set. | ||
1097 | * | ||
1098 | * This will attempt to prepend one cabinet to another, such that | ||
1099 | * <tt>(cab->prevcab == prevcab) && (prevcab->nextcab == cab)</tt>. In | ||
1100 | * all other respects, it is identical to append(). See append() for the | ||
1101 | * full documentation. | ||
1102 | * | ||
1103 | * @param self a self-referential pointer to the mscab_decompressor | ||
1104 | * instance being called | ||
1105 | * @param cab the cabinet which will be prepended to, | ||
1106 | * successor of prevcab | ||
1107 | * @param prevcab the cabinet which will be prepended, | ||
1108 | * predecessor of cab | ||
1109 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1110 | * @see append(), open(), close() | ||
1111 | */ | ||
1112 | int (*prepend) (struct mscab_decompressor *self, | ||
1113 | struct mscabd_cabinet *cab, | ||
1114 | struct mscabd_cabinet *prevcab); | ||
1115 | |||
1116 | /** | ||
1117 | * Extracts a file from a cabinet or cabinet set. | ||
1118 | * | ||
1119 | * This extracts a compressed file in a cabinet and writes it to the given | ||
1120 | * filename. | ||
1121 | * | ||
1122 | * The MS-DOS filename of the file, mscabd_file::filename, is NOT USED | ||
1123 | * by extract(). The caller must examine this MS-DOS filename, copy and | ||
1124 | * change it as necessary, create directories as necessary, and provide | ||
1125 | * the correct filename as a parameter, which will be passed unchanged | ||
1126 | * to the decompressor's mspack_system::open() | ||
1127 | * | ||
1128 | * If the file belongs to a split folder in a multi-part cabinet set, | ||
1129 | * and not enough parts of the cabinet set have been loaded and appended | ||
1130 | * or prepended, an error will be returned immediately. | ||
1131 | * | ||
1132 | * @param self a self-referential pointer to the mscab_decompressor | ||
1133 | * instance being called | ||
1134 | * @param file the file to be decompressed | ||
1135 | * @param filename the filename of the file being written to | ||
1136 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1137 | */ | ||
1138 | int (*extract)(struct mscab_decompressor *self, | ||
1139 | struct mscabd_file *file, | ||
1140 | const char *filename); | ||
1141 | |||
1142 | /** | ||
1143 | * Sets a CAB decompression engine parameter. | ||
1144 | * | ||
1145 | * The following parameters are defined: | ||
1146 | * - #MSCABD_PARAM_SEARCHBUF: How many bytes should be allocated as a | ||
1147 | * buffer when using search()? The minimum value is 4. The default | ||
1148 | * value is 32768. | ||
1149 | * - #MSCABD_PARAM_FIXMSZIP: If non-zero, extract() will ignore bad | ||
1150 | * checksums and recover from decompression errors in MS-ZIP | ||
1151 | * compressed folders. The default value is 0 (don't recover). | ||
1152 | * - #MSCABD_PARAM_DECOMPBUF: How many bytes should be used as an input | ||
1153 | * bit buffer by decompressors? The minimum value is 4. The default | ||
1154 | * value is 4096. | ||
1155 | * | ||
1156 | * @param self a self-referential pointer to the mscab_decompressor | ||
1157 | * instance being called | ||
1158 | * @param param the parameter to set | ||
1159 | * @param value the value to set the parameter to | ||
1160 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there | ||
1161 | * is a problem with either parameter or value. | ||
1162 | * @see search(), extract() | ||
1163 | */ | ||
1164 | int (*set_param)(struct mscab_decompressor *self, | ||
1165 | int param, | ||
1166 | int value); | ||
1167 | |||
1168 | /** | ||
1169 | * Returns the error code set by the most recently called method. | ||
1170 | * | ||
1171 | * This is useful for open() and search(), which do not return an error | ||
1172 | * code directly. | ||
1173 | * | ||
1174 | * @param self a self-referential pointer to the mscab_decompressor | ||
1175 | * instance being called | ||
1176 | * @return the most recent error code | ||
1177 | * @see open(), search() | ||
1178 | */ | ||
1179 | int (*last_error)(struct mscab_decompressor *self); | ||
1180 | }; | ||
1181 | |||
1182 | /* --- support for .CHM (HTMLHelp) file format ----------------------------- */ | ||
1183 | |||
1184 | /** | ||
1185 | * A structure which represents a file to be placed in a CHM helpfile. | ||
1186 | * | ||
1187 | * A contiguous array of these structures should be passed to | ||
1188 | * mschm_compressor::generate(). The array list is terminated with an | ||
1189 | * entry whose mschmc_file::section field is set to #MSCHMC_ENDLIST, the | ||
1190 | * other fields in this entry are ignored. | ||
1191 | */ | ||
1192 | struct mschmc_file { | ||
1193 | /** One of #MSCHMC_ENDLIST, #MSCHMC_UNCOMP or #MSCHMC_MSCOMP. */ | ||
1194 | int section; | ||
1195 | |||
1196 | /** The filename of the source file that will be added to the CHM. This | ||
1197 | * is passed directly to mspack_system::open(). */ | ||
1198 | const char *filename; | ||
1199 | |||
1200 | /** The full path and filename of the file within the CHM helpfile, a | ||
1201 | * UTF-1 encoded null-terminated string. */ | ||
1202 | char *chm_filename; | ||
1203 | |||
1204 | /** The length of the file, in bytes. This will be adhered to strictly | ||
1205 | * and a read error will be issued if this many bytes cannot be read | ||
1206 | * from the real file at CHM generation time. */ | ||
1207 | off_t length; | ||
1208 | }; | ||
1209 | |||
1210 | /** | ||
1211 | * A structure which represents a section of a CHM helpfile. | ||
1212 | * | ||
1213 | * All fields are READ ONLY. | ||
1214 | * | ||
1215 | * Not used directly, but used as a generic base type for | ||
1216 | * mschmd_sec_uncompressed and mschmd_sec_mscompressed. | ||
1217 | */ | ||
1218 | struct mschmd_section { | ||
1219 | /** A pointer to the CHM helpfile that contains this section. */ | ||
1220 | struct mschmd_header *chm; | ||
1221 | |||
1222 | /** | ||
1223 | * The section ID. Either 0 for the uncompressed section | ||
1224 | * mschmd_sec_uncompressed, or 1 for the LZX compressed section | ||
1225 | * mschmd_sec_mscompressed. No other section IDs are known. | ||
1226 | */ | ||
1227 | unsigned int id; | ||
1228 | }; | ||
1229 | |||
1230 | /** | ||
1231 | * A structure which represents the uncompressed section of a CHM helpfile. | ||
1232 | * | ||
1233 | * All fields are READ ONLY. | ||
1234 | */ | ||
1235 | struct mschmd_sec_uncompressed { | ||
1236 | /** Generic section data. */ | ||
1237 | struct mschmd_section base; | ||
1238 | |||
1239 | /** The file offset of where this section begins in the CHM helpfile. */ | ||
1240 | off_t offset; | ||
1241 | }; | ||
1242 | |||
1243 | /** | ||
1244 | * A structure which represents the LZX compressed section of a CHM helpfile. | ||
1245 | * | ||
1246 | * All fields are READ ONLY. | ||
1247 | */ | ||
1248 | struct mschmd_sec_mscompressed { | ||
1249 | /** Generic section data. */ | ||
1250 | struct mschmd_section base; | ||
1251 | |||
1252 | /** A pointer to the meta-file which represents all LZX compressed data. */ | ||
1253 | struct mschmd_file *content; | ||
1254 | |||
1255 | /** A pointer to the file which contains the LZX control data. */ | ||
1256 | struct mschmd_file *control; | ||
1257 | |||
1258 | /** A pointer to the file which contains the LZX reset table. */ | ||
1259 | struct mschmd_file *rtable; | ||
1260 | |||
1261 | /** A pointer to the file which contains the LZX span information. | ||
1262 | * Available only in CHM decoder version 2 and above. | ||
1263 | */ | ||
1264 | struct mschmd_file *spaninfo; | ||
1265 | }; | ||
1266 | |||
1267 | /** | ||
1268 | * A structure which represents a CHM helpfile. | ||
1269 | * | ||
1270 | * All fields are READ ONLY. | ||
1271 | */ | ||
1272 | struct mschmd_header { | ||
1273 | /** The version of the CHM file format used in this file. */ | ||
1274 | unsigned int version; | ||
1275 | |||
1276 | /** | ||
1277 | * The "timestamp" of the CHM helpfile. | ||
1278 | * | ||
1279 | * It is the lower 32 bits of a 64-bit value representing the number of | ||
1280 | * centiseconds since 1601-01-01 00:00:00 UTC, plus 42. It is not useful | ||
1281 | * as a timestamp, but it is useful as a semi-unique ID. | ||
1282 | */ | ||
1283 | unsigned int timestamp; | ||
1284 | |||
1285 | /** | ||
1286 | * The default Language and Country ID (LCID) of the user who ran the | ||
1287 | * HTMLHelp Compiler. This is not the language of the CHM file itself. | ||
1288 | */ | ||
1289 | unsigned int language; | ||
1290 | |||
1291 | /** | ||
1292 | * The filename of the CHM helpfile. This is given by the library user | ||
1293 | * and may be in any format. | ||
1294 | */ | ||
1295 | const char *filename; | ||
1296 | |||
1297 | /** The length of the CHM helpfile, in bytes. */ | ||
1298 | off_t length; | ||
1299 | |||
1300 | /** A list of all non-system files in the CHM helpfile. */ | ||
1301 | struct mschmd_file *files; | ||
1302 | |||
1303 | /** | ||
1304 | * A list of all system files in the CHM helpfile. | ||
1305 | * | ||
1306 | * System files are files which begin with "::". They are meta-files | ||
1307 | * generated by the CHM creation process. | ||
1308 | */ | ||
1309 | struct mschmd_file *sysfiles; | ||
1310 | |||
1311 | /** The section 0 (uncompressed) data in this CHM helpfile. */ | ||
1312 | struct mschmd_sec_uncompressed sec0; | ||
1313 | |||
1314 | /** The section 1 (MSCompressed) data in this CHM helpfile. */ | ||
1315 | struct mschmd_sec_mscompressed sec1; | ||
1316 | |||
1317 | /** The file offset of the first PMGL/PMGI directory chunk. */ | ||
1318 | off_t dir_offset; | ||
1319 | |||
1320 | /** The number of PMGL/PMGI directory chunks in this CHM helpfile. */ | ||
1321 | unsigned int num_chunks; | ||
1322 | |||
1323 | /** The size of each PMGL/PMGI chunk, in bytes. */ | ||
1324 | unsigned int chunk_size; | ||
1325 | |||
1326 | /** The "density" of the quick-reference section in PMGL/PMGI chunks. */ | ||
1327 | unsigned int density; | ||
1328 | |||
1329 | /** The depth of the index tree. | ||
1330 | * | ||
1331 | * - if 1, there are no PMGI chunks, only PMGL chunks. | ||
1332 | * - if 2, there is 1 PMGI chunk. All chunk indices point to PMGL chunks. | ||
1333 | * - if 3, the root PMGI chunk points to secondary PMGI chunks, which in | ||
1334 | * turn point to PMGL chunks. | ||
1335 | * - and so on... | ||
1336 | */ | ||
1337 | unsigned int depth; | ||
1338 | |||
1339 | /** | ||
1340 | * The number of the root PMGI chunk. | ||
1341 | * | ||
1342 | * If there is no index in the CHM helpfile, this will be 0xFFFFFFFF. | ||
1343 | */ | ||
1344 | unsigned int index_root; | ||
1345 | |||
1346 | /** | ||
1347 | * The number of the first PMGL chunk. Usually zero. | ||
1348 | * Available only in CHM decoder version 2 and above. | ||
1349 | */ | ||
1350 | unsigned int first_pmgl; | ||
1351 | |||
1352 | /** | ||
1353 | * The number of the last PMGL chunk. Usually num_chunks-1. | ||
1354 | * Available only in CHM decoder version 2 and above. | ||
1355 | */ | ||
1356 | unsigned int last_pmgl; | ||
1357 | |||
1358 | /** | ||
1359 | * A cache of loaded chunks, filled in by mschm_decoder::fast_find(). | ||
1360 | * Available only in CHM decoder version 2 and above. | ||
1361 | */ | ||
1362 | unsigned char **chunk_cache; | ||
1363 | }; | ||
1364 | |||
1365 | /** | ||
1366 | * A structure which represents a file stored in a CHM helpfile. | ||
1367 | * | ||
1368 | * All fields are READ ONLY. | ||
1369 | */ | ||
1370 | struct mschmd_file { | ||
1371 | /** | ||
1372 | * A pointer to the next file in the list, or NULL if this is the final | ||
1373 | * file. | ||
1374 | */ | ||
1375 | struct mschmd_file *next; | ||
1376 | |||
1377 | /** | ||
1378 | * A pointer to the section that this file is located in. Indirectly, | ||
1379 | * it also points to the CHM helpfile the file is located in. | ||
1380 | */ | ||
1381 | struct mschmd_section *section; | ||
1382 | |||
1383 | /** The offset within the section data that this file is located at. */ | ||
1384 | off_t offset; | ||
1385 | |||
1386 | /** The length of this file, in bytes */ | ||
1387 | off_t length; | ||
1388 | |||
1389 | /** The filename of this file -- a null terminated string in UTF-8. */ | ||
1390 | char *filename; | ||
1391 | }; | ||
1392 | |||
1393 | /** mschmc_file::section value: end of CHM file list */ | ||
1394 | #define MSCHMC_ENDLIST (0) | ||
1395 | /** mschmc_file::section value: this file is in the Uncompressed section */ | ||
1396 | #define MSCHMC_UNCOMP (1) | ||
1397 | /** mschmc_file::section value: this file is in the MSCompressed section */ | ||
1398 | #define MSCHMC_MSCOMP (2) | ||
1399 | |||
1400 | /** mschm_compressor::set_param() parameter: "timestamp" header */ | ||
1401 | #define MSCHMC_PARAM_TIMESTAMP (0) | ||
1402 | /** mschm_compressor::set_param() parameter: "language" header */ | ||
1403 | #define MSCHMC_PARAM_LANGUAGE (1) | ||
1404 | /** mschm_compressor::set_param() parameter: LZX window size */ | ||
1405 | #define MSCHMC_PARAM_LZXWINDOW (2) | ||
1406 | /** mschm_compressor::set_param() parameter: intra-chunk quickref density */ | ||
1407 | #define MSCHMC_PARAM_DENSITY (3) | ||
1408 | /** mschm_compressor::set_param() parameter: whether to create indices */ | ||
1409 | #define MSCHMC_PARAM_INDEX (4) | ||
1410 | |||
1411 | /** | ||
1412 | * A compressor for .CHM (Microsoft HTMLHelp) files. | ||
1413 | * | ||
1414 | * All fields are READ ONLY. | ||
1415 | * | ||
1416 | * @see mspack_create_chm_compressor(), mspack_destroy_chm_compressor() | ||
1417 | */ | ||
1418 | struct mschm_compressor { | ||
1419 | /** | ||
1420 | * Generates a CHM help file. | ||
1421 | * | ||
1422 | * The help file will contain up to two sections, an Uncompressed | ||
1423 | * section and potentially an MSCompressed (LZX compressed) | ||
1424 | * section. | ||
1425 | * | ||
1426 | * While the contents listing of a CHM file is always in lexical order, | ||
1427 | * the file list passed in will be taken as the correct order for files | ||
1428 | * within the sections. It is in your interest to place similar files | ||
1429 | * together for better compression. | ||
1430 | * | ||
1431 | * There are two modes of generation, to use a temporary file or not to | ||
1432 | * use one. See use_temporary_file() for the behaviour of generate() in | ||
1433 | * these two different modes. | ||
1434 | * | ||
1435 | * @param self a self-referential pointer to the mschm_compressor | ||
1436 | * instance being called | ||
1437 | * @param file_list an array of mschmc_file structures, terminated | ||
1438 | * with an entry whose mschmc_file::section field is | ||
1439 | * #MSCHMC_ENDLIST. The order of the list is | ||
1440 | * preserved within each section. The length of any | ||
1441 | * mschmc_file::chm_filename string cannot exceed | ||
1442 | * roughly 4096 bytes. Each source file must be able | ||
1443 | * to supply as many bytes as given in the | ||
1444 | * mschmc_file::length field. | ||
1445 | * @param output_file the file to write the generated CHM helpfile to. | ||
1446 | * This is passed directly to mspack_system::open() | ||
1447 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1448 | * @see use_temporary_file() set_param() | ||
1449 | */ | ||
1450 | int (*generate)(struct mschm_compressor *self, | ||
1451 | struct mschmc_file file_list[], | ||
1452 | const char *output_file); | ||
1453 | |||
1454 | /** | ||
1455 | * Specifies whether a temporary file is used during CHM generation. | ||
1456 | * | ||
1457 | * The CHM file format includes data about the compressed section (such | ||
1458 | * as its overall size) that is stored in the output CHM file prior to | ||
1459 | * the compressed section itself. This unavoidably requires that the | ||
1460 | * compressed section has to be generated, before these details can be | ||
1461 | * set. There are several ways this can be handled. Firstly, the | ||
1462 | * compressed section could be generated entirely in memory before | ||
1463 | * writing any of the output CHM file. This approach is not used in | ||
1464 | * libmspack, as the compressed section can exceed the addressable | ||
1465 | * memory space on most architectures. | ||
1466 | * | ||
1467 | * libmspack has two options, either to write these unknowable sections | ||
1468 | * with blank data, generate the compressed section, then re-open the | ||
1469 | * output file for update once the compressed section has been | ||
1470 | * completed, or to write the compressed section to a temporary file, | ||
1471 | * then write the entire output file at once, performing a simple | ||
1472 | * file-to-file copy for the compressed section. | ||
1473 | * | ||
1474 | * The simple solution of buffering the entire compressed section in | ||
1475 | * memory can still be used, if desired. As the temporary file's | ||
1476 | * filename is passed directly to mspack_system::open(), it is possible | ||
1477 | * for a custom mspack_system implementation to hold this file in memory, | ||
1478 | * without writing to a disk. | ||
1479 | * | ||
1480 | * If a temporary file is set, generate() performs the following | ||
1481 | * sequence of events: the temporary file is opened for writing, the | ||
1482 | * compression algorithm writes to the temporary file, the temporary | ||
1483 | * file is closed. Then the output file is opened for writing and the | ||
1484 | * temporary file is re-opened for reading. The output file is written | ||
1485 | * and the temporary file is read from. Both files are then closed. The | ||
1486 | * temporary file itself is not deleted. If that is desired, the | ||
1487 | * temporary file should be deleted after the completion of generate(), | ||
1488 | * if it exists. | ||
1489 | * | ||
1490 | * If a temporary file is set not to be used, generate() performs the | ||
1491 | * following sequence of events: the output file is opened for writing, | ||
1492 | * then it is written and closed. The output file is then re-opened for | ||
1493 | * update, the appropriate sections are seek()ed to and re-written, then | ||
1494 | * the output file is closed. | ||
1495 | * | ||
1496 | * @param self a self-referential pointer to the | ||
1497 | * mschm_compressor instance being called | ||
1498 | * @param use_temp_file non-zero if the temporary file should be used, | ||
1499 | * zero if the temporary file should not be used. | ||
1500 | * @param temp_file a file to temporarily write compressed data to, | ||
1501 | * before opening it for reading and copying the | ||
1502 | * contents to the output file. This is passed | ||
1503 | * directly to mspack_system::open(). | ||
1504 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1505 | * @see generate() | ||
1506 | */ | ||
1507 | int (*use_temporary_file)(struct mschm_compressor *self, | ||
1508 | int use_temp_file, | ||
1509 | const char *temp_file); | ||
1510 | /** | ||
1511 | * Sets a CHM compression engine parameter. | ||
1512 | * | ||
1513 | * The following parameters are defined: | ||
1514 | |||
1515 | * - #MSCHMC_PARAM_TIMESTAMP: Sets the "timestamp" of the CHM file | ||
1516 | * generated. This is not a timestamp, see mschmd_header::timestamp | ||
1517 | * for a description. If this timestamp is 0, generate() will use its | ||
1518 | * own algorithm for making a unique ID, based on the lengths and | ||
1519 | * names of files in the CHM itself. Defaults to 0, any value between | ||
1520 | * 0 and (2^32)-1 is valid. | ||
1521 | * - #MSCHMC_PARAM_LANGUAGE: Sets the "language" of the CHM file | ||
1522 | * generated. This is not the language used in the CHM file, but the | ||
1523 | * language setting of the user who ran the HTMLHelp compiler. It | ||
1524 | * defaults to 0x0409. The valid range is between 0x0000 and 0x7F7F. | ||
1525 | * - #MSCHMC_PARAM_LZXWINDOW: Sets the size of the LZX history window, | ||
1526 | * which is also the interval at which the compressed data stream can be | ||
1527 | * randomly accessed. The value is not a size in bytes, but a power of | ||
1528 | * two. The default value is 16 (which makes the window 2^16 bytes, or | ||
1529 | * 64 kilobytes), the valid range is from 15 (32 kilobytes) to 21 (2 | ||
1530 | * megabytes). | ||
1531 | * - #MSCHMC_PARAM_DENSITY: Sets the "density" of quick reference | ||
1532 | * entries stored at the end of directory listing chunk. Each chunk is | ||
1533 | * 4096 bytes in size, and contains as many file entries as there is | ||
1534 | * room for. At the other end of the chunk, a list of "quick reference" | ||
1535 | * pointers is included. The offset of every 'N'th file entry is given a | ||
1536 | * quick reference, where N = (2^density) + 1. The default density is | ||
1537 | * 2. The smallest density is 0 (N=2), the maximum is 10 (N=1025). As | ||
1538 | * each file entry requires at least 5 bytes, the maximum number of | ||
1539 | * entries in a single chunk is roughly 800, so the maximum value 10 | ||
1540 | * can be used to indicate there are no quickrefs at all. | ||
1541 | * - #MSCHMC_PARAM_INDEX: Sets whether or not to include quick lookup | ||
1542 | * index chunk(s), in addition to normal directory listing chunks. A | ||
1543 | * value of zero means no index chunks will be created, a non-zero value | ||
1544 | * means index chunks will be created. The default is zero, "don't | ||
1545 | * create an index". | ||
1546 | * | ||
1547 | * @param self a self-referential pointer to the mschm_compressor | ||
1548 | * instance being called | ||
1549 | * @param param the parameter to set | ||
1550 | * @param value the value to set the parameter to | ||
1551 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there | ||
1552 | * is a problem with either parameter or value. | ||
1553 | * @see generate() | ||
1554 | */ | ||
1555 | int (*set_param)(struct mschm_compressor *self, | ||
1556 | int param, | ||
1557 | int value); | ||
1558 | |||
1559 | /** | ||
1560 | * Returns the error code set by the most recently called method. | ||
1561 | * | ||
1562 | * @param self a self-referential pointer to the mschm_compressor | ||
1563 | * instance being called | ||
1564 | * @return the most recent error code | ||
1565 | * @see set_param(), generate() | ||
1566 | */ | ||
1567 | int (*last_error)(struct mschm_compressor *self); | ||
1568 | }; | ||
1569 | |||
1570 | /** | ||
1571 | * A decompressor for .CHM (Microsoft HTMLHelp) files | ||
1572 | * | ||
1573 | * All fields are READ ONLY. | ||
1574 | * | ||
1575 | * @see mspack_create_chm_decompressor(), mspack_destroy_chm_decompressor() | ||
1576 | */ | ||
1577 | struct mschm_decompressor { | ||
1578 | /** | ||
1579 | * Opens a CHM helpfile and reads its contents. | ||
1580 | * | ||
1581 | * If the file opened is a valid CHM helpfile, all headers will be read | ||
1582 | * and a mschmd_header structure will be returned, with a full list of | ||
1583 | * files. | ||
1584 | * | ||
1585 | * In the case of an error occuring, NULL is returned and the error code | ||
1586 | * is available from last_error(). | ||
1587 | * | ||
1588 | * The filename pointer should be considered "in use" until close() is | ||
1589 | * called on the CHM helpfile. | ||
1590 | * | ||
1591 | * @param self a self-referential pointer to the mschm_decompressor | ||
1592 | * instance being called | ||
1593 | * @param filename the filename of the CHM helpfile. This is passed | ||
1594 | * directly to mspack_system::open(). | ||
1595 | * @return a pointer to a mschmd_header structure, or NULL on failure | ||
1596 | * @see close() | ||
1597 | */ | ||
1598 | struct mschmd_header *(*open)(struct mschm_decompressor *self, | ||
1599 | const char *filename); | ||
1600 | |||
1601 | /** | ||
1602 | * Closes a previously opened CHM helpfile. | ||
1603 | * | ||
1604 | * This closes a CHM helpfile, frees the mschmd_header and all | ||
1605 | * mschmd_file structures associated with it (if any). This works on | ||
1606 | * both helpfiles opened with open() and helpfiles opened with | ||
1607 | * fast_open(). | ||
1608 | * | ||
1609 | * The CHM header pointer is now invalid and cannot be used again. All | ||
1610 | * mschmd_file pointers referencing that CHM are also now invalid, and | ||
1611 | * cannot be used again. | ||
1612 | * | ||
1613 | * @param self a self-referential pointer to the mschm_decompressor | ||
1614 | * instance being called | ||
1615 | * @param chm the CHM helpfile to close | ||
1616 | * @see open(), fast_open() | ||
1617 | */ | ||
1618 | void (*close)(struct mschm_decompressor *self, | ||
1619 | struct mschmd_header *chm); | ||
1620 | |||
1621 | /** | ||
1622 | * Extracts a file from a CHM helpfile. | ||
1623 | * | ||
1624 | * This extracts a file from a CHM helpfile and writes it to the given | ||
1625 | * filename. The filename of the file, mscabd_file::filename, is not | ||
1626 | * used by extract(), but can be used by the caller as a guide for | ||
1627 | * constructing an appropriate filename. | ||
1628 | * | ||
1629 | * This method works both with files found in the mschmd_header::files | ||
1630 | * and mschmd_header::sysfiles list and mschmd_file structures generated | ||
1631 | * on the fly by fast_find(). | ||
1632 | * | ||
1633 | * @param self a self-referential pointer to the mschm_decompressor | ||
1634 | * instance being called | ||
1635 | * @param file the file to be decompressed | ||
1636 | * @param filename the filename of the file being written to | ||
1637 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1638 | */ | ||
1639 | int (*extract)(struct mschm_decompressor *self, | ||
1640 | struct mschmd_file *file, | ||
1641 | const char *filename); | ||
1642 | |||
1643 | /** | ||
1644 | * Returns the error code set by the most recently called method. | ||
1645 | * | ||
1646 | * This is useful for open() and fast_open(), which do not return an | ||
1647 | * error code directly. | ||
1648 | * | ||
1649 | * @param self a self-referential pointer to the mschm_decompressor | ||
1650 | * instance being called | ||
1651 | * @return the most recent error code | ||
1652 | * @see open(), extract() | ||
1653 | */ | ||
1654 | int (*last_error)(struct mschm_decompressor *self); | ||
1655 | |||
1656 | /** | ||
1657 | * Opens a CHM helpfile quickly. | ||
1658 | * | ||
1659 | * If the file opened is a valid CHM helpfile, only essential headers | ||
1660 | * will be read. A mschmd_header structure will be still be returned, as | ||
1661 | * with open(), but the mschmd_header::files field will be NULL. No | ||
1662 | * files details will be automatically read. The fast_find() method | ||
1663 | * must be used to obtain file details. | ||
1664 | * | ||
1665 | * In the case of an error occuring, NULL is returned and the error code | ||
1666 | * is available from last_error(). | ||
1667 | * | ||
1668 | * The filename pointer should be considered "in use" until close() is | ||
1669 | * called on the CHM helpfile. | ||
1670 | * | ||
1671 | * @param self a self-referential pointer to the mschm_decompressor | ||
1672 | * instance being called | ||
1673 | * @param filename the filename of the CHM helpfile. This is passed | ||
1674 | * directly to mspack_system::open(). | ||
1675 | * @return a pointer to a mschmd_header structure, or NULL on failure | ||
1676 | * @see open(), close(), fast_find(), extract() | ||
1677 | */ | ||
1678 | struct mschmd_header *(*fast_open)(struct mschm_decompressor *self, | ||
1679 | const char *filename); | ||
1680 | |||
1681 | /** | ||
1682 | * Finds file details quickly. | ||
1683 | * | ||
1684 | * Instead of reading all CHM helpfile headers and building a list of | ||
1685 | * files, fast_open() and fast_find() are intended for finding file | ||
1686 | * details only when they are needed. The CHM file format includes an | ||
1687 | * on-disk file index to allow this. | ||
1688 | * | ||
1689 | * Given a case-sensitive filename, fast_find() will search the on-disk | ||
1690 | * index for that file. | ||
1691 | * | ||
1692 | * If the file was found, the caller-provided mschmd_file structure will | ||
1693 | * be filled out like so: | ||
1694 | * - section: the correct value for the found file | ||
1695 | * - offset: the correct value for the found file | ||
1696 | * - length: the correct value for the found file | ||
1697 | * - all other structure elements: NULL or 0 | ||
1698 | * | ||
1699 | * If the file was not found, MSPACK_ERR_OK will still be returned as the | ||
1700 | * result, but the caller-provided structure will be filled out like so: | ||
1701 | * - section: NULL | ||
1702 | * - offset: 0 | ||
1703 | * - length: 0 | ||
1704 | * - all other structure elements: NULL or 0 | ||
1705 | * | ||
1706 | * This method is intended to be used in conjunction with CHM helpfiles | ||
1707 | * opened with fast_open(), but it also works with helpfiles opened | ||
1708 | * using the regular open(). | ||
1709 | * | ||
1710 | * @param self a self-referential pointer to the mschm_decompressor | ||
1711 | * instance being called | ||
1712 | * @param chm the CHM helpfile to search for the file | ||
1713 | * @param filename the filename of the file to search for | ||
1714 | * @param f_ptr a pointer to a caller-provded mschmd_file structure | ||
1715 | * @param f_size <tt>sizeof(struct mschmd_file)</tt> | ||
1716 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1717 | * @see open(), close(), fast_find(), extract() | ||
1718 | */ | ||
1719 | int (*fast_find)(struct mschm_decompressor *self, | ||
1720 | struct mschmd_header *chm, | ||
1721 | const char *filename, | ||
1722 | struct mschmd_file *f_ptr, | ||
1723 | int f_size); | ||
1724 | }; | ||
1725 | |||
1726 | /* --- support for .LIT (EBook) file format -------------------------------- */ | ||
1727 | |||
1728 | /** TODO */ | ||
1729 | struct mslit_compressor { | ||
1730 | int dummy; | ||
1731 | }; | ||
1732 | |||
1733 | /** TODO */ | ||
1734 | struct mslit_decompressor { | ||
1735 | int dummy; | ||
1736 | }; | ||
1737 | |||
1738 | |||
1739 | /* --- support for .HLP (MS Help) file format ------------------------------ */ | ||
1740 | |||
1741 | /** TODO */ | ||
1742 | struct mshlp_compressor { | ||
1743 | int dummy; | ||
1744 | }; | ||
1745 | |||
1746 | /** TODO */ | ||
1747 | struct mshlp_decompressor { | ||
1748 | int dummy; | ||
1749 | }; | ||
1750 | |||
1751 | |||
1752 | /* --- support for SZDD file format ---------------------------------------- */ | ||
1753 | |||
1754 | /** msszdd_compressor::set_param() parameter: the missing character */ | ||
1755 | #define MSSZDDC_PARAM_MISSINGCHAR (0) | ||
1756 | |||
1757 | /** msszddd_header::format value - a regular SZDD file */ | ||
1758 | #define MSSZDD_FMT_NORMAL (0) | ||
1759 | |||
1760 | /** msszddd_header::format value - a special QBasic SZDD file */ | ||
1761 | #define MSSZDD_FMT_QBASIC (1) | ||
1762 | |||
1763 | /** | ||
1764 | * A structure which represents an SZDD compressed file. | ||
1765 | * | ||
1766 | * All fields are READ ONLY. | ||
1767 | */ | ||
1768 | struct msszddd_header { | ||
1769 | /** The file format; either #MSSZDD_FMT_NORMAL or #MSSZDD_FMT_QBASIC */ | ||
1770 | int format; | ||
1771 | |||
1772 | /** The amount of data in the SZDD file once uncompressed. */ | ||
1773 | off_t length; | ||
1774 | |||
1775 | /** | ||
1776 | * The last character in the filename, traditionally replaced with an | ||
1777 | * underscore to show the file is compressed. The null character is used | ||
1778 | * to show that this character has not been stored (e.g. because the | ||
1779 | * filename is not known). Generally, only characters that may appear in | ||
1780 | * an MS-DOS filename (except ".") are valid. | ||
1781 | */ | ||
1782 | char missing_char; | ||
1783 | }; | ||
1784 | |||
1785 | /** | ||
1786 | * A compressor for the SZDD file format. | ||
1787 | * | ||
1788 | * All fields are READ ONLY. | ||
1789 | * | ||
1790 | * @see mspack_create_szdd_compressor(), mspack_destroy_szdd_compressor() | ||
1791 | */ | ||
1792 | struct msszdd_compressor { | ||
1793 | /** | ||
1794 | * Reads an input file and creates a compressed output file in the | ||
1795 | * SZDD compressed file format. The SZDD compression format is quick | ||
1796 | * but gives poor compression. It is possible for the compressed output | ||
1797 | * file to be larger than the input file. | ||
1798 | * | ||
1799 | * Conventionally, SZDD compressed files have the final character in | ||
1800 | * their filename replaced with an underscore, to show they are | ||
1801 | * compressed. The missing character is stored in the compressed file | ||
1802 | * itself. This is due to the restricted filename conventions of MS-DOS, | ||
1803 | * most operating systems, such as UNIX, simply append another file | ||
1804 | * extension to the existing filename. As mspack does not deal with | ||
1805 | * filenames, this is left up to you. If you wish to set the missing | ||
1806 | * character stored in the file header, use set_param() with the | ||
1807 | * #MSSZDDC_PARAM_MISSINGCHAR parameter. | ||
1808 | * | ||
1809 | * "Stream" compression (where the length of the input data is not | ||
1810 | * known) is not possible. The length of the input data is stored in the | ||
1811 | * header of the SZDD file and must therefore be known before any data | ||
1812 | * is compressed. Due to technical limitations of the file format, the | ||
1813 | * maximum size of uncompressed file that will be accepted is 2147483647 | ||
1814 | * bytes. | ||
1815 | * | ||
1816 | * @param self a self-referential pointer to the msszdd_compressor | ||
1817 | * instance being called | ||
1818 | * @param input the name of the file to compressed. This is passed | ||
1819 | * passed directly to mspack_system::open() | ||
1820 | * @param output the name of the file to write compressed data to. | ||
1821 | * This is passed directly to mspack_system::open(). | ||
1822 | * @param length the length of the uncompressed file, or -1 to indicate | ||
1823 | * that this should be determined automatically by using | ||
1824 | * mspack_system::seek() on the input file. | ||
1825 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1826 | * @see set_param() | ||
1827 | */ | ||
1828 | int (*compress)(struct msszdd_compressor *self, | ||
1829 | const char *input, | ||
1830 | const char *output, | ||
1831 | off_t length); | ||
1832 | |||
1833 | /** | ||
1834 | * Sets an SZDD compression engine parameter. | ||
1835 | * | ||
1836 | * The following parameters are defined: | ||
1837 | |||
1838 | * - #MSSZDDC_PARAM_CHARACTER: the "missing character", the last character | ||
1839 | * in the uncompressed file's filename, which is traditionally replaced | ||
1840 | * with an underscore to show the file is compressed. Traditionally, | ||
1841 | * this can only be a character that is a valid part of an MS-DOS, | ||
1842 | * filename, but libmspack permits any character between 0x00 and 0xFF | ||
1843 | * to be stored. 0x00 is the default, and it represents "no character | ||
1844 | * stored". | ||
1845 | * | ||
1846 | * @param self a self-referential pointer to the msszdd_compressor | ||
1847 | * instance being called | ||
1848 | * @param param the parameter to set | ||
1849 | * @param value the value to set the parameter to | ||
1850 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there | ||
1851 | * is a problem with either parameter or value. | ||
1852 | * @see compress() | ||
1853 | */ | ||
1854 | int (*set_param)(struct msszdd_compressor *self, | ||
1855 | int param, | ||
1856 | int value); | ||
1857 | |||
1858 | /** | ||
1859 | * Returns the error code set by the most recently called method. | ||
1860 | * | ||
1861 | * @param self a self-referential pointer to the msszdd_compressor | ||
1862 | * instance being called | ||
1863 | * @return the most recent error code | ||
1864 | * @see compress() | ||
1865 | */ | ||
1866 | int (*last_error)(struct mschm_decompressor *self); | ||
1867 | }; | ||
1868 | |||
1869 | /** | ||
1870 | * A decompressor for SZDD compressed files. | ||
1871 | * | ||
1872 | * All fields are READ ONLY. | ||
1873 | * | ||
1874 | * @see mspack_create_szdd_decompressor(), mspack_destroy_szdd_decompressor() | ||
1875 | */ | ||
1876 | struct msszdd_decompressor { | ||
1877 | /** | ||
1878 | * Opens a SZDD file and reads the header. | ||
1879 | * | ||
1880 | * If the file opened is a valid SZDD file, all headers will be read and | ||
1881 | * a msszddd_header structure will be returned. | ||
1882 | * | ||
1883 | * In the case of an error occuring, NULL is returned and the error code | ||
1884 | * is available from last_error(). | ||
1885 | * | ||
1886 | * The filename pointer should be considered "in use" until close() is | ||
1887 | * called on the SZDD file. | ||
1888 | * | ||
1889 | * @param self a self-referential pointer to the msszdd_decompressor | ||
1890 | * instance being called | ||
1891 | * @param filename the filename of the SZDD compressed file. This is | ||
1892 | * passed directly to mspack_system::open(). | ||
1893 | * @return a pointer to a msszddd_header structure, or NULL on failure | ||
1894 | * @see close() | ||
1895 | */ | ||
1896 | struct msszddd_header *(*open)(struct msszdd_decompressor *self, | ||
1897 | const char *filename); | ||
1898 | |||
1899 | /** | ||
1900 | * Closes a previously opened SZDD file. | ||
1901 | * | ||
1902 | * This closes a SZDD file and frees the msszddd_header associated with | ||
1903 | * it. | ||
1904 | * | ||
1905 | * The SZDD header pointer is now invalid and cannot be used again. | ||
1906 | * | ||
1907 | * @param self a self-referential pointer to the msszdd_decompressor | ||
1908 | * instance being called | ||
1909 | * @param szdd the SZDD file to close | ||
1910 | * @see open() | ||
1911 | */ | ||
1912 | void (*close)(struct msszdd_decompressor *self, | ||
1913 | struct msszddd_header *szdd); | ||
1914 | |||
1915 | /** | ||
1916 | * Extracts the compressed data from a SZDD file. | ||
1917 | * | ||
1918 | * This decompresses the compressed SZDD data stream and writes it to | ||
1919 | * an output file. | ||
1920 | * | ||
1921 | * @param self a self-referential pointer to the msszdd_decompressor | ||
1922 | * instance being called | ||
1923 | * @param szdd the SZDD file to extract data from | ||
1924 | * @param filename the filename to write the decompressed data to. This | ||
1925 | * is passed directly to mspack_system::open(). | ||
1926 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1927 | */ | ||
1928 | int (*extract)(struct msszdd_decompressor *self, | ||
1929 | struct msszddd_header *szdd, | ||
1930 | const char *filename); | ||
1931 | |||
1932 | /** | ||
1933 | * Decompresses an SZDD file to an output file in one step. | ||
1934 | * | ||
1935 | * This opens an SZDD file as input, reads the header, then decompresses | ||
1936 | * the compressed data immediately to an output file, finally closing | ||
1937 | * both the input and output file. It is more convenient to use than | ||
1938 | * open() then extract() then close(), if you do not need to know the | ||
1939 | * SZDD output size or missing character. | ||
1940 | * | ||
1941 | * @param self a self-referential pointer to the msszdd_decompressor | ||
1942 | * instance being called | ||
1943 | * @param input the filename of the input SZDD file. This is passed | ||
1944 | * directly to mspack_system::open(). | ||
1945 | * @param output the filename to write the decompressed data to. This | ||
1946 | * is passed directly to mspack_system::open(). | ||
1947 | * @return an error code, or MSPACK_ERR_OK if successful | ||
1948 | */ | ||
1949 | int (*decompress)(struct msszdd_decompressor *self, | ||
1950 | const char *input, | ||
1951 | const char *output); | ||
1952 | |||
1953 | /** | ||
1954 | * Returns the error code set by the most recently called method. | ||
1955 | * | ||
1956 | * This is useful for open() which does not return an | ||
1957 | * error code directly. | ||
1958 | * | ||
1959 | * @param self a self-referential pointer to the msszdd_decompressor | ||
1960 | * instance being called | ||
1961 | * @return the most recent error code | ||
1962 | * @see open(), extract(), decompress() | ||
1963 | */ | ||
1964 | int (*last_error)(struct msszdd_decompressor *self); | ||
1965 | }; | ||
1966 | |||
1967 | /* --- support for KWAJ file format ---------------------------------------- */ | ||
1968 | |||
1969 | /** mskwaj_compressor::set_param() parameter: compression type */ | ||
1970 | #define MSKWAJC_PARAM_COMP_TYPE (0) | ||
1971 | |||
1972 | /** mskwaj_compressor::set_param() parameter: include the length of the | ||
1973 | * uncompressed file in the header? | ||
1974 | */ | ||
1975 | #define MSKWAJC_PARAM_INCLUDE_LENGTH (1) | ||
1976 | |||
1977 | /** KWAJ compression type: no compression. */ | ||
1978 | #define MSKWAJ_COMP_NONE (0) | ||
1979 | /** KWAJ compression type: no compression, 0xFF XOR "encryption". */ | ||
1980 | #define MSKWAJ_COMP_XOR (1) | ||
1981 | /** KWAJ compression type: LZSS (same method as SZDD) */ | ||
1982 | #define MSKWAJ_COMP_SZDD (2) | ||
1983 | /** KWAJ compression type: LZ+Huffman compression */ | ||
1984 | #define MSKWAJ_COMP_LZH (3) | ||
1985 | /** KWAJ compression type: MSZIP */ | ||
1986 | #define MSKWAJ_COMP_MSZIP (4) | ||
1987 | |||
1988 | /** KWAJ optional header flag: decompressed file length is included */ | ||
1989 | #define MSKWAJ_HDR_HASLENGTH (0x01) | ||
1990 | |||
1991 | /** KWAJ optional header flag: unknown 2-byte structure is included */ | ||
1992 | #define MSKWAJ_HDR_HASUNKNOWN1 (0x02) | ||
1993 | |||
1994 | /** KWAJ optional header flag: unknown multi-sized structure is included */ | ||
1995 | #define MSKWAJ_HDR_HASUNKNOWN2 (0x04) | ||
1996 | |||
1997 | /** KWAJ optional header flag: file name (no extension) is included */ | ||
1998 | #define MSKWAJ_HDR_HASFILENAME (0x08) | ||
1999 | |||
2000 | /** KWAJ optional header flag: file extension is included */ | ||
2001 | #define MSKWAJ_HDR_HASFILEEXT (0x10) | ||
2002 | |||
2003 | /** KWAJ optional header flag: extra text is included */ | ||
2004 | #define MSKWAJ_HDR_HASEXTRATEXT (0x20) | ||
2005 | |||
2006 | /** | ||
2007 | * A structure which represents an KWAJ compressed file. | ||
2008 | * | ||
2009 | * All fields are READ ONLY. | ||
2010 | */ | ||
2011 | struct mskwajd_header { | ||
2012 | /** The compression type; should be one of #MSKWAJ_COMP_NONE, | ||
2013 | * #MSKWAJ_COMP_XOR, #MSKWAJ_COMP_SZDD or #MSKWAJ_COMP_LZH | ||
2014 | */ | ||
2015 | unsigned short comp_type; | ||
2016 | |||
2017 | /** The offset in the file where the compressed data stream begins */ | ||
2018 | off_t data_offset; | ||
2019 | |||
2020 | /** Flags indicating which optional headers were included. */ | ||
2021 | int headers; | ||
2022 | |||
2023 | /** The amount of uncompressed data in the file, or 0 if not present. */ | ||
2024 | off_t length; | ||
2025 | |||
2026 | /** output filename, or NULL if not present */ | ||
2027 | char *filename; | ||
2028 | |||
2029 | /** extra uncompressed data (usually text) in the header. | ||
2030 | * This data can contain nulls so use extra_length to get the size. | ||
2031 | */ | ||
2032 | char *extra; | ||
2033 | |||
2034 | /** length of extra uncompressed data in the header */ | ||
2035 | unsigned short extra_length; | ||
2036 | }; | ||
2037 | |||
2038 | /** | ||
2039 | * A compressor for the KWAJ file format. | ||
2040 | * | ||
2041 | * All fields are READ ONLY. | ||
2042 | * | ||
2043 | * @see mspack_create_kwaj_compressor(), mspack_destroy_kwaj_compressor() | ||
2044 | */ | ||
2045 | struct mskwaj_compressor { | ||
2046 | /** | ||
2047 | * Reads an input file and creates a compressed output file in the | ||
2048 | * KWAJ compressed file format. The KWAJ compression format is quick | ||
2049 | * but gives poor compression. It is possible for the compressed output | ||
2050 | * file to be larger than the input file. | ||
2051 | * | ||
2052 | * @param self a self-referential pointer to the mskwaj_compressor | ||
2053 | * instance being called | ||
2054 | * @param input the name of the file to compressed. This is passed | ||
2055 | * passed directly to mspack_system::open() | ||
2056 | * @param output the name of the file to write compressed data to. | ||
2057 | * This is passed directly to mspack_system::open(). | ||
2058 | * @param length the length of the uncompressed file, or -1 to indicate | ||
2059 | * that this should be determined automatically by using | ||
2060 | * mspack_system::seek() on the input file. | ||
2061 | * @return an error code, or MSPACK_ERR_OK if successful | ||
2062 | * @see set_param() | ||
2063 | */ | ||
2064 | int (*compress)(struct mskwaj_compressor *self, | ||
2065 | const char *input, | ||
2066 | const char *output, | ||
2067 | off_t length); | ||
2068 | |||
2069 | /** | ||
2070 | * Sets an KWAJ compression engine parameter. | ||
2071 | * | ||
2072 | * The following parameters are defined: | ||
2073 | * | ||
2074 | * - #MSKWAJC_PARAM_COMP_TYPE: the compression method to use. Must | ||
2075 | * be one of #MSKWAJC_COMP_NONE, #MSKWAJC_COMP_XOR, #MSKWAJ_COMP_SZDD | ||
2076 | * or #MSKWAJ_COMP_LZH. The default is #MSKWAJ_COMP_LZH. | ||
2077 | * | ||
2078 | * - #MSKWAJC_PARAM_INCLUDE_LENGTH: a boolean; should the compressed | ||
2079 | * output file should include the uncompressed length of the input | ||
2080 | * file in the header? This adds 4 bytes to the size of the output | ||
2081 | * file. A value of zero says "no", non-zero says "yes". The default | ||
2082 | * is "no". | ||
2083 | * | ||
2084 | * @param self a self-referential pointer to the mskwaj_compressor | ||
2085 | * instance being called | ||
2086 | * @param param the parameter to set | ||
2087 | * @param value the value to set the parameter to | ||
2088 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there | ||
2089 | * is a problem with either parameter or value. | ||
2090 | * @see generate() | ||
2091 | */ | ||
2092 | int (*set_param)(struct mskwaj_compressor *self, | ||
2093 | int param, | ||
2094 | int value); | ||
2095 | |||
2096 | |||
2097 | /** | ||
2098 | * Sets the original filename of the file before compression, | ||
2099 | * which will be stored in the header of the output file. | ||
2100 | * | ||
2101 | * The filename should be a null-terminated string, it must be an | ||
2102 | * MS-DOS "8.3" type filename (up to 8 bytes for the filename, then | ||
2103 | * optionally a "." and up to 3 bytes for a filename extension). | ||
2104 | * | ||
2105 | * If NULL is passed as the filename, no filename is included in the | ||
2106 | * header. This is the default. | ||
2107 | * | ||
2108 | * @param self a self-referential pointer to the mskwaj_compressor | ||
2109 | * instance being called | ||
2110 | * @param filename the original filename to use | ||
2111 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if the | ||
2112 | * filename is too long | ||
2113 | */ | ||
2114 | int (*set_filename)(struct mskwaj_compressor *self, | ||
2115 | const char *filename); | ||
2116 | |||
2117 | /** | ||
2118 | * Sets arbitrary data that will be stored in the header of the | ||
2119 | * output file, uncompressed. It can be up to roughly 64 kilobytes, | ||
2120 | * as the overall size of the header must not exceed 65535 bytes. | ||
2121 | * The data can contain null bytes if desired. | ||
2122 | * | ||
2123 | * If NULL is passed as the data pointer, or zero is passed as the | ||
2124 | * length, no extra data is included in the header. This is the | ||
2125 | * default. | ||
2126 | * | ||
2127 | * @param self a self-referential pointer to the mskwaj_compressor | ||
2128 | * instance being called | ||
2129 | * @param data a pointer to the data to be stored in the header | ||
2130 | * @param bytes the length of the data in bytes | ||
2131 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS extra data | ||
2132 | * is too long | ||
2133 | */ | ||
2134 | int (*set_extra_data)(struct mskwaj_compressor *self, | ||
2135 | void *data, | ||
2136 | size_t bytes); | ||
2137 | |||
2138 | /** | ||
2139 | * Returns the error code set by the most recently called method. | ||
2140 | * | ||
2141 | * @param self a self-referential pointer to the mskwaj_compressor | ||
2142 | * instance being called | ||
2143 | * @return the most recent error code | ||
2144 | * @see compress() | ||
2145 | */ | ||
2146 | int (*last_error)(struct mschm_decompressor *self); | ||
2147 | }; | ||
2148 | |||
2149 | /** | ||
2150 | * A decompressor for KWAJ compressed files. | ||
2151 | * | ||
2152 | * All fields are READ ONLY. | ||
2153 | * | ||
2154 | * @see mspack_create_kwaj_decompressor(), mspack_destroy_kwaj_decompressor() | ||
2155 | */ | ||
2156 | struct mskwaj_decompressor { | ||
2157 | /** | ||
2158 | * Opens a KWAJ file and reads the header. | ||
2159 | * | ||
2160 | * If the file opened is a valid KWAJ file, all headers will be read and | ||
2161 | * a mskwajd_header structure will be returned. | ||
2162 | * | ||
2163 | * In the case of an error occuring, NULL is returned and the error code | ||
2164 | * is available from last_error(). | ||
2165 | * | ||
2166 | * The filename pointer should be considered "in use" until close() is | ||
2167 | * called on the KWAJ file. | ||
2168 | * | ||
2169 | * @param self a self-referential pointer to the mskwaj_decompressor | ||
2170 | * instance being called | ||
2171 | * @param filename the filename of the KWAJ compressed file. This is | ||
2172 | * passed directly to mspack_system::open(). | ||
2173 | * @return a pointer to a mskwajd_header structure, or NULL on failure | ||
2174 | * @see close() | ||
2175 | */ | ||
2176 | struct mskwajd_header *(*open)(struct mskwaj_decompressor *self, | ||
2177 | const char *filename); | ||
2178 | |||
2179 | /** | ||
2180 | * Closes a previously opened KWAJ file. | ||
2181 | * | ||
2182 | * This closes a KWAJ file and frees the mskwajd_header associated | ||
2183 | * with it. The KWAJ header pointer is now invalid and cannot be | ||
2184 | * used again. | ||
2185 | * | ||
2186 | * @param self a self-referential pointer to the mskwaj_decompressor | ||
2187 | * instance being called | ||
2188 | * @param kwaj the KWAJ file to close | ||
2189 | * @see open() | ||
2190 | */ | ||
2191 | void (*close)(struct mskwaj_decompressor *self, | ||
2192 | struct mskwajd_header *kwaj); | ||
2193 | |||
2194 | /** | ||
2195 | * Extracts the compressed data from a KWAJ file. | ||
2196 | * | ||
2197 | * This decompresses the compressed KWAJ data stream and writes it to | ||
2198 | * an output file. | ||
2199 | * | ||
2200 | * @param self a self-referential pointer to the mskwaj_decompressor | ||
2201 | * instance being called | ||
2202 | * @param kwaj the KWAJ file to extract data from | ||
2203 | * @param filename the filename to write the decompressed data to. This | ||
2204 | * is passed directly to mspack_system::open(). | ||
2205 | * @return an error code, or MSPACK_ERR_OK if successful | ||
2206 | */ | ||
2207 | int (*extract)(struct mskwaj_decompressor *self, | ||
2208 | struct mskwajd_header *kwaj, | ||
2209 | const char *filename); | ||
2210 | |||
2211 | /** | ||
2212 | * Decompresses an KWAJ file to an output file in one step. | ||
2213 | * | ||
2214 | * This opens an KWAJ file as input, reads the header, then decompresses | ||
2215 | * the compressed data immediately to an output file, finally closing | ||
2216 | * both the input and output file. It is more convenient to use than | ||
2217 | * open() then extract() then close(), if you do not need to know the | ||
2218 | * KWAJ output size or output filename. | ||
2219 | * | ||
2220 | * @param self a self-referential pointer to the mskwaj_decompressor | ||
2221 | * instance being called | ||
2222 | * @param input the filename of the input KWAJ file. This is passed | ||
2223 | * directly to mspack_system::open(). | ||
2224 | * @param output the filename to write the decompressed data to. This | ||
2225 | * is passed directly to mspack_system::open(). | ||
2226 | * @return an error code, or MSPACK_ERR_OK if successful | ||
2227 | */ | ||
2228 | int (*decompress)(struct mskwaj_decompressor *self, | ||
2229 | const char *input, | ||
2230 | const char *output); | ||
2231 | |||
2232 | /** | ||
2233 | * Returns the error code set by the most recently called method. | ||
2234 | * | ||
2235 | * This is useful for open() which does not return an | ||
2236 | * error code directly. | ||
2237 | * | ||
2238 | * @param self a self-referential pointer to the mskwaj_decompressor | ||
2239 | * instance being called | ||
2240 | * @return the most recent error code | ||
2241 | * @see open(), search() | ||
2242 | */ | ||
2243 | int (*last_error)(struct mskwaj_decompressor *self); | ||
2244 | }; | ||
2245 | |||
2246 | /* --- support for .LZX (Offline Address Book) file format ----------------- */ | ||
2247 | |||
2248 | /** | ||
2249 | * A compressor for the Offline Address Book (OAB) format. | ||
2250 | * | ||
2251 | * All fields are READ ONLY. | ||
2252 | * | ||
2253 | * @see mspack_create_oab_compressor(), mspack_destroy_oab_compressor() | ||
2254 | */ | ||
2255 | struct msoab_compressor { | ||
2256 | /** | ||
2257 | * Compress a full OAB file. | ||
2258 | * | ||
2259 | * The input file will be read and the compressed contents written to the | ||
2260 | * output file. | ||
2261 | * | ||
2262 | * @param self a self-referential pointer to the msoab_decompressor | ||
2263 | * instance being called | ||
2264 | * @param input the filename of the input file. This is passed | ||
2265 | * directly to mspack_system::open(). | ||
2266 | * @param output the filename of the output file. This is passed | ||
2267 | * directly to mspack_system::open(). | ||
2268 | * @return an error code, or MSPACK_ERR_OK if successful | ||
2269 | */ | ||
2270 | int (*compress) (struct msoab_compressor *self, | ||
2271 | const char *input, | ||
2272 | const char *output); | ||
2273 | |||
2274 | /** | ||
2275 | * Generate a compressed incremental OAB patch file. | ||
2276 | * | ||
2277 | * The two uncompressed files "input" and "base" will be read, and an | ||
2278 | * incremental patch to generate "input" from "base" will be written to | ||
2279 | * the output file. | ||
2280 | * | ||
2281 | * @param self a self-referential pointer to the msoab_compressor | ||
2282 | * instance being called | ||
2283 | * @param input the filename of the input file containing the new | ||
2284 | * version of its contents. This is passed directly | ||
2285 | * to mspack_system::open(). | ||
2286 | * @param base the filename of the original base file containing | ||
2287 | * the old version of its contents, against which the | ||
2288 | * incremental patch shall generated. This is passed | ||
2289 | * directly to mspack_system::open(). | ||
2290 | * @param output the filename of the output file. This is passed | ||
2291 | * directly to mspack_system::open(). | ||
2292 | * @return an error code, or MSPACK_ERR_OK if successful | ||
2293 | */ | ||
2294 | int (*compress_incremental) (struct msoab_compressor *self, | ||
2295 | const char *input, | ||
2296 | const char *base, | ||
2297 | const char *output); | ||
2298 | }; | ||
2299 | |||
2300 | /** | ||
2301 | * A decompressor for .LZX (Offline Address Book) files | ||
2302 | * | ||
2303 | * All fields are READ ONLY. | ||
2304 | * | ||
2305 | * @see mspack_create_oab_decompressor(), mspack_destroy_oab_decompressor() | ||
2306 | */ | ||
2307 | struct msoab_decompressor { | ||
2308 | /** | ||
2309 | * Decompresses a full Offline Address Book file. | ||
2310 | * | ||
2311 | * If the input file is a valid compressed Offline Address Book file, | ||
2312 | * it will be read and the decompressed contents will be written to | ||
2313 | * the output file. | ||
2314 | * | ||
2315 | * @param self a self-referential pointer to the msoab_decompressor | ||
2316 | * instance being called | ||
2317 | * @param input the filename of the input file. This is passed | ||
2318 | * directly to mspack_system::open(). | ||
2319 | * @param output the filename of the output file. This is passed | ||
2320 | * directly to mspack_system::open(). | ||
2321 | * @return an error code, or MSPACK_ERR_OK if successful | ||
2322 | */ | ||
2323 | int (*decompress) (struct msoab_decompressor *self, | ||
2324 | const char *input, | ||
2325 | const char *output); | ||
2326 | |||
2327 | /** | ||
2328 | * Decompresses an Offline Address Book with an incremental patch file. | ||
2329 | * | ||
2330 | * This requires both a full UNCOMPRESSED Offline Address Book file to | ||
2331 | * act as the "base", and a compressed incremental patch file as input. | ||
2332 | * If the input file is valid, it will be decompressed with reference to | ||
2333 | * the base file, and the decompressed contents will be written to the | ||
2334 | * output file. | ||
2335 | * | ||
2336 | * There is no way to tell what the right base file is for the given | ||
2337 | * incremental patch, but if you get it wrong, this will usually result | ||
2338 | * in incorrect data being decompressed, which will then fail a checksum | ||
2339 | * test. | ||
2340 | * | ||
2341 | * @param self a self-referential pointer to the msoab_decompressor | ||
2342 | * instance being called | ||
2343 | * @param input the filename of the input file. This is passed | ||
2344 | * directly to mspack_system::open(). | ||
2345 | * @param base the filename of the base file to which the | ||
2346 | * incremental patch shall be applied. This is passed | ||
2347 | * directly to mspack_system::open(). | ||
2348 | * @param output the filename of the output file. This is passed | ||
2349 | * directly to mspack_system::open(). | ||
2350 | * @return an error code, or MSPACK_ERR_OK if successful | ||
2351 | */ | ||
2352 | int (*decompress_incremental) (struct msoab_decompressor *self, | ||
2353 | const char *input, | ||
2354 | const char *base, | ||
2355 | const char *output); | ||
2356 | |||
2357 | /** | ||
2358 | * Sets an OAB decompression engine parameter. Available only in OAB | ||
2359 | * decompressor version 2 and above. | ||
2360 | * | ||
2361 | * - #MSOABD_PARAM_DECOMPBUF: How many bytes should be used as an input | ||
2362 | * buffer by decompressors? The minimum value is 16. The default value | ||
2363 | * is 4096. | ||
2364 | * | ||
2365 | * @param self a self-referential pointer to the msoab_decompressor | ||
2366 | * instance being called | ||
2367 | * @param param the parameter to set | ||
2368 | * @param value the value to set the parameter to | ||
2369 | * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there | ||
2370 | * is a problem with either parameter or value. | ||
2371 | */ | ||
2372 | int (*set_param)(struct msoab_decompressor *self, | ||
2373 | int param, | ||
2374 | int value); | ||
2375 | |||
2376 | }; | ||
2377 | |||
2378 | /** msoab_decompressor::set_param() parameter: size of decompression buffer */ | ||
2379 | #define MSOABD_PARAM_DECOMPBUF (0) | ||
2380 | |||
2381 | #ifdef __cplusplus | ||
2382 | } | ||
2383 | #endif | ||
2384 | |||
2385 | #endif | ||
diff --git a/utils/rbutilqt/mspack/mszip.h b/utils/rbutilqt/mspack/mszip.h new file mode 100644 index 0000000000..2cd608234e --- /dev/null +++ b/utils/rbutilqt/mspack/mszip.h | |||
@@ -0,0 +1,126 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * The deflate method was created by Phil Katz. MSZIP is equivalent to the | ||
5 | * deflate method. | ||
6 | * | ||
7 | * libmspack is free software; you can redistribute it and/or modify it under | ||
8 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
9 | * | ||
10 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
11 | */ | ||
12 | |||
13 | #ifndef MSPACK_MSZIP_H | ||
14 | #define MSPACK_MSZIP_H 1 | ||
15 | |||
16 | #ifdef __cplusplus | ||
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 | /* decompresses an entire MS-ZIP stream in a KWAJ file. Acts very much | ||
112 | * like mszipd_decompress(), but doesn't take an out_bytes parameter | ||
113 | */ | ||
114 | extern int mszipd_decompress_kwaj(struct mszipd_stream *zip); | ||
115 | |||
116 | /* frees all stream associated with an MS-ZIP data stream | ||
117 | * | ||
118 | * - calls system->free() using the system pointer given in mszipd_init() | ||
119 | */ | ||
120 | void mszipd_free(struct mszipd_stream *zip); | ||
121 | |||
122 | #ifdef __cplusplus | ||
123 | } | ||
124 | #endif | ||
125 | |||
126 | #endif | ||
diff --git a/utils/rbutilqt/mspack/mszipc.c b/utils/rbutilqt/mspack/mszipc.c new file mode 100644 index 0000000000..2f14b9ac83 --- /dev/null +++ b/utils/rbutilqt/mspack/mszipc.c | |||
@@ -0,0 +1,18 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * The deflate method was created by Phil Katz. MSZIP is equivalent to the | ||
5 | * deflate method. | ||
6 | * | ||
7 | * libmspack is free software; you can redistribute it and/or modify it under | ||
8 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
9 | * | ||
10 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
11 | */ | ||
12 | |||
13 | /* MS-ZIP compression implementation */ | ||
14 | |||
15 | #include "system-mspack.h" | ||
16 | #include "mszip.h" | ||
17 | |||
18 | /* todo */ | ||
diff --git a/utils/rbutilqt/mspack/mszipd.c b/utils/rbutilqt/mspack/mszipd.c new file mode 100644 index 0000000000..c1b02b1207 --- /dev/null +++ b/utils/rbutilqt/mspack/mszipd.c | |||
@@ -0,0 +1,515 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2010 Stuart Caie. | ||
3 | * | ||
4 | * The deflate method was created by Phil Katz. MSZIP is equivalent to the | ||
5 | * deflate method. | ||
6 | * | ||
7 | * libmspack is free software; you can redistribute it and/or modify it under | ||
8 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
9 | * | ||
10 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
11 | */ | ||
12 | |||
13 | /* MS-ZIP decompression implementation. */ | ||
14 | |||
15 | #include "system-mspack.h" | ||
16 | #include "mszip.h" | ||
17 | |||
18 | /* import bit-reading macros and code */ | ||
19 | #define BITS_TYPE struct mszipd_stream | ||
20 | #define BITS_VAR zip | ||
21 | #define BITS_ORDER_LSB | ||
22 | #define BITS_LSB_TABLE | ||
23 | #define READ_BYTES do { \ | ||
24 | READ_IF_NEEDED; \ | ||
25 | INJECT_BITS(*i_ptr++, 8); \ | ||
26 | } while (0) | ||
27 | #include "readbits.h" | ||
28 | |||
29 | /* import huffman macros and code */ | ||
30 | #define TABLEBITS(tbl) MSZIP_##tbl##_TABLEBITS | ||
31 | #define MAXSYMBOLS(tbl) MSZIP_##tbl##_MAXSYMBOLS | ||
32 | #define HUFF_TABLE(tbl,idx) zip->tbl##_table[idx] | ||
33 | #define HUFF_LEN(tbl,idx) zip->tbl##_len[idx] | ||
34 | #define HUFF_ERROR return INF_ERR_HUFFSYM | ||
35 | #include "readhuff.h" | ||
36 | |||
37 | #define FLUSH_IF_NEEDED do { \ | ||
38 | if (zip->window_posn == MSZIP_FRAME_SIZE) { \ | ||
39 | if (zip->flush_window(zip, MSZIP_FRAME_SIZE)) { \ | ||
40 | return INF_ERR_FLUSH; \ | ||
41 | } \ | ||
42 | zip->window_posn = 0; \ | ||
43 | } \ | ||
44 | } while (0) | ||
45 | |||
46 | /* match lengths for literal codes 257.. 285 */ | ||
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 | /* round up input buffer size to multiple of two */ | ||
353 | input_buffer_size = (input_buffer_size + 1) & -2; | ||
354 | if (input_buffer_size < 2) return NULL; | ||
355 | |||
356 | /* allocate decompression state */ | ||
357 | if (!(zip = (struct mszipd_stream *) system->alloc(system, sizeof(struct mszipd_stream)))) { | ||
358 | return NULL; | ||
359 | } | ||
360 | |||
361 | /* allocate input buffer */ | ||
362 | zip->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size); | ||
363 | if (!zip->inbuf) { | ||
364 | system->free(zip); | ||
365 | return NULL; | ||
366 | } | ||
367 | |||
368 | /* initialise decompression state */ | ||
369 | zip->sys = system; | ||
370 | zip->input = input; | ||
371 | zip->output = output; | ||
372 | zip->inbuf_size = input_buffer_size; | ||
373 | zip->input_end = 0; | ||
374 | zip->error = MSPACK_ERR_OK; | ||
375 | zip->repair_mode = repair_mode; | ||
376 | zip->flush_window = &mszipd_flush_window; | ||
377 | |||
378 | zip->i_ptr = zip->i_end = &zip->inbuf[0]; | ||
379 | zip->o_ptr = zip->o_end = NULL; | ||
380 | zip->bit_buffer = 0; zip->bits_left = 0; | ||
381 | return zip; | ||
382 | } | ||
383 | |||
384 | int mszipd_decompress(struct mszipd_stream *zip, off_t out_bytes) { | ||
385 | /* for the bit buffer */ | ||
386 | register unsigned int bit_buffer; | ||
387 | register int bits_left; | ||
388 | unsigned char *i_ptr, *i_end; | ||
389 | |||
390 | int i, state, error; | ||
391 | |||
392 | /* easy answers */ | ||
393 | if (!zip || (out_bytes < 0)) return MSPACK_ERR_ARGS; | ||
394 | if (zip->error) return zip->error; | ||
395 | |||
396 | /* flush out any stored-up bytes before we begin */ | ||
397 | i = zip->o_end - zip->o_ptr; | ||
398 | if ((off_t) i > out_bytes) i = (int) out_bytes; | ||
399 | if (i) { | ||
400 | if (zip->sys->write(zip->output, zip->o_ptr, i) != i) { | ||
401 | return zip->error = MSPACK_ERR_WRITE; | ||
402 | } | ||
403 | zip->o_ptr += i; | ||
404 | out_bytes -= i; | ||
405 | } | ||
406 | if (out_bytes == 0) return MSPACK_ERR_OK; | ||
407 | |||
408 | |||
409 | while (out_bytes > 0) { | ||
410 | /* unpack another block */ | ||
411 | RESTORE_BITS; | ||
412 | |||
413 | /* skip to next read 'CK' header */ | ||
414 | i = bits_left & 7; REMOVE_BITS(i); /* align to bytestream */ | ||
415 | state = 0; | ||
416 | do { | ||
417 | READ_BITS(i, 8); | ||
418 | if (i == 'C') state = 1; | ||
419 | else if ((state == 1) && (i == 'K')) state = 2; | ||
420 | else state = 0; | ||
421 | } while (state != 2); | ||
422 | |||
423 | /* inflate a block, repair and realign if necessary */ | ||
424 | zip->window_posn = 0; | ||
425 | zip->bytes_output = 0; | ||
426 | STORE_BITS; | ||
427 | if ((error = inflate(zip))) { | ||
428 | D(("inflate error %d", error)) | ||
429 | if (zip->repair_mode) { | ||
430 | /* recover partially-inflated buffers */ | ||
431 | if (zip->bytes_output == 0 && zip->window_posn > 0) { | ||
432 | zip->flush_window(zip, zip->window_posn); | ||
433 | } | ||
434 | zip->sys->message(NULL, "MSZIP error, %u bytes of data lost.", | ||
435 | MSZIP_FRAME_SIZE - zip->bytes_output); | ||
436 | for (i = zip->bytes_output; i < MSZIP_FRAME_SIZE; i++) { | ||
437 | zip->window[i] = '\0'; | ||
438 | } | ||
439 | zip->bytes_output = MSZIP_FRAME_SIZE; | ||
440 | } | ||
441 | else { | ||
442 | return zip->error = (error > 0) ? error : MSPACK_ERR_DECRUNCH; | ||
443 | } | ||
444 | } | ||
445 | zip->o_ptr = &zip->window[0]; | ||
446 | zip->o_end = &zip->o_ptr[zip->bytes_output]; | ||
447 | |||
448 | /* write a frame */ | ||
449 | i = (out_bytes < (off_t)zip->bytes_output) ? | ||
450 | (int)out_bytes : zip->bytes_output; | ||
451 | if (zip->sys->write(zip->output, zip->o_ptr, i) != i) { | ||
452 | return zip->error = MSPACK_ERR_WRITE; | ||
453 | } | ||
454 | |||
455 | /* mspack errors (i.e. read errors) are fatal and can't be recovered */ | ||
456 | if ((error > 0) && zip->repair_mode) return error; | ||
457 | |||
458 | zip->o_ptr += i; | ||
459 | out_bytes -= i; | ||
460 | } | ||
461 | |||
462 | if (out_bytes) { | ||
463 | D(("bytes left to output")) | ||
464 | return zip->error = MSPACK_ERR_DECRUNCH; | ||
465 | } | ||
466 | return MSPACK_ERR_OK; | ||
467 | } | ||
468 | |||
469 | int mszipd_decompress_kwaj(struct mszipd_stream *zip) { | ||
470 | /* for the bit buffer */ | ||
471 | register unsigned int bit_buffer; | ||
472 | register int bits_left; | ||
473 | unsigned char *i_ptr, *i_end; | ||
474 | |||
475 | int i, error, block_len; | ||
476 | |||
477 | /* unpack blocks until block_len == 0 */ | ||
478 | for (;;) { | ||
479 | RESTORE_BITS; | ||
480 | |||
481 | /* align to bytestream, read block_len */ | ||
482 | i = bits_left & 7; REMOVE_BITS(i); | ||
483 | READ_BITS(block_len, 8); | ||
484 | READ_BITS(i, 8); block_len |= i << 8; | ||
485 | |||
486 | if (block_len == 0) break; | ||
487 | |||
488 | /* read "CK" header */ | ||
489 | READ_BITS(i, 8); if (i != 'C') return MSPACK_ERR_DATAFORMAT; | ||
490 | READ_BITS(i, 8); if (i != 'K') return MSPACK_ERR_DATAFORMAT; | ||
491 | |||
492 | /* inflate block */ | ||
493 | zip->window_posn = 0; | ||
494 | zip->bytes_output = 0; | ||
495 | STORE_BITS; | ||
496 | if ((error = inflate(zip))) { | ||
497 | D(("inflate error %d", error)) | ||
498 | return zip->error = (error > 0) ? error : MSPACK_ERR_DECRUNCH; | ||
499 | } | ||
500 | |||
501 | /* write inflated block */ | ||
502 | if (zip->sys->write(zip->output, &zip->window[0], zip->bytes_output) | ||
503 | != zip->bytes_output) return zip->error = MSPACK_ERR_WRITE; | ||
504 | } | ||
505 | return MSPACK_ERR_OK; | ||
506 | } | ||
507 | |||
508 | void mszipd_free(struct mszipd_stream *zip) { | ||
509 | struct mspack_system *sys; | ||
510 | if (zip) { | ||
511 | sys = zip->sys; | ||
512 | sys->free(zip->inbuf); | ||
513 | sys->free(zip); | ||
514 | } | ||
515 | } | ||
diff --git a/utils/rbutilqt/mspack/qtm.h b/utils/rbutilqt/mspack/qtm.h new file mode 100644 index 0000000000..20a38538a2 --- /dev/null +++ b/utils/rbutilqt/mspack/qtm.h | |||
@@ -0,0 +1,128 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * The Quantum method was created by David Stafford, adapted by Microsoft | ||
5 | * Corporation. | ||
6 | * | ||
7 | * libmspack is free software; you can redistribute it and/or modify it under | ||
8 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
9 | * | ||
10 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
11 | */ | ||
12 | |||
13 | #ifndef MSPACK_QTM_H | ||
14 | #define MSPACK_QTM_H 1 | ||
15 | |||
16 | #ifdef __cplusplus | ||
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/utils/rbutilqt/mspack/qtmd.c b/utils/rbutilqt/mspack/qtmd.c new file mode 100644 index 0000000000..58e4787b7f --- /dev/null +++ b/utils/rbutilqt/mspack/qtmd.c | |||
@@ -0,0 +1,490 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * The Quantum method was created by David Stafford, adapted by Microsoft | ||
5 | * Corporation. | ||
6 | * | ||
7 | * This decompressor is based on an implementation by Matthew Russotto, used | ||
8 | * with permission. | ||
9 | * | ||
10 | * libmspack is free software; you can redistribute it and/or modify it under | ||
11 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
12 | * | ||
13 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
14 | */ | ||
15 | |||
16 | /* Quantum decompression implementation */ | ||
17 | |||
18 | /* This decompressor was researched and implemented by Matthew Russotto. It | ||
19 | * has since been tidied up by Stuart Caie. More information can be found at | ||
20 | * http://www.speakeasy.org/~russotto/quantumcomp.html | ||
21 | */ | ||
22 | |||
23 | #include "system-mspack.h" | ||
24 | #include "qtm.h" | ||
25 | |||
26 | /* import bit-reading macros and code */ | ||
27 | #define BITS_TYPE struct qtmd_stream | ||
28 | #define BITS_VAR qtm | ||
29 | #define BITS_ORDER_MSB | ||
30 | #define READ_BYTES do { \ | ||
31 | unsigned char b0, b1; \ | ||
32 | READ_IF_NEEDED; b0 = *i_ptr++; \ | ||
33 | READ_IF_NEEDED; b1 = *i_ptr++; \ | ||
34 | INJECT_BITS((b0 << 8) | b1, 16); \ | ||
35 | } while (0) | ||
36 | #include "readbits.h" | ||
37 | |||
38 | /* Quantum static data tables: | ||
39 | * | ||
40 | * Quantum uses 'position slots' to represent match offsets. For every | ||
41 | * match, a small 'position slot' number and a small offset from that slot | ||
42 | * are encoded instead of one large offset. | ||
43 | * | ||
44 | * position_base[] is an index to the position slot bases | ||
45 | * | ||
46 | * extra_bits[] states how many bits of offset-from-base data is needed. | ||
47 | * | ||
48 | * length_base[] and length_extra[] are equivalent in function, but are | ||
49 | * used for encoding selector 6 (variable length match) match lengths, | ||
50 | * instead of match offsets. | ||
51 | * | ||
52 | * They are generated with the following code: | ||
53 | * unsigned int i, offset; | ||
54 | * for (i = 0, offset = 0; i < 42; i++) { | ||
55 | * position_base[i] = offset; | ||
56 | * extra_bits[i] = ((i < 2) ? 0 : (i - 2)) >> 1; | ||
57 | * offset += 1 << extra_bits[i]; | ||
58 | * } | ||
59 | * for (i = 0, offset = 0; i < 26; i++) { | ||
60 | * length_base[i] = offset; | ||
61 | * length_extra[i] = (i < 2 ? 0 : i - 2) >> 2; | ||
62 | * offset += 1 << length_extra[i]; | ||
63 | * } | ||
64 | * length_base[26] = 254; length_extra[26] = 0; | ||
65 | */ | ||
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 | /* round up input buffer size to multiple of two */ | ||
201 | input_buffer_size = (input_buffer_size + 1) & -2; | ||
202 | if (input_buffer_size < 2) return NULL; | ||
203 | |||
204 | /* allocate decompression state */ | ||
205 | if (!(qtm = (struct qtmd_stream *) system->alloc(system, sizeof(struct qtmd_stream)))) { | ||
206 | return NULL; | ||
207 | } | ||
208 | |||
209 | /* allocate decompression window and input buffer */ | ||
210 | qtm->window = (unsigned char *) system->alloc(system, (size_t) window_size); | ||
211 | qtm->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size); | ||
212 | if (!qtm->window || !qtm->inbuf) { | ||
213 | system->free(qtm->window); | ||
214 | system->free(qtm->inbuf); | ||
215 | system->free(qtm); | ||
216 | return NULL; | ||
217 | } | ||
218 | |||
219 | /* initialise decompression state */ | ||
220 | qtm->sys = system; | ||
221 | qtm->input = input; | ||
222 | qtm->output = output; | ||
223 | qtm->inbuf_size = input_buffer_size; | ||
224 | qtm->window_size = window_size; | ||
225 | qtm->window_posn = 0; | ||
226 | qtm->frame_todo = QTM_FRAME_SIZE; | ||
227 | qtm->header_read = 0; | ||
228 | qtm->error = MSPACK_ERR_OK; | ||
229 | |||
230 | qtm->i_ptr = qtm->i_end = &qtm->inbuf[0]; | ||
231 | qtm->o_ptr = qtm->o_end = &qtm->window[0]; | ||
232 | qtm->input_end = 0; | ||
233 | qtm->bits_left = 0; | ||
234 | qtm->bit_buffer = 0; | ||
235 | |||
236 | /* initialise arithmetic coding models | ||
237 | * - model 4 depends on window size, ranges from 20 to 24 | ||
238 | * - model 5 depends on window size, ranges from 20 to 36 | ||
239 | * - model 6pos depends on window size, ranges from 20 to 42 | ||
240 | */ | ||
241 | i = window_bits * 2; | ||
242 | qtmd_init_model(&qtm->model0, &qtm->m0sym[0], 0, 64); | ||
243 | qtmd_init_model(&qtm->model1, &qtm->m1sym[0], 64, 64); | ||
244 | qtmd_init_model(&qtm->model2, &qtm->m2sym[0], 128, 64); | ||
245 | qtmd_init_model(&qtm->model3, &qtm->m3sym[0], 192, 64); | ||
246 | qtmd_init_model(&qtm->model4, &qtm->m4sym[0], 0, (i > 24) ? 24 : i); | ||
247 | qtmd_init_model(&qtm->model5, &qtm->m5sym[0], 0, (i > 36) ? 36 : i); | ||
248 | qtmd_init_model(&qtm->model6, &qtm->m6sym[0], 0, i); | ||
249 | qtmd_init_model(&qtm->model6len, &qtm->m6lsym[0], 0, 27); | ||
250 | qtmd_init_model(&qtm->model7, &qtm->m7sym[0], 0, 7); | ||
251 | |||
252 | /* all ok */ | ||
253 | return qtm; | ||
254 | } | ||
255 | |||
256 | int qtmd_decompress(struct qtmd_stream *qtm, off_t out_bytes) { | ||
257 | unsigned int frame_todo, frame_end, window_posn, match_offset, range; | ||
258 | unsigned char *window, *i_ptr, *i_end, *runsrc, *rundest; | ||
259 | int i, j, selector, extra, sym, match_length; | ||
260 | unsigned short H, L, C, symf; | ||
261 | |||
262 | register unsigned int bit_buffer; | ||
263 | register unsigned char bits_left; | ||
264 | |||
265 | /* easy answers */ | ||
266 | if (!qtm || (out_bytes < 0)) return MSPACK_ERR_ARGS; | ||
267 | if (qtm->error) return qtm->error; | ||
268 | |||
269 | /* flush out any stored-up bytes before we begin */ | ||
270 | i = qtm->o_end - qtm->o_ptr; | ||
271 | if ((off_t) i > out_bytes) i = (int) out_bytes; | ||
272 | if (i) { | ||
273 | if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { | ||
274 | return qtm->error = MSPACK_ERR_WRITE; | ||
275 | } | ||
276 | qtm->o_ptr += i; | ||
277 | out_bytes -= i; | ||
278 | } | ||
279 | if (out_bytes == 0) return MSPACK_ERR_OK; | ||
280 | |||
281 | /* restore local state */ | ||
282 | RESTORE_BITS; | ||
283 | window = qtm->window; | ||
284 | window_posn = qtm->window_posn; | ||
285 | frame_todo = qtm->frame_todo; | ||
286 | H = qtm->H; | ||
287 | L = qtm->L; | ||
288 | C = qtm->C; | ||
289 | |||
290 | /* while we do not have enough decoded bytes in reserve: */ | ||
291 | while ((qtm->o_end - qtm->o_ptr) < out_bytes) { | ||
292 | /* read header if necessary. Initialises H, L and C */ | ||
293 | if (!qtm->header_read) { | ||
294 | H = 0xFFFF; L = 0; READ_BITS(C, 16); | ||
295 | qtm->header_read = 1; | ||
296 | } | ||
297 | |||
298 | /* decode more, up to the number of bytes needed, the frame boundary, | ||
299 | * or the window boundary, whichever comes first */ | ||
300 | frame_end = window_posn + (out_bytes - (qtm->o_end - qtm->o_ptr)); | ||
301 | if ((window_posn + frame_todo) < frame_end) { | ||
302 | frame_end = window_posn + frame_todo; | ||
303 | } | ||
304 | if (frame_end > qtm->window_size) { | ||
305 | frame_end = qtm->window_size; | ||
306 | } | ||
307 | |||
308 | while (window_posn < frame_end) { | ||
309 | GET_SYMBOL(qtm->model7, selector); | ||
310 | if (selector < 4) { | ||
311 | /* literal byte */ | ||
312 | struct qtmd_model *mdl = (selector == 0) ? &qtm->model0 : | ||
313 | ((selector == 1) ? &qtm->model1 : | ||
314 | ((selector == 2) ? &qtm->model2 : | ||
315 | &qtm->model3)); | ||
316 | GET_SYMBOL((*mdl), sym); | ||
317 | window[window_posn++] = sym; | ||
318 | frame_todo--; | ||
319 | } | ||
320 | else { | ||
321 | /* match repeated string */ | ||
322 | switch (selector) { | ||
323 | case 4: /* selector 4 = fixed length match (3 bytes) */ | ||
324 | GET_SYMBOL(qtm->model4, sym); | ||
325 | READ_MANY_BITS(extra, extra_bits[sym]); | ||
326 | match_offset = position_base[sym] + extra + 1; | ||
327 | match_length = 3; | ||
328 | break; | ||
329 | |||
330 | case 5: /* selector 5 = fixed length match (4 bytes) */ | ||
331 | GET_SYMBOL(qtm->model5, sym); | ||
332 | READ_MANY_BITS(extra, extra_bits[sym]); | ||
333 | match_offset = position_base[sym] + extra + 1; | ||
334 | match_length = 4; | ||
335 | break; | ||
336 | |||
337 | case 6: /* selector 6 = variable length match */ | ||
338 | GET_SYMBOL(qtm->model6len, sym); | ||
339 | READ_MANY_BITS(extra, length_extra[sym]); | ||
340 | match_length = length_base[sym] + extra + 5; | ||
341 | |||
342 | GET_SYMBOL(qtm->model6, sym); | ||
343 | READ_MANY_BITS(extra, extra_bits[sym]); | ||
344 | match_offset = position_base[sym] + extra + 1; | ||
345 | break; | ||
346 | |||
347 | default: | ||
348 | /* should be impossible, model7 can only return 0-6 */ | ||
349 | D(("got %d from selector", selector)) | ||
350 | return qtm->error = MSPACK_ERR_DECRUNCH; | ||
351 | } | ||
352 | |||
353 | rundest = &window[window_posn]; | ||
354 | frame_todo -= match_length; | ||
355 | |||
356 | /* does match destination wrap the window? This situation is possible | ||
357 | * where the window size is less than the 32k frame size, but matches | ||
358 | * must not go beyond a frame boundary */ | ||
359 | if ((window_posn + match_length) > qtm->window_size) { | ||
360 | /* copy first part of match, before window end */ | ||
361 | i = qtm->window_size - window_posn; | ||
362 | j = window_posn - match_offset; | ||
363 | while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; | ||
364 | |||
365 | /* flush currently stored data */ | ||
366 | i = (&window[qtm->window_size] - qtm->o_ptr); | ||
367 | |||
368 | /* this should not happen, but if it does then this code | ||
369 | * can't handle the situation (can't flush up to the end of | ||
370 | * the window, but can't break out either because we haven't | ||
371 | * finished writing the match). bail out in this case */ | ||
372 | if (i > out_bytes) { | ||
373 | D(("during window-wrap match; %d bytes to flush but only need %d", | ||
374 | i, (int) out_bytes)) | ||
375 | return qtm->error = MSPACK_ERR_DECRUNCH; | ||
376 | } | ||
377 | if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { | ||
378 | return qtm->error = MSPACK_ERR_WRITE; | ||
379 | } | ||
380 | out_bytes -= i; | ||
381 | qtm->o_ptr = &window[0]; | ||
382 | qtm->o_end = &window[0]; | ||
383 | |||
384 | /* copy second part of match, after window wrap */ | ||
385 | rundest = &window[0]; | ||
386 | i = match_length - (qtm->window_size - window_posn); | ||
387 | while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; | ||
388 | window_posn = window_posn + match_length - qtm->window_size; | ||
389 | |||
390 | break; /* because "window_posn < frame_end" has now failed */ | ||
391 | } | ||
392 | else { | ||
393 | /* normal match - output won't wrap window or frame end */ | ||
394 | i = match_length; | ||
395 | |||
396 | /* does match _offset_ wrap the window? */ | ||
397 | if (match_offset > window_posn) { | ||
398 | /* j = length from match offset to end of window */ | ||
399 | j = match_offset - window_posn; | ||
400 | if (j > (int) qtm->window_size) { | ||
401 | D(("match offset beyond window boundaries")) | ||
402 | return qtm->error = MSPACK_ERR_DECRUNCH; | ||
403 | } | ||
404 | runsrc = &window[qtm->window_size - j]; | ||
405 | if (j < i) { | ||
406 | /* if match goes over the window edge, do two copy runs */ | ||
407 | i -= j; while (j-- > 0) *rundest++ = *runsrc++; | ||
408 | runsrc = window; | ||
409 | } | ||
410 | while (i-- > 0) *rundest++ = *runsrc++; | ||
411 | } | ||
412 | else { | ||
413 | runsrc = rundest - match_offset; | ||
414 | while (i-- > 0) *rundest++ = *runsrc++; | ||
415 | } | ||
416 | window_posn += match_length; | ||
417 | } | ||
418 | } /* if (window_posn+match_length > frame_end) */ | ||
419 | } /* while (window_posn < frame_end) */ | ||
420 | |||
421 | qtm->o_end = &window[window_posn]; | ||
422 | |||
423 | /* if we subtracted too much from frame_todo, it will | ||
424 | * wrap around past zero and go above its max value */ | ||
425 | if (frame_todo > QTM_FRAME_SIZE) { | ||
426 | D(("overshot frame alignment")) | ||
427 | return qtm->error = MSPACK_ERR_DECRUNCH; | ||
428 | } | ||
429 | |||
430 | /* another frame completed? */ | ||
431 | if (frame_todo == 0) { | ||
432 | /* re-align input */ | ||
433 | if (bits_left & 7) REMOVE_BITS(bits_left & 7); | ||
434 | |||
435 | /* special Quantum hack -- cabd.c injects a trailer byte to allow the | ||
436 | * decompressor to realign itself. CAB Quantum blocks, unlike LZX | ||
437 | * blocks, can have anything from 0 to 4 trailing null bytes. */ | ||
438 | do { READ_BITS(i, 8); } while (i != 0xFF); | ||
439 | |||
440 | qtm->header_read = 0; | ||
441 | |||
442 | frame_todo = QTM_FRAME_SIZE; | ||
443 | } | ||
444 | |||
445 | /* window wrap? */ | ||
446 | if (window_posn == qtm->window_size) { | ||
447 | /* flush all currently stored data */ | ||
448 | i = (qtm->o_end - qtm->o_ptr); | ||
449 | /* break out if we have more than enough to finish this request */ | ||
450 | if (i >= out_bytes) break; | ||
451 | if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { | ||
452 | return qtm->error = MSPACK_ERR_WRITE; | ||
453 | } | ||
454 | out_bytes -= i; | ||
455 | qtm->o_ptr = &window[0]; | ||
456 | qtm->o_end = &window[0]; | ||
457 | window_posn = 0; | ||
458 | } | ||
459 | |||
460 | } /* while (more bytes needed) */ | ||
461 | |||
462 | if (out_bytes) { | ||
463 | i = (int) out_bytes; | ||
464 | if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { | ||
465 | return qtm->error = MSPACK_ERR_WRITE; | ||
466 | } | ||
467 | qtm->o_ptr += i; | ||
468 | } | ||
469 | |||
470 | /* store local state */ | ||
471 | |||
472 | STORE_BITS; | ||
473 | qtm->window_posn = window_posn; | ||
474 | qtm->frame_todo = frame_todo; | ||
475 | qtm->H = H; | ||
476 | qtm->L = L; | ||
477 | qtm->C = C; | ||
478 | |||
479 | return MSPACK_ERR_OK; | ||
480 | } | ||
481 | |||
482 | void qtmd_free(struct qtmd_stream *qtm) { | ||
483 | struct mspack_system *sys; | ||
484 | if (qtm) { | ||
485 | sys = qtm->sys; | ||
486 | sys->free(qtm->window); | ||
487 | sys->free(qtm->inbuf); | ||
488 | sys->free(qtm); | ||
489 | } | ||
490 | } | ||
diff --git a/utils/rbutilqt/mspack/readbits.h b/utils/rbutilqt/mspack/readbits.h new file mode 100644 index 0000000000..9b237a3693 --- /dev/null +++ b/utils/rbutilqt/mspack/readbits.h | |||
@@ -0,0 +1,207 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2010 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifndef MSPACK_READBITS_H | ||
11 | #define MSPACK_READBITS_H 1 | ||
12 | |||
13 | /* this header defines macros that read data streams by | ||
14 | * the individual bits | ||
15 | * | ||
16 | * INIT_BITS initialises bitstream state in state structure | ||
17 | * STORE_BITS stores bitstream state in state structure | ||
18 | * RESTORE_BITS restores bitstream state from state structure | ||
19 | * ENSURE_BITS(n) ensure there are at least N bits in the bit buffer | ||
20 | * READ_BITS(var,n) takes N bits from the buffer and puts them in var | ||
21 | * PEEK_BITS(n) extracts without removing N bits from the bit buffer | ||
22 | * REMOVE_BITS(n) removes N bits from the bit buffer | ||
23 | * | ||
24 | * READ_BITS simply calls ENSURE_BITS, PEEK_BITS and REMOVE_BITS, | ||
25 | * which means it's limited to reading the number of bits you can | ||
26 | * ensure at any one time. It also fails if asked to read zero bits. | ||
27 | * If you need to read zero bits, or more bits than can be ensured in | ||
28 | * one go, use READ_MANY_BITS instead. | ||
29 | * | ||
30 | * These macros have variable names baked into them, so to use them | ||
31 | * you have to define some macros: | ||
32 | * - BITS_TYPE: the type name of your state structure | ||
33 | * - BITS_VAR: the variable that points to your state structure | ||
34 | * - define BITS_ORDER_MSB if bits are read from the MSB, or | ||
35 | * define BITS_ORDER_LSB if bits are read from the LSB | ||
36 | * - READ_BYTES: some code that reads more data into the bit buffer, | ||
37 | * it should use READ_IF_NEEDED (calls read_input if the byte buffer | ||
38 | * is empty), then INJECT_BITS(data,n) to put data from the byte | ||
39 | * buffer into the bit buffer. | ||
40 | * | ||
41 | * You also need to define some variables and structure members: | ||
42 | * - unsigned char *i_ptr; // current position in the byte buffer | ||
43 | * - unsigned char *i_end; // end of the byte buffer | ||
44 | * - unsigned int bit_buffer; // the bit buffer itself | ||
45 | * - unsigned int bits_left; // number of bits remaining | ||
46 | * | ||
47 | * If you use read_input() and READ_IF_NEEDED, they also expect these | ||
48 | * structure members: | ||
49 | * - struct mspack_system *sys; // to access sys->read() | ||
50 | * - unsigned int error; // to record/return read errors | ||
51 | * - unsigned char input_end; // to mark reaching the EOF | ||
52 | * - unsigned char *inbuf; // the input byte buffer | ||
53 | * - unsigned int inbuf_size; // the size of the input byte buffer | ||
54 | * | ||
55 | * Your READ_BYTES implementation should read data from *i_ptr and | ||
56 | * put them in the bit buffer. READ_IF_NEEDED will call read_input() | ||
57 | * if i_ptr reaches i_end, and will fill up inbuf and set i_ptr to | ||
58 | * the start of inbuf and i_end to the end of inbuf. | ||
59 | * | ||
60 | * If you're reading in MSB order, the routines work by using the area | ||
61 | * beyond the MSB and the LSB of the bit buffer as a free source of | ||
62 | * zeroes when shifting. This avoids having to mask any bits. So we | ||
63 | * have to know the bit width of the bit buffer variable. We use | ||
64 | * <limits.h> and CHAR_BIT to find the size of the bit buffer in bits. | ||
65 | * | ||
66 | * If you are reading in LSB order, bits need to be masked. Normally | ||
67 | * this is done by computing the mask: N bits are masked by the value | ||
68 | * (1<<N)-1). However, you can define BITS_LSB_TABLE to use a lookup | ||
69 | * table instead of computing this. This adds two new macros, | ||
70 | * PEEK_BITS_T and READ_BITS_T which work the same way as PEEK_BITS | ||
71 | * and READ_BITS, except they use this lookup table. This is useful if | ||
72 | * you need to look up a number of bits that are only known at | ||
73 | * runtime, so the bit mask can't be turned into a constant by the | ||
74 | * compiler. | ||
75 | |||
76 | * The bit buffer datatype should be at least 32 bits wide: it must be | ||
77 | * possible to ENSURE_BITS(17), so it must be possible to add 16 new bits | ||
78 | * to the bit buffer when the bit buffer already has 1 to 15 bits left. | ||
79 | */ | ||
80 | |||
81 | #ifndef BITS_VAR | ||
82 | # error "define BITS_VAR as the state structure poiner variable name" | ||
83 | #endif | ||
84 | #ifndef BITS_TYPE | ||
85 | # error "define BITS_TYPE as the state structure type" | ||
86 | #endif | ||
87 | #if defined(BITS_ORDER_MSB) && defined(BITS_ORDER_LSB) | ||
88 | # error "you must define either BITS_ORDER_MSB or BITS_ORDER_LSB" | ||
89 | #else | ||
90 | # if !(defined(BITS_ORDER_MSB) || defined(BITS_ORDER_LSB)) | ||
91 | # error "you must define BITS_ORDER_MSB or BITS_ORDER_LSB" | ||
92 | # endif | ||
93 | #endif | ||
94 | |||
95 | #if HAVE_LIMITS_H | ||
96 | # include <limits.h> | ||
97 | #endif | ||
98 | #ifndef CHAR_BIT | ||
99 | # define CHAR_BIT (8) | ||
100 | #endif | ||
101 | #define BITBUF_WIDTH (sizeof(bit_buffer) * CHAR_BIT) | ||
102 | |||
103 | #define INIT_BITS do { \ | ||
104 | BITS_VAR->i_ptr = &BITS_VAR->inbuf[0]; \ | ||
105 | BITS_VAR->i_end = &BITS_VAR->inbuf[0]; \ | ||
106 | BITS_VAR->bit_buffer = 0; \ | ||
107 | BITS_VAR->bits_left = 0; \ | ||
108 | BITS_VAR->input_end = 0; \ | ||
109 | } while (0) | ||
110 | |||
111 | #define STORE_BITS do { \ | ||
112 | BITS_VAR->i_ptr = i_ptr; \ | ||
113 | BITS_VAR->i_end = i_end; \ | ||
114 | BITS_VAR->bit_buffer = bit_buffer; \ | ||
115 | BITS_VAR->bits_left = bits_left; \ | ||
116 | } while (0) | ||
117 | |||
118 | #define RESTORE_BITS do { \ | ||
119 | i_ptr = BITS_VAR->i_ptr; \ | ||
120 | i_end = BITS_VAR->i_end; \ | ||
121 | bit_buffer = BITS_VAR->bit_buffer; \ | ||
122 | bits_left = BITS_VAR->bits_left; \ | ||
123 | } while (0) | ||
124 | |||
125 | #define ENSURE_BITS(nbits) do { \ | ||
126 | while (bits_left < (nbits)) READ_BYTES; \ | ||
127 | } while (0) | ||
128 | |||
129 | #define READ_BITS(val, nbits) do { \ | ||
130 | ENSURE_BITS(nbits); \ | ||
131 | (val) = PEEK_BITS(nbits); \ | ||
132 | REMOVE_BITS(nbits); \ | ||
133 | } while (0) | ||
134 | |||
135 | #define READ_MANY_BITS(val, bits) do { \ | ||
136 | unsigned char needed = (bits), bitrun; \ | ||
137 | (val) = 0; \ | ||
138 | while (needed > 0) { \ | ||
139 | if (bits_left <= (BITBUF_WIDTH - 16)) READ_BYTES; \ | ||
140 | bitrun = (bits_left < needed) ? bits_left : needed; \ | ||
141 | (val) = ((val) << bitrun) | PEEK_BITS(bitrun); \ | ||
142 | REMOVE_BITS(bitrun); \ | ||
143 | needed -= bitrun; \ | ||
144 | } \ | ||
145 | } while (0) | ||
146 | |||
147 | #ifdef BITS_ORDER_MSB | ||
148 | # define PEEK_BITS(nbits) (bit_buffer >> (BITBUF_WIDTH - (nbits))) | ||
149 | # define REMOVE_BITS(nbits) ((bit_buffer <<= (nbits)), (bits_left -= (nbits))) | ||
150 | # define INJECT_BITS(bitdata,nbits) ((bit_buffer |= \ | ||
151 | (bitdata) << (BITBUF_WIDTH - (nbits) - bits_left)), (bits_left += (nbits))) | ||
152 | #else /* BITS_ORDER_LSB */ | ||
153 | # define PEEK_BITS(nbits) (bit_buffer & ((1 << (nbits))-1)) | ||
154 | # define REMOVE_BITS(nbits) ((bit_buffer >>= (nbits)), (bits_left -= (nbits))) | ||
155 | # define INJECT_BITS(bitdata,nbits) ((bit_buffer |= \ | ||
156 | (bitdata) << bits_left), (bits_left += (nbits))) | ||
157 | #endif | ||
158 | |||
159 | #ifdef BITS_LSB_TABLE | ||
160 | /* lsb_bit_mask[n] = (1 << n) - 1 */ | ||
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/utils/rbutilqt/mspack/readhuff.h b/utils/rbutilqt/mspack/readhuff.h new file mode 100644 index 0000000000..4d94225789 --- /dev/null +++ b/utils/rbutilqt/mspack/readhuff.h | |||
@@ -0,0 +1,172 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2014 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifndef MSPACK_READHUFF_H | ||
11 | #define MSPACK_READHUFF_H 1 | ||
12 | |||
13 | /* This implements a fast Huffman tree decoding system. */ | ||
14 | |||
15 | #if !(defined(BITS_ORDER_MSB) || defined(BITS_ORDER_LSB)) | ||
16 | # error "readhuff.h is used in conjunction with readbits.h, include that first" | ||
17 | #endif | ||
18 | #if !(defined(TABLEBITS) && defined(MAXSYMBOLS)) | ||
19 | # error "define TABLEBITS(tbl) and MAXSYMBOLS(tbl) before using readhuff.h" | ||
20 | #endif | ||
21 | #if !(defined(HUFF_TABLE) && defined(HUFF_LEN)) | ||
22 | # error "define HUFF_TABLE(tbl) and HUFF_LEN(tbl) before using readhuff.h" | ||
23 | #endif | ||
24 | #ifndef HUFF_ERROR | ||
25 | # error "define HUFF_ERROR before using readhuff.h" | ||
26 | #endif | ||
27 | #ifndef HUFF_MAXBITS | ||
28 | # define HUFF_MAXBITS 16 | ||
29 | #endif | ||
30 | |||
31 | /* Decodes the next huffman symbol from the input bitstream into var. | ||
32 | * Do not use this macro on a table unless build_decode_table() succeeded. | ||
33 | */ | ||
34 | #define READ_HUFFSYM(tbl, var) do { \ | ||
35 | ENSURE_BITS(HUFF_MAXBITS); \ | ||
36 | sym = HUFF_TABLE(tbl, PEEK_BITS(TABLEBITS(tbl))); \ | ||
37 | if (sym >= MAXSYMBOLS(tbl)) HUFF_TRAVERSE(tbl); \ | ||
38 | (var) = sym; \ | ||
39 | i = HUFF_LEN(tbl, sym); \ | ||
40 | REMOVE_BITS(i); \ | ||
41 | } while (0) | ||
42 | |||
43 | #ifdef BITS_ORDER_LSB | ||
44 | # define HUFF_TRAVERSE(tbl) do { \ | ||
45 | i = TABLEBITS(tbl) - 1; \ | ||
46 | do { \ | ||
47 | if (i++ > HUFF_MAXBITS) HUFF_ERROR; \ | ||
48 | sym = HUFF_TABLE(tbl, \ | ||
49 | (sym << 1) | ((bit_buffer >> i) & 1)); \ | ||
50 | } while (sym >= MAXSYMBOLS(tbl)); \ | ||
51 | } while (0) | ||
52 | #else | ||
53 | #define HUFF_TRAVERSE(tbl) do { \ | ||
54 | i = 1 << (BITBUF_WIDTH - TABLEBITS(tbl)); \ | ||
55 | do { \ | ||
56 | if ((i >>= 1) == 0) HUFF_ERROR; \ | ||
57 | sym = HUFF_TABLE(tbl, \ | ||
58 | (sym << 1) | ((bit_buffer & i) ? 1 : 0)); \ | ||
59 | } while (sym >= MAXSYMBOLS(tbl)); \ | ||
60 | } while (0) | ||
61 | #endif | ||
62 | |||
63 | /* make_decode_table(nsyms, nbits, length[], table[]) | ||
64 | * | ||
65 | * This function was originally coded by David Tritscher. | ||
66 | * It builds a fast huffman decoding table from | ||
67 | * a canonical huffman code lengths table. | ||
68 | * | ||
69 | * nsyms = total number of symbols in this huffman tree. | ||
70 | * nbits = any symbols with a code length of nbits or less can be decoded | ||
71 | * in one lookup of the table. | ||
72 | * length = A table to get code lengths from [0 to nsyms-1] | ||
73 | * table = The table to fill up with decoded symbols and pointers. | ||
74 | * Should be ((1<<nbits) + (nsyms*2)) in length. | ||
75 | * | ||
76 | * Returns 0 for OK or 1 for error | ||
77 | */ | ||
78 | static int make_decode_table(unsigned int nsyms, unsigned int nbits, | ||
79 | unsigned char *length, unsigned short *table) | ||
80 | { | ||
81 | register unsigned short sym, next_symbol; | ||
82 | register unsigned int leaf, fill; | ||
83 | #ifdef BITS_ORDER_LSB | ||
84 | register unsigned int reverse; | ||
85 | #endif | ||
86 | register unsigned char bit_num; | ||
87 | unsigned int pos = 0; /* the current position in the decode table */ | ||
88 | unsigned int table_mask = 1 << nbits; | ||
89 | unsigned int bit_mask = table_mask >> 1; /* don't do 0 length codes */ | ||
90 | |||
91 | /* fill entries for codes short enough for a direct mapping */ | ||
92 | for (bit_num = 1; bit_num <= nbits; bit_num++) { | ||
93 | for (sym = 0; sym < nsyms; sym++) { | ||
94 | if (length[sym] != bit_num) continue; | ||
95 | #ifdef BITS_ORDER_MSB | ||
96 | leaf = pos; | ||
97 | #else | ||
98 | /* reverse the significant bits */ | ||
99 | fill = length[sym]; reverse = pos >> (nbits - fill); leaf = 0; | ||
100 | do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); | ||
101 | #endif | ||
102 | |||
103 | if((pos += bit_mask) > table_mask) return 1; /* table overrun */ | ||
104 | |||
105 | /* fill all possible lookups of this symbol with the symbol itself */ | ||
106 | #ifdef BITS_ORDER_MSB | ||
107 | for (fill = bit_mask; fill-- > 0;) table[leaf++] = sym; | ||
108 | #else | ||
109 | fill = bit_mask; next_symbol = 1 << bit_num; | ||
110 | do { table[leaf] = sym; leaf += next_symbol; } while (--fill); | ||
111 | #endif | ||
112 | } | ||
113 | bit_mask >>= 1; | ||
114 | } | ||
115 | |||
116 | /* exit with success if table is now complete */ | ||
117 | if (pos == table_mask) return 0; | ||
118 | |||
119 | /* mark all remaining table entries as unused */ | ||
120 | for (sym = pos; sym < table_mask; sym++) { | ||
121 | #ifdef BITS_ORDER_MSB | ||
122 | table[sym] = 0xFFFF; | ||
123 | #else | ||
124 | reverse = sym; leaf = 0; fill = nbits; | ||
125 | do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill); | ||
126 | table[leaf] = 0xFFFF; | ||
127 | #endif | ||
128 | } | ||
129 | |||
130 | /* next_symbol = base of allocation for long codes */ | ||
131 | next_symbol = ((table_mask >> 1) < nsyms) ? nsyms : (table_mask >> 1); | ||
132 | |||
133 | /* give ourselves room for codes to grow by up to 16 more bits. | ||
134 | * codes now start at bit nbits+16 and end at (nbits+16-codelength) */ | ||
135 | pos <<= 16; | ||
136 | table_mask <<= 16; | ||
137 | bit_mask = 1 << 15; | ||
138 | |||
139 | for (bit_num = nbits+1; bit_num <= HUFF_MAXBITS; bit_num++) { | ||
140 | for (sym = 0; sym < nsyms; sym++) { | ||
141 | if (length[sym] != bit_num) continue; | ||
142 | if (pos >= table_mask) return 1; /* table overflow */ | ||
143 | |||
144 | #ifdef BITS_ORDER_MSB | ||
145 | leaf = pos >> 16; | ||
146 | #else | ||
147 | /* leaf = the first nbits of the code, reversed */ | ||
148 | reverse = pos >> 16; leaf = 0; fill = nbits; | ||
149 | do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); | ||
150 | #endif | ||
151 | for (fill = 0; fill < (bit_num - nbits); fill++) { | ||
152 | /* if this path hasn't been taken yet, 'allocate' two entries */ | ||
153 | if (table[leaf] == 0xFFFF) { | ||
154 | table[(next_symbol << 1) ] = 0xFFFF; | ||
155 | table[(next_symbol << 1) + 1 ] = 0xFFFF; | ||
156 | table[leaf] = next_symbol++; | ||
157 | } | ||
158 | |||
159 | /* follow the path and select either left or right for next bit */ | ||
160 | leaf = table[leaf] << 1; | ||
161 | if ((pos >> (15-fill)) & 1) leaf++; | ||
162 | } | ||
163 | table[leaf] = sym; | ||
164 | pos += bit_mask; | ||
165 | } | ||
166 | bit_mask >>= 1; | ||
167 | } | ||
168 | |||
169 | /* full table? */ | ||
170 | return (pos == table_mask) ? 0 : 1; | ||
171 | } | ||
172 | #endif | ||
diff --git a/utils/rbutilqt/mspack/sha.h b/utils/rbutilqt/mspack/sha.h new file mode 100644 index 0000000000..360521519b --- /dev/null +++ b/utils/rbutilqt/mspack/sha.h | |||
@@ -0,0 +1,15 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifndef MSPACK_LZSS_H | ||
11 | #define MSPACK_LZSS_H 1 | ||
12 | |||
13 | /* SHA-1 message digest definitions */ | ||
14 | |||
15 | #endif | ||
diff --git a/utils/rbutilqt/mspack/system-mspack.c b/utils/rbutilqt/mspack/system-mspack.c new file mode 100644 index 0000000000..9d4886a8db --- /dev/null +++ b/utils/rbutilqt/mspack/system-mspack.c | |||
@@ -0,0 +1,240 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifdef HAVE_CONFIG_H | ||
11 | # include "config.h" | ||
12 | #endif | ||
13 | |||
14 | #include "system-mspack.h" | ||
15 | |||
16 | #ifndef LARGEFILE_SUPPORT | ||
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 | (void)self; | ||
119 | |||
120 | switch (mode) { | ||
121 | case MSPACK_SYS_OPEN_READ: fmode = "rb"; break; | ||
122 | case MSPACK_SYS_OPEN_WRITE: fmode = "wb"; break; | ||
123 | case MSPACK_SYS_OPEN_UPDATE: fmode = "r+b"; break; | ||
124 | case MSPACK_SYS_OPEN_APPEND: fmode = "ab"; break; | ||
125 | default: return NULL; | ||
126 | } | ||
127 | |||
128 | if ((fh = (struct mspack_file_p *) malloc(sizeof(struct mspack_file_p)))) { | ||
129 | fh->name = filename; | ||
130 | if ((fh->fh = fopen(filename, fmode))) return (struct mspack_file *) fh; | ||
131 | free(fh); | ||
132 | } | ||
133 | return NULL; | ||
134 | } | ||
135 | |||
136 | static void msp_close(struct mspack_file *file) { | ||
137 | struct mspack_file_p *self = (struct mspack_file_p *) file; | ||
138 | if (self) { | ||
139 | fclose(self->fh); | ||
140 | free(self); | ||
141 | } | ||
142 | } | ||
143 | |||
144 | static int msp_read(struct mspack_file *file, void *buffer, int bytes) { | ||
145 | struct mspack_file_p *self = (struct mspack_file_p *) file; | ||
146 | if (self && buffer && bytes >= 0) { | ||
147 | size_t count = fread(buffer, 1, (size_t) bytes, self->fh); | ||
148 | if (!ferror(self->fh)) return (int) count; | ||
149 | } | ||
150 | return -1; | ||
151 | } | ||
152 | |||
153 | static int msp_write(struct mspack_file *file, void *buffer, int bytes) { | ||
154 | struct mspack_file_p *self = (struct mspack_file_p *) file; | ||
155 | if (self && buffer && bytes >= 0) { | ||
156 | size_t count = fwrite(buffer, 1, (size_t) bytes, self->fh); | ||
157 | if (!ferror(self->fh)) return (int) count; | ||
158 | } | ||
159 | return -1; | ||
160 | } | ||
161 | |||
162 | static int msp_seek(struct mspack_file *file, off_t offset, int mode) { | ||
163 | struct mspack_file_p *self = (struct mspack_file_p *) file; | ||
164 | if (self) { | ||
165 | switch (mode) { | ||
166 | case MSPACK_SYS_SEEK_START: mode = SEEK_SET; break; | ||
167 | case MSPACK_SYS_SEEK_CUR: mode = SEEK_CUR; break; | ||
168 | case MSPACK_SYS_SEEK_END: mode = SEEK_END; break; | ||
169 | default: return -1; | ||
170 | } | ||
171 | #ifdef HAVE_FSEEKO | ||
172 | return fseeko(self->fh, offset, mode); | ||
173 | #else | ||
174 | return fseek(self->fh, offset, mode); | ||
175 | #endif | ||
176 | } | ||
177 | return -1; | ||
178 | } | ||
179 | |||
180 | static off_t msp_tell(struct mspack_file *file) { | ||
181 | struct mspack_file_p *self = (struct mspack_file_p *) file; | ||
182 | #ifdef HAVE_FSEEKO | ||
183 | return (self) ? (off_t) ftello(self->fh) : 0; | ||
184 | #else | ||
185 | return (self) ? (off_t) ftell(self->fh) : 0; | ||
186 | #endif | ||
187 | } | ||
188 | |||
189 | static void msp_msg(struct mspack_file *file, const char *format, ...) { | ||
190 | va_list ap; | ||
191 | if (file) fprintf(stderr, "%s: ", ((struct mspack_file_p *) file)->name); | ||
192 | va_start(ap, format); | ||
193 | vfprintf(stderr, format, ap); | ||
194 | va_end(ap); | ||
195 | fputc((int) '\n', stderr); | ||
196 | fflush(stderr); | ||
197 | } | ||
198 | |||
199 | static void *msp_alloc(struct mspack_system *self, size_t bytes) { | ||
200 | #ifdef DEBUG | ||
201 | /* make uninitialised data obvious */ | ||
202 | char *buf = malloc(bytes + 8); | ||
203 | (void)self; | ||
204 | if (buf) memset(buf, 0xDC, bytes); | ||
205 | *((size_t *)buf) = bytes; | ||
206 | return &buf[8]; | ||
207 | #else | ||
208 | (void)self; | ||
209 | return malloc(bytes); | ||
210 | #endif | ||
211 | } | ||
212 | |||
213 | static void msp_free(void *buffer) { | ||
214 | #ifdef DEBUG | ||
215 | char *buf = buffer; | ||
216 | size_t bytes; | ||
217 | if (buf) { | ||
218 | buf -= 8; | ||
219 | bytes = *((size_t *)buf); | ||
220 | /* make freed data obvious */ | ||
221 | memset(buf, 0xED, bytes); | ||
222 | free(buf); | ||
223 | } | ||
224 | #else | ||
225 | free(buffer); | ||
226 | #endif | ||
227 | } | ||
228 | |||
229 | static void msp_copy(void *src, void *dest, size_t bytes) { | ||
230 | memcpy(dest, src, bytes); | ||
231 | } | ||
232 | |||
233 | static struct mspack_system msp_system = { | ||
234 | &msp_open, &msp_close, &msp_read, &msp_write, &msp_seek, | ||
235 | &msp_tell, &msp_msg, &msp_alloc, &msp_free, &msp_copy, NULL | ||
236 | }; | ||
237 | |||
238 | struct mspack_system *mspack_default_system = &msp_system; | ||
239 | |||
240 | #endif | ||
diff --git a/utils/rbutilqt/mspack/system-mspack.h b/utils/rbutilqt/mspack/system-mspack.h new file mode 100644 index 0000000000..a0e6cf3ca8 --- /dev/null +++ b/utils/rbutilqt/mspack/system-mspack.h | |||
@@ -0,0 +1,129 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifndef MSPACK_SYSTEM_H | ||
11 | #define MSPACK_SYSTEM_H 1 | ||
12 | |||
13 | #ifdef __cplusplus | ||
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 1 | ||
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 | #ifdef _MSC_VER /* MSVC requires use of __inline instead of inline */ | ||
112 | #define INLINE __inline | ||
113 | #else | ||
114 | #define INLINE inline | ||
115 | #endif | ||
116 | static INLINE int mspack_memcmp(const void *s1, const void *s2, size_t n) { | ||
117 | unsigned char *c1 = (unsigned char *) s1; | ||
118 | unsigned char *c2 = (unsigned char *) s2; | ||
119 | if (n == 0) return 0; | ||
120 | while (--n && (*c1 == *c2)) c1++, c2++; | ||
121 | return *c1 - *c2; | ||
122 | } | ||
123 | #endif | ||
124 | |||
125 | #ifdef __cplusplus | ||
126 | } | ||
127 | #endif | ||
128 | |||
129 | #endif | ||
diff --git a/utils/rbutilqt/mspack/szdd.h b/utils/rbutilqt/mspack/szdd.h new file mode 100644 index 0000000000..b9936b42d1 --- /dev/null +++ b/utils/rbutilqt/mspack/szdd.h | |||
@@ -0,0 +1,39 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | #ifndef MSPACK_SZDD_H | ||
11 | #define MSPACK_SZDD_H 1 | ||
12 | |||
13 | #include "lzss.h" | ||
14 | |||
15 | /* input buffer size during decompression - not worth parameterising IMHO */ | ||
16 | #define SZDD_INPUT_SIZE (2048) | ||
17 | |||
18 | /* SZDD compression definitions */ | ||
19 | |||
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/utils/rbutilqt/mspack/szddc.c b/utils/rbutilqt/mspack/szddc.c new file mode 100644 index 0000000000..6ad6501217 --- /dev/null +++ b/utils/rbutilqt/mspack/szddc.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | /* SZDD compression implementation */ | ||
11 | |||
12 | #include "system-mspack.h" | ||
13 | #include "szdd.h" | ||
14 | |||
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/utils/rbutilqt/mspack/szddd.c b/utils/rbutilqt/mspack/szddd.c new file mode 100644 index 0000000000..1d6d05f844 --- /dev/null +++ b/utils/rbutilqt/mspack/szddd.c | |||
@@ -0,0 +1,247 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2010 Stuart Caie. | ||
3 | * | ||
4 | * SZDD is a format used in the MS-DOS commands COMPRESS.EXE and | ||
5 | * EXPAND.EXE. The compression method is attributed to Steven Zeck, | ||
6 | * however it's pretty much identical to LZSS. | ||
7 | * | ||
8 | * libmspack is free software; you can redistribute it and/or modify it under | ||
9 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
10 | * | ||
11 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
12 | */ | ||
13 | |||
14 | /* SZDD decompression implementation */ | ||
15 | |||
16 | #include "system-mspack.h" | ||
17 | #include "szdd.h" | ||
18 | |||
19 | /* prototypes */ | ||
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 | 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 ((memcmp(buf, szdd_signature_expand, 8) == 0)) { | ||
154 | /* common SZDD */ | ||
155 | hdr->format = MSSZDD_FMT_NORMAL; | ||
156 | |||
157 | /* read the rest of the header */ | ||
158 | if (sys->read(fh, buf, 6) != 6) return MSPACK_ERR_READ; | ||
159 | if (buf[0] != 0x41) return MSPACK_ERR_DATAFORMAT; | ||
160 | hdr->missing_char = buf[1]; | ||
161 | hdr->length = EndGetI32(&buf[2]); | ||
162 | } | ||
163 | else if ((memcmp(buf, szdd_signature_qbasic, 8) == 0)) { | ||
164 | /* special QBasic SZDD */ | ||
165 | hdr->format = MSSZDD_FMT_QBASIC; | ||
166 | if (sys->read(fh, buf, 4) != 4) return MSPACK_ERR_READ; | ||
167 | hdr->missing_char = '\0'; | ||
168 | hdr->length = EndGetI32(buf); | ||
169 | } | ||
170 | else { | ||
171 | return MSPACK_ERR_SIGNATURE; | ||
172 | } | ||
173 | return MSPACK_ERR_OK; | ||
174 | } | ||
175 | |||
176 | /*************************************** | ||
177 | * SZDDD_EXTRACT | ||
178 | *************************************** | ||
179 | * decompresses an SZDD file | ||
180 | */ | ||
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 | } | ||