From 739a7ae0e9acb27227f5473a003833ea5a9c97ef Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Mon, 11 Mar 2013 18:46:03 +0100 Subject: Add libmspack to rbutil Change-Id: I520c14131ec1e12013f106c13cba00aac058ad83 Reviewed-on: http://gerrit.rockbox.org/391 Reviewed-by: Dominik Riebeling --- rbutil/rbutilqt/mspack/COPYING.LIB | 504 ++++++++ rbutil/rbutilqt/mspack/README.ROCKBOX | 6 + rbutil/rbutilqt/mspack/cab.h | 127 ++ rbutil/rbutilqt/mspack/cabc.c | 24 + rbutil/rbutilqt/mspack/cabd.c | 1444 +++++++++++++++++++++ rbutil/rbutilqt/mspack/chm.h | 122 ++ rbutil/rbutilqt/mspack/chmc.c | 24 + rbutil/rbutilqt/mspack/chmd.c | 1346 ++++++++++++++++++++ rbutil/rbutilqt/mspack/des.h | 15 + rbutil/rbutilqt/mspack/hlp.h | 33 + rbutil/rbutilqt/mspack/hlpc.c | 24 + rbutil/rbutilqt/mspack/hlpd.c | 24 + rbutil/rbutilqt/mspack/kwaj.h | 118 ++ rbutil/rbutilqt/mspack/kwajc.c | 24 + rbutil/rbutilqt/mspack/kwajd.c | 555 +++++++++ rbutil/rbutilqt/mspack/lit.h | 35 + rbutil/rbutilqt/mspack/litc.c | 24 + rbutil/rbutilqt/mspack/litd.c | 24 + rbutil/rbutilqt/mspack/lzss.h | 66 + rbutil/rbutilqt/mspack/lzssd.c | 93 ++ rbutil/rbutilqt/mspack/lzx.h | 194 +++ rbutil/rbutilqt/mspack/lzxc.c | 18 + rbutil/rbutilqt/mspack/lzxd.c | 738 +++++++++++ rbutil/rbutilqt/mspack/mspack.h | 2203 +++++++++++++++++++++++++++++++++ rbutil/rbutilqt/mspack/mszip.h | 121 ++ rbutil/rbutilqt/mspack/mszipc.c | 18 + rbutil/rbutilqt/mspack/mszipd.c | 475 +++++++ rbutil/rbutilqt/mspack/qtm.h | 128 ++ rbutil/rbutilqt/mspack/qtmd.c | 489 ++++++++ rbutil/rbutilqt/mspack/readbits.h | 207 ++++ rbutil/rbutilqt/mspack/readhuff.h | 173 +++ rbutil/rbutilqt/mspack/sha.h | 15 + rbutil/rbutilqt/mspack/system.c | 237 ++++ rbutil/rbutilqt/mspack/system.h | 124 ++ rbutil/rbutilqt/mspack/szdd.h | 39 + rbutil/rbutilqt/mspack/szddc.c | 24 + rbutil/rbutilqt/mspack/szddd.c | 247 ++++ 37 files changed, 10082 insertions(+) create mode 100644 rbutil/rbutilqt/mspack/COPYING.LIB create mode 100644 rbutil/rbutilqt/mspack/README.ROCKBOX create mode 100644 rbutil/rbutilqt/mspack/cab.h create mode 100644 rbutil/rbutilqt/mspack/cabc.c create mode 100644 rbutil/rbutilqt/mspack/cabd.c create mode 100644 rbutil/rbutilqt/mspack/chm.h create mode 100644 rbutil/rbutilqt/mspack/chmc.c create mode 100644 rbutil/rbutilqt/mspack/chmd.c create mode 100644 rbutil/rbutilqt/mspack/des.h create mode 100644 rbutil/rbutilqt/mspack/hlp.h create mode 100644 rbutil/rbutilqt/mspack/hlpc.c create mode 100644 rbutil/rbutilqt/mspack/hlpd.c create mode 100644 rbutil/rbutilqt/mspack/kwaj.h create mode 100644 rbutil/rbutilqt/mspack/kwajc.c create mode 100644 rbutil/rbutilqt/mspack/kwajd.c create mode 100644 rbutil/rbutilqt/mspack/lit.h create mode 100644 rbutil/rbutilqt/mspack/litc.c create mode 100644 rbutil/rbutilqt/mspack/litd.c create mode 100644 rbutil/rbutilqt/mspack/lzss.h create mode 100644 rbutil/rbutilqt/mspack/lzssd.c create mode 100644 rbutil/rbutilqt/mspack/lzx.h create mode 100644 rbutil/rbutilqt/mspack/lzxc.c create mode 100644 rbutil/rbutilqt/mspack/lzxd.c create mode 100644 rbutil/rbutilqt/mspack/mspack.h create mode 100644 rbutil/rbutilqt/mspack/mszip.h create mode 100644 rbutil/rbutilqt/mspack/mszipc.c create mode 100644 rbutil/rbutilqt/mspack/mszipd.c create mode 100644 rbutil/rbutilqt/mspack/qtm.h create mode 100644 rbutil/rbutilqt/mspack/qtmd.c create mode 100644 rbutil/rbutilqt/mspack/readbits.h create mode 100644 rbutil/rbutilqt/mspack/readhuff.h create mode 100644 rbutil/rbutilqt/mspack/sha.h create mode 100644 rbutil/rbutilqt/mspack/system.c create mode 100644 rbutil/rbutilqt/mspack/system.h create mode 100644 rbutil/rbutilqt/mspack/szdd.h create mode 100644 rbutil/rbutilqt/mspack/szddc.c create mode 100644 rbutil/rbutilqt/mspack/szddd.c (limited to 'rbutil/rbutilqt/mspack') diff --git a/rbutil/rbutilqt/mspack/COPYING.LIB b/rbutil/rbutilqt/mspack/COPYING.LIB new file mode 100644 index 0000000000..b1e3f5a263 --- /dev/null +++ b/rbutil/rbutilqt/mspack/COPYING.LIB @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/rbutil/rbutilqt/mspack/README.ROCKBOX b/rbutil/rbutilqt/mspack/README.ROCKBOX new file mode 100644 index 0000000000..db5ba7f482 --- /dev/null +++ b/rbutil/rbutilqt/mspack/README.ROCKBOX @@ -0,0 +1,6 @@ +This folder contains the mspack project for MS files compression/decompression. +These files are distributed under the LGPL. +The source files have been last synced with libmspack-0.3alpha +http://sourceforge.net/projects/libmspack/on January 28, 2013 + + diff --git a/rbutil/rbutilqt/mspack/cab.h b/rbutil/rbutilqt/mspack/cab.h new file mode 100644 index 0000000000..9f449d1f29 --- /dev/null +++ b/rbutil/rbutilqt/mspack/cab.h @@ -0,0 +1,127 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_CAB_H +#define MSPACK_CAB_H 1 + +#include +#include +#include + +/* generic CAB definitions */ + +/* structure offsets */ +#define cfhead_Signature (0x00) +#define cfhead_CabinetSize (0x08) +#define cfhead_FileOffset (0x10) +#define cfhead_MinorVersion (0x18) +#define cfhead_MajorVersion (0x19) +#define cfhead_NumFolders (0x1A) +#define cfhead_NumFiles (0x1C) +#define cfhead_Flags (0x1E) +#define cfhead_SetID (0x20) +#define cfhead_CabinetIndex (0x22) +#define cfhead_SIZEOF (0x24) +#define cfheadext_HeaderReserved (0x00) +#define cfheadext_FolderReserved (0x02) +#define cfheadext_DataReserved (0x03) +#define cfheadext_SIZEOF (0x04) +#define cffold_DataOffset (0x00) +#define cffold_NumBlocks (0x04) +#define cffold_CompType (0x06) +#define cffold_SIZEOF (0x08) +#define cffile_UncompressedSize (0x00) +#define cffile_FolderOffset (0x04) +#define cffile_FolderIndex (0x08) +#define cffile_Date (0x0A) +#define cffile_Time (0x0C) +#define cffile_Attribs (0x0E) +#define cffile_SIZEOF (0x10) +#define cfdata_CheckSum (0x00) +#define cfdata_CompressedSize (0x04) +#define cfdata_UncompressedSize (0x06) +#define cfdata_SIZEOF (0x08) + +/* flags */ +#define cffoldCOMPTYPE_MASK (0x000f) +#define cffoldCOMPTYPE_NONE (0x0000) +#define cffoldCOMPTYPE_MSZIP (0x0001) +#define cffoldCOMPTYPE_QUANTUM (0x0002) +#define cffoldCOMPTYPE_LZX (0x0003) +#define cfheadPREV_CABINET (0x0001) +#define cfheadNEXT_CABINET (0x0002) +#define cfheadRESERVE_PRESENT (0x0004) +#define cffileCONTINUED_FROM_PREV (0xFFFD) +#define cffileCONTINUED_TO_NEXT (0xFFFE) +#define cffileCONTINUED_PREV_AND_NEXT (0xFFFF) + +/* CAB data blocks are <= 32768 bytes in uncompressed form. Uncompressed + * blocks have zero growth. MSZIP guarantees that it won't grow above + * uncompressed size by more than 12 bytes. LZX guarantees it won't grow + * more than 6144 bytes. Quantum has no documentation, but the largest + * block seen in the wild is 337 bytes above uncompressed size. + */ +#define CAB_BLOCKMAX (32768) +#define CAB_INPUTMAX (CAB_BLOCKMAX+6144) + +/* CAB compression definitions */ + +struct mscab_compressor_p { + struct mscab_compressor base; + struct mspack_system *system; + /* todo */ +}; + +/* CAB decompression definitions */ + +struct mscabd_decompress_state { + struct mscabd_folder_p *folder; /* current folder we're extracting from */ + struct mscabd_folder_data *data; /* current folder split we're in */ + unsigned int offset; /* uncompressed offset within folder */ + unsigned int block; /* which block are we decompressing? */ + struct mspack_system sys; /* special I/O code for decompressor */ + int comp_type; /* type of compression used by folder */ + int (*decompress)(void *, off_t); /* decompressor code */ + void *state; /* decompressor state */ + struct mscabd_cabinet_p *incab; /* cabinet where input data comes from */ + struct mspack_file *infh; /* input file handle */ + struct mspack_file *outfh; /* output file handle */ + unsigned char *i_ptr, *i_end; /* input data consumed, end */ + unsigned char input[CAB_INPUTMAX]; /* one input block of data */ +}; + +struct mscab_decompressor_p { + struct mscab_decompressor base; + struct mscabd_decompress_state *d; + struct mspack_system *system; + int param[3]; /* !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!! */ + int error, read_error; +}; + +struct mscabd_cabinet_p { + struct mscabd_cabinet base; + off_t blocks_off; /* offset to data blocks */ + int block_resv; /* reserved space in data blocks */ +}; + +/* there is one of these for every cabinet a folder spans */ +struct mscabd_folder_data { + struct mscabd_folder_data *next; + struct mscabd_cabinet_p *cab; /* cabinet file of this folder span */ + off_t offset; /* cabinet offset of first datablock */ +}; + +struct mscabd_folder_p { + struct mscabd_folder base; + struct mscabd_folder_data data; /* where are the data blocks? */ + struct mscabd_file *merge_prev; /* first file needing backwards merge */ + struct mscabd_file *merge_next; /* first file needing forwards merge */ +}; + +#endif diff --git a/rbutil/rbutilqt/mspack/cabc.c b/rbutil/rbutilqt/mspack/cabc.c new file mode 100644 index 0000000000..242e0347c3 --- /dev/null +++ b/rbutil/rbutilqt/mspack/cabc.c @@ -0,0 +1,24 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* CAB compression implementation */ + +#include +#include + +struct mscab_compressor * + mspack_create_cab_compressor(struct mspack_system *sys) +{ + /* todo */ + return NULL; +} + +void mspack_destroy_cab_compressor(struct mscab_compressor *self) { + /* todo */ +} diff --git a/rbutil/rbutilqt/mspack/cabd.c b/rbutil/rbutilqt/mspack/cabd.c new file mode 100644 index 0000000000..24ff2031f3 --- /dev/null +++ b/rbutil/rbutilqt/mspack/cabd.c @@ -0,0 +1,1444 @@ +/* This file is part of libmspack. + * (C) 2003-2011 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* Cabinet (.CAB) files are a form of file archive. Each cabinet contains + * "folders", which are compressed spans of data. Each cabinet has + * "files", whose metadata is in the cabinet header, but whose actual data + * is stored compressed in one of the "folders". Cabinets can span more + * than one physical file on disk, in which case they are a "cabinet set", + * and usually the last folder of each cabinet extends into the next + * cabinet. + * + * For a complete description of the format, see the MSDN site: + * http://msdn.microsoft.com/en-us/library/bb267310.aspx + */ + +/* CAB decompression implementation */ + +#include +#include +#include + +/* Notes on compliance with cabinet specification: + * + * One of the main changes between cabextract 0.6 and libmspack's cab + * decompressor is the move from block-oriented decompression to + * stream-oriented decompression. + * + * cabextract would read one data block from disk, decompress it with the + * appropriate method, then write the decompressed data. The CAB + * specification is specifically designed to work like this, as it ensures + * compression matches do not span the maximum decompressed block size + * limit of 32kb. + * + * However, the compression algorithms used are stream oriented, with + * specific hacks added to them to enforce the "individual 32kb blocks" + * rule in CABs. In other file formats, they do not have this limitation. + * + * In order to make more generalised decompressors, libmspack's CAB + * decompressor has moved from being block-oriented to more stream + * oriented. This also makes decompression slightly faster. + * + * However, this leads to incompliance with the CAB specification. The + * CAB controller can no longer ensure each block of input given to the + * decompressors is matched with their output. The "decompressed size" of + * each individual block is thrown away. + * + * Each CAB block is supposed to be seen as individually compressed. This + * means each consecutive data block can have completely different + * "uncompressed" sizes, ranging from 1 to 32768 bytes. However, in + * reality, all data blocks in a folder decompress to exactly 32768 bytes, + * excepting the final block. + * + * Given this situation, the decompression algorithms are designed to + * realign their input bitstreams on 32768 output-byte boundaries, and + * various other special cases have been made. libmspack will not + * correctly decompress LZX or Quantum compressed folders where the blocks + * do not follow this "32768 bytes until last block" pattern. It could be + * implemented if needed, but hopefully this is not necessary -- it has + * not been seen in over 3Gb of CAB archives. + */ + +/* prototypes */ +static struct mscabd_cabinet * cabd_open( + struct mscab_decompressor *base, const char *filename); +static void cabd_close( + struct mscab_decompressor *base, struct mscabd_cabinet *origcab); +static int cabd_read_headers( + struct mspack_system *sys, struct mspack_file *fh, + struct mscabd_cabinet_p *cab, off_t offset, int quiet); +static char *cabd_read_string( + struct mspack_system *sys, struct mspack_file *fh, + struct mscabd_cabinet_p *cab, int *error); + +static struct mscabd_cabinet *cabd_search( + struct mscab_decompressor *base, const char *filename); +static int cabd_find( + struct mscab_decompressor_p *self, unsigned char *buf, + struct mspack_file *fh, const char *filename, off_t flen, + off_t *firstlen, struct mscabd_cabinet_p **firstcab); + +static int cabd_prepend( + struct mscab_decompressor *base, struct mscabd_cabinet *cab, + struct mscabd_cabinet *prevcab); +static int cabd_append( + struct mscab_decompressor *base, struct mscabd_cabinet *cab, + struct mscabd_cabinet *nextcab); +static int cabd_merge( + struct mscab_decompressor *base, struct mscabd_cabinet *lcab, + struct mscabd_cabinet *rcab); +static int cabd_can_merge_folders( + struct mspack_system *sys, struct mscabd_folder_p *lfol, + struct mscabd_folder_p *rfol); + +static int cabd_extract( + struct mscab_decompressor *base, struct mscabd_file *file, + const char *filename); +static int cabd_init_decomp( + struct mscab_decompressor_p *self, unsigned int ct); +static void cabd_free_decomp( + struct mscab_decompressor_p *self); +static int cabd_sys_read( + struct mspack_file *file, void *buffer, int bytes); +static int cabd_sys_write( + struct mspack_file *file, void *buffer, int bytes); +static int cabd_sys_read_block( + struct mspack_system *sys, struct mscabd_decompress_state *d, int *out, + int ignore_cksum); +static unsigned int cabd_checksum( + unsigned char *data, unsigned int bytes, unsigned int cksum); +static struct noned_state *noned_init( + struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out, + int bufsize); + +static int noned_decompress( + struct noned_state *s, off_t bytes); +static void noned_free( + struct noned_state *state); + +static int cabd_param( + struct mscab_decompressor *base, int param, int value); + +static int cabd_error( + struct mscab_decompressor *base); + + +/*************************************** + * MSPACK_CREATE_CAB_DECOMPRESSOR + *************************************** + * constructor + */ +struct mscab_decompressor * + mspack_create_cab_decompressor(struct mspack_system *sys) +{ + struct mscab_decompressor_p *self = NULL; + + if (!sys) sys = mspack_default_system; + if (!mspack_valid_system(sys)) return NULL; + + if ((self = (struct mscab_decompressor_p *) sys->alloc(sys, sizeof(struct mscab_decompressor_p)))) { + self->base.open = &cabd_open; + self->base.close = &cabd_close; + self->base.search = &cabd_search; + self->base.extract = &cabd_extract; + self->base.prepend = &cabd_prepend; + self->base.append = &cabd_append; + self->base.set_param = &cabd_param; + self->base.last_error = &cabd_error; + self->system = sys; + self->d = NULL; + self->error = MSPACK_ERR_OK; + + self->param[MSCABD_PARAM_SEARCHBUF] = 32768; + self->param[MSCABD_PARAM_FIXMSZIP] = 0; + self->param[MSCABD_PARAM_DECOMPBUF] = 4096; + } + return (struct mscab_decompressor *) self; +} + +/*************************************** + * MSPACK_DESTROY_CAB_DECOMPRESSOR + *************************************** + * destructor + */ +void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) { + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + if (self) { + struct mspack_system *sys = self->system; + cabd_free_decomp(self); + if (self->d) { + if (self->d->infh) sys->close(self->d->infh); + sys->free(self->d); + } + sys->free(self); + } +} + + +/*************************************** + * CABD_OPEN + *************************************** + * opens a file and tries to read it as a cabinet file + */ +static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, + const char *filename) +{ + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + struct mscabd_cabinet_p *cab = NULL; + struct mspack_system *sys; + struct mspack_file *fh; + int error; + + if (!base) return NULL; + sys = self->system; + + if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { + if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { + cab->base.filename = filename; + error = cabd_read_headers(sys, fh, cab, (off_t) 0, 0); + if (error) { + cabd_close(base, (struct mscabd_cabinet *) cab); + cab = NULL; + } + self->error = error; + } + else { + self->error = MSPACK_ERR_NOMEMORY; + } + sys->close(fh); + } + else { + self->error = MSPACK_ERR_OPEN; + } + return (struct mscabd_cabinet *) cab; +} + +/*************************************** + * CABD_CLOSE + *************************************** + * frees all memory associated with a given mscabd_cabinet. + */ +static void cabd_close(struct mscab_decompressor *base, + struct mscabd_cabinet *origcab) +{ + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + struct mscabd_folder_data *dat, *ndat; + struct mscabd_cabinet *cab, *ncab; + struct mscabd_folder *fol, *nfol; + struct mscabd_file *fi, *nfi; + struct mspack_system *sys; + + if (!base) return; + sys = self->system; + + self->error = MSPACK_ERR_OK; + + while (origcab) { + /* free files */ + for (fi = origcab->files; fi; fi = nfi) { + nfi = fi->next; + sys->free(fi->filename); + sys->free(fi); + } + + /* free folders */ + for (fol = origcab->folders; fol; fol = nfol) { + nfol = fol->next; + + /* free folder decompression state if it has been decompressed */ + if (self->d && (self->d->folder == (struct mscabd_folder_p *) fol)) { + if (self->d->infh) sys->close(self->d->infh); + cabd_free_decomp(self); + sys->free(self->d); + self->d = NULL; + } + + /* free folder data segments */ + for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) { + ndat = dat->next; + sys->free(dat); + } + sys->free(fol); + } + + /* free predecessor cabinets (and the original cabinet's strings) */ + for (cab = origcab; cab; cab = ncab) { + ncab = cab->prevcab; + sys->free(cab->prevname); + sys->free(cab->nextname); + sys->free(cab->previnfo); + sys->free(cab->nextinfo); + if (cab != origcab) sys->free(cab); + } + + /* free successor cabinets */ + for (cab = origcab->nextcab; cab; cab = ncab) { + ncab = cab->nextcab; + sys->free(cab->prevname); + sys->free(cab->nextname); + sys->free(cab->previnfo); + sys->free(cab->nextinfo); + sys->free(cab); + } + + /* free actual cabinet structure */ + cab = origcab->next; + sys->free(origcab); + + /* repeat full procedure again with the cab->next pointer (if set) */ + origcab = cab; + } +} + +/*************************************** + * CABD_READ_HEADERS + *************************************** + * reads the cabinet file header, folder list and file list. + * fills out a pre-existing mscabd_cabinet structure, allocates memory + * for folders and files as necessary + */ +static int cabd_read_headers(struct mspack_system *sys, + struct mspack_file *fh, + struct mscabd_cabinet_p *cab, + off_t offset, int quiet) +{ + int num_folders, num_files, folder_resv, i, x; + struct mscabd_folder_p *fol, *linkfol = NULL; + struct mscabd_file *file, *linkfile = NULL; + unsigned char buf[64]; + + /* initialise pointers */ + cab->base.next = NULL; + cab->base.files = NULL; + cab->base.folders = NULL; + cab->base.prevcab = cab->base.nextcab = NULL; + cab->base.prevname = cab->base.nextname = NULL; + cab->base.previnfo = cab->base.nextinfo = NULL; + + cab->base.base_offset = offset; + + /* seek to CFHEADER */ + if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { + return MSPACK_ERR_SEEK; + } + + /* read in the CFHEADER */ + if (sys->read(fh, &buf[0], cfhead_SIZEOF) != cfhead_SIZEOF) { + return MSPACK_ERR_READ; + } + + /* check for "MSCF" signature */ + if (EndGetI32(&buf[cfhead_Signature]) != 0x4643534D) { + return MSPACK_ERR_SIGNATURE; + } + + /* some basic header fields */ + cab->base.length = EndGetI32(&buf[cfhead_CabinetSize]); + cab->base.set_id = EndGetI16(&buf[cfhead_SetID]); + cab->base.set_index = EndGetI16(&buf[cfhead_CabinetIndex]); + + /* get the number of folders */ + num_folders = EndGetI16(&buf[cfhead_NumFolders]); + if (num_folders == 0) { + if (!quiet) sys->message(fh, "no folders in cabinet."); + return MSPACK_ERR_DATAFORMAT; + } + + /* get the number of files */ + num_files = EndGetI16(&buf[cfhead_NumFiles]); + if (num_files == 0) { + if (!quiet) sys->message(fh, "no files in cabinet."); + return MSPACK_ERR_DATAFORMAT; + } + + /* check cabinet version */ + if ((buf[cfhead_MajorVersion] != 1) && (buf[cfhead_MinorVersion] != 3)) { + if (!quiet) sys->message(fh, "WARNING; cabinet version is not 1.3"); + } + + /* read the reserved-sizes part of header, if present */ + cab->base.flags = EndGetI16(&buf[cfhead_Flags]); + if (cab->base.flags & cfheadRESERVE_PRESENT) { + if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) { + return MSPACK_ERR_READ; + } + cab->base.header_resv = EndGetI16(&buf[cfheadext_HeaderReserved]); + folder_resv = buf[cfheadext_FolderReserved]; + cab->block_resv = buf[cfheadext_DataReserved]; + + if (cab->base.header_resv > 60000) { + if (!quiet) sys->message(fh, "WARNING; reserved header > 60000."); + } + + /* skip the reserved header */ + if (cab->base.header_resv) { + if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) { + return MSPACK_ERR_SEEK; + } + } + } + else { + cab->base.header_resv = 0; + folder_resv = 0; + cab->block_resv = 0; + } + + /* read name and info of preceeding cabinet in set, if present */ + if (cab->base.flags & cfheadPREV_CABINET) { + cab->base.prevname = cabd_read_string(sys, fh, cab, &x); if (x) return x; + cab->base.previnfo = cabd_read_string(sys, fh, cab, &x); if (x) return x; + } + + /* read name and info of next cabinet in set, if present */ + if (cab->base.flags & cfheadNEXT_CABINET) { + cab->base.nextname = cabd_read_string(sys, fh, cab, &x); if (x) return x; + cab->base.nextinfo = cabd_read_string(sys, fh, cab, &x); if (x) return x; + } + + /* read folders */ + for (i = 0; i < num_folders; i++) { + if (sys->read(fh, &buf[0], cffold_SIZEOF) != cffold_SIZEOF) { + return MSPACK_ERR_READ; + } + if (folder_resv) { + if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) { + return MSPACK_ERR_SEEK; + } + } + + if (!(fol = (struct mscabd_folder_p *) sys->alloc(sys, sizeof(struct mscabd_folder_p)))) { + return MSPACK_ERR_NOMEMORY; + } + fol->base.next = NULL; + fol->base.comp_type = EndGetI16(&buf[cffold_CompType]); + fol->base.num_blocks = EndGetI16(&buf[cffold_NumBlocks]); + fol->data.next = NULL; + fol->data.cab = (struct mscabd_cabinet_p *) cab; + fol->data.offset = offset + (off_t) + ( (unsigned int) EndGetI32(&buf[cffold_DataOffset]) ); + fol->merge_prev = NULL; + fol->merge_next = NULL; + + /* link folder into list of folders */ + if (!linkfol) cab->base.folders = (struct mscabd_folder *) fol; + else linkfol->base.next = (struct mscabd_folder *) fol; + linkfol = fol; + } + + /* read files */ + for (i = 0; i < num_files; i++) { + if (sys->read(fh, &buf[0], cffile_SIZEOF) != cffile_SIZEOF) { + return MSPACK_ERR_READ; + } + + if (!(file = (struct mscabd_file *) sys->alloc(sys, sizeof(struct mscabd_file)))) { + return MSPACK_ERR_NOMEMORY; + } + + file->next = NULL; + file->length = EndGetI32(&buf[cffile_UncompressedSize]); + file->attribs = EndGetI16(&buf[cffile_Attribs]); + file->offset = EndGetI32(&buf[cffile_FolderOffset]); + + /* set folder pointer */ + x = EndGetI16(&buf[cffile_FolderIndex]); + if (x < cffileCONTINUED_FROM_PREV) { + /* normal folder index; count up to the correct folder. the folder + * pointer will be NULL if folder index is invalid */ + struct mscabd_folder *ifol = cab->base.folders; + while (x--) if (ifol) ifol = ifol->next; + file->folder = ifol; + + if (!ifol) { + sys->free(file); + D(("invalid folder index")) + return MSPACK_ERR_DATAFORMAT; + } + } + else { + /* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or + * CONTINUED_PREV_AND_NEXT */ + if ((x == cffileCONTINUED_TO_NEXT) || + (x == cffileCONTINUED_PREV_AND_NEXT)) + { + /* get last folder */ + struct mscabd_folder *ifol = cab->base.folders; + while (ifol->next) ifol = ifol->next; + file->folder = ifol; + + /* set "merge next" pointer */ + fol = (struct mscabd_folder_p *) ifol; + if (!fol->merge_next) fol->merge_next = file; + } + + if ((x == cffileCONTINUED_FROM_PREV) || + (x == cffileCONTINUED_PREV_AND_NEXT)) + { + /* get first folder */ + file->folder = cab->base.folders; + + /* set "merge prev" pointer */ + fol = (struct mscabd_folder_p *) file->folder; + if (!fol->merge_prev) fol->merge_prev = file; + } + } + + /* get time */ + x = EndGetI16(&buf[cffile_Time]); + file->time_h = x >> 11; + file->time_m = (x >> 5) & 0x3F; + file->time_s = (x << 1) & 0x3E; + + /* get date */ + x = EndGetI16(&buf[cffile_Date]); + file->date_d = x & 0x1F; + file->date_m = (x >> 5) & 0xF; + file->date_y = (x >> 9) + 1980; + + /* get filename */ + file->filename = cabd_read_string(sys, fh, cab, &x); + if (x) { + sys->free(file); + return x; + } + + /* link file entry into file list */ + if (!linkfile) cab->base.files = file; + else linkfile->next = file; + linkfile = file; + } + + return MSPACK_ERR_OK; +} + +static char *cabd_read_string(struct mspack_system *sys, + struct mspack_file *fh, + struct mscabd_cabinet_p *cab, int *error) +{ + off_t base = sys->tell(fh); + char buf[256], *str; + unsigned int len, i, ok; + + /* read up to 256 bytes */ + len = sys->read(fh, &buf[0], 256); + + /* search for a null terminator in the buffer */ + for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; } + if (!ok) { + *error = MSPACK_ERR_DATAFORMAT; + return NULL; + } + + len = i + 1; + + /* set the data stream to just after the string and return */ + if (sys->seek(fh, base + (off_t)len, MSPACK_SYS_SEEK_START)) { + *error = MSPACK_ERR_SEEK; + return NULL; + } + + if (!(str = (char *) sys->alloc(sys, len))) { + *error = MSPACK_ERR_NOMEMORY; + return NULL; + } + + sys->copy(&buf[0], str, len); + *error = MSPACK_ERR_OK; + return str; +} + +/*************************************** + * CABD_SEARCH, CABD_FIND + *************************************** + * cabd_search opens a file, finds its extent, allocates a search buffer, + * then reads through the whole file looking for possible cabinet headers. + * if it finds any, it tries to read them as real cabinets. returns a linked + * list of results + * + * cabd_find is the inner loop of cabd_search, to make it easier to + * break out of the loop and be sure that all resources are freed + */ +static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, + const char *filename) +{ + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + struct mscabd_cabinet_p *cab = NULL; + struct mspack_system *sys; + unsigned char *search_buf; + struct mspack_file *fh; + off_t filelen, firstlen = 0; + + if (!base) return NULL; + sys = self->system; + + /* allocate a search buffer */ + search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->param[MSCABD_PARAM_SEARCHBUF]); + if (!search_buf) { + self->error = MSPACK_ERR_NOMEMORY; + return NULL; + } + + /* open file and get its full file length */ + if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { + if (!(self->error = mspack_sys_filelen(sys, fh, &filelen))) { + self->error = cabd_find(self, search_buf, fh, filename, + filelen, &firstlen, &cab); + } + + /* truncated / extraneous data warning: */ + if (firstlen && (firstlen != filelen) && + (!cab || (cab->base.base_offset == 0))) + { + if (firstlen < filelen) { + sys->message(fh, "WARNING; possible %" LD + " extra bytes at end of file.", + filelen - firstlen); + } + else { + sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.", + firstlen - filelen); + } + } + + sys->close(fh); + } + else { + self->error = MSPACK_ERR_OPEN; + } + + /* free the search buffer */ + sys->free(search_buf); + + return (struct mscabd_cabinet *) cab; +} + +static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, + struct mspack_file *fh, const char *filename, off_t flen, + off_t *firstlen, struct mscabd_cabinet_p **firstcab) +{ + struct mscabd_cabinet_p *cab, *link = NULL; + off_t caboff, offset, length; + struct mspack_system *sys = self->system; + unsigned char *p, *pend, state = 0; + unsigned int cablen_u32 = 0, foffset_u32 = 0; + int false_cabs = 0; + +#ifndef LARGEFILE_SUPPORT + /* detect 32-bit off_t overflow */ + if (flen < 0) { + sys->message(fh, largefile_msg); + return MSPACK_ERR_OK; + } +#endif + + /* search through the full file length */ + for (offset = 0; offset < flen; offset += length) { + /* search length is either the full length of the search buffer, or the + * amount of data remaining to the end of the file, whichever is less. */ + length = flen - offset; + if (length > self->param[MSCABD_PARAM_SEARCHBUF]) { + length = self->param[MSCABD_PARAM_SEARCHBUF]; + } + + /* fill the search buffer with data from disk */ + if (sys->read(fh, &buf[0], (int) length) != (int) length) { + return MSPACK_ERR_READ; + } + + /* FAQ avoidance strategy */ + if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) { + sys->message(fh, "WARNING; found InstallShield header. " + "This is probably an InstallShield file. " + "Use UNSHIELD from www.synce.org to unpack it."); + } + + /* read through the entire buffer. */ + for (p = &buf[0], pend = &buf[length]; p < pend; ) { + switch (state) { + /* starting state */ + case 0: + /* we spend most of our time in this while loop, looking for + * a leading 'M' of the 'MSCF' signature */ + while (p < pend && *p != 0x4D) p++; + /* if we found tht 'M', advance state */ + if (p++ < pend) state = 1; + break; + + /* verify that the next 3 bytes are 'S', 'C' and 'F' */ + case 1: state = (*p++ == 0x53) ? 2 : 0; break; + case 2: state = (*p++ == 0x43) ? 3 : 0; break; + case 3: state = (*p++ == 0x46) ? 4 : 0; break; + + /* we don't care about bytes 4-7 (see default: for action) */ + + /* bytes 8-11 are the overall length of the cabinet */ + case 8: cablen_u32 = *p++; state++; break; + case 9: cablen_u32 |= *p++ << 8; state++; break; + case 10: cablen_u32 |= *p++ << 16; state++; break; + case 11: cablen_u32 |= *p++ << 24; state++; break; + + /* we don't care about bytes 12-15 (see default: for action) */ + + /* bytes 16-19 are the offset within the cabinet of the filedata */ + case 16: foffset_u32 = *p++; state++; break; + case 17: foffset_u32 |= *p++ << 8; state++; break; + case 18: foffset_u32 |= *p++ << 16; state++; break; + case 19: foffset_u32 |= *p++ << 24; + /* now we have recieved 20 bytes of potential cab header. work out + * the offset in the file of this potential cabinet */ + caboff = offset + (p - &buf[0]) - 20; + + /* should reading cabinet fail, restart search just after 'MSCF' */ + offset = caboff + 4; + + /* capture the "length of cabinet" field if there is a cabinet at + * offset 0 in the file, regardless of whether the cabinet can be + * read correctly or not */ + if (caboff == 0) *firstlen = (off_t) cablen_u32; + + /* check that the files offset is less than the alleged length of + * the cabinet, and that the offset + the alleged length are + * 'roughly' within the end of overall file length */ + if ((foffset_u32 < cablen_u32) && + ((caboff + (off_t) foffset_u32) < (flen + 32)) && + ((caboff + (off_t) cablen_u32) < (flen + 32)) ) + { + /* likely cabinet found -- try reading it */ + if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { + return MSPACK_ERR_NOMEMORY; + } + cab->base.filename = filename; + if (cabd_read_headers(sys, fh, cab, caboff, 1)) { + /* destroy the failed cabinet */ + cabd_close((struct mscab_decompressor *) self, + (struct mscabd_cabinet *) cab); + false_cabs++; + } + else { + /* cabinet read correctly! */ + + /* link the cab into the list */ + if (!link) *firstcab = cab; + else link->base.next = (struct mscabd_cabinet *) cab; + link = cab; + + /* cause the search to restart after this cab's data. */ + offset = caboff + (off_t) cablen_u32; + +#ifndef LARGEFILE_SUPPORT + /* detect 32-bit off_t overflow */ + if (offset < caboff) { + sys->message(fh, largefile_msg); + return MSPACK_ERR_OK; + } +#endif + } + } + + /* restart search */ + if (offset >= flen) return MSPACK_ERR_OK; + if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { + return MSPACK_ERR_SEEK; + } + length = 0; + p = pend; + state = 0; + break; + + /* for bytes 4-7 and 12-15, just advance state/pointer */ + default: + p++, state++; + } /* switch(state) */ + } /* for (... p < pend ...) */ + } /* for (... offset < length ...) */ + + if (false_cabs) { + D(("%d false cabinets found", false_cabs)) + } + + return MSPACK_ERR_OK; +} + +/*************************************** + * CABD_MERGE, CABD_PREPEND, CABD_APPEND + *************************************** + * joins cabinets together, also merges split folders between these two + * cabinets only. This includes freeing the duplicate folder and file(s) + * and allocating a further mscabd_folder_data structure to append to the + * merged folder's data parts list. + */ +static int cabd_prepend(struct mscab_decompressor *base, + struct mscabd_cabinet *cab, + struct mscabd_cabinet *prevcab) +{ + return cabd_merge(base, prevcab, cab); +} + +static int cabd_append(struct mscab_decompressor *base, + struct mscabd_cabinet *cab, + struct mscabd_cabinet *nextcab) +{ + return cabd_merge(base, cab, nextcab); +} + +static int cabd_merge(struct mscab_decompressor *base, + struct mscabd_cabinet *lcab, + struct mscabd_cabinet *rcab) +{ + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + struct mscabd_folder_data *data, *ndata; + struct mscabd_folder_p *lfol, *rfol; + struct mscabd_file *fi, *rfi, *lfi; + struct mscabd_cabinet *cab; + struct mspack_system *sys; + + if (!self) return MSPACK_ERR_ARGS; + sys = self->system; + + /* basic args check */ + if (!lcab || !rcab || (lcab == rcab)) { + D(("lcab NULL, rcab NULL or lcab = rcab")) + return self->error = MSPACK_ERR_ARGS; + } + + /* check there's not already a cabinet attached */ + if (lcab->nextcab || rcab->prevcab) { + D(("cabs already joined")) + return self->error = MSPACK_ERR_ARGS; + } + + /* do not create circular cabinet chains */ + for (cab = lcab->prevcab; cab; cab = cab->prevcab) { + if (cab == rcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;} + } + for (cab = rcab->nextcab; cab; cab = cab->nextcab) { + if (cab == lcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;} + } + + /* warn about odd set IDs or indices */ + if (lcab->set_id != rcab->set_id) { + sys->message(NULL, "WARNING; merged cabinets with differing Set IDs."); + } + + if (lcab->set_index > rcab->set_index) { + sys->message(NULL, "WARNING; merged cabinets with odd order."); + } + + /* merging the last folder in lcab with the first folder in rcab */ + lfol = (struct mscabd_folder_p *) lcab->folders; + rfol = (struct mscabd_folder_p *) rcab->folders; + while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; + + /* do we need to merge folders? */ + if (!lfol->merge_next && !rfol->merge_prev) { + /* no, at least one of the folders is not for merging */ + + /* attach cabs */ + lcab->nextcab = rcab; + rcab->prevcab = lcab; + + /* attach folders */ + lfol->base.next = (struct mscabd_folder *) rfol; + + /* attach files */ + fi = lcab->files; + while (fi->next) fi = fi->next; + fi->next = rcab->files; + } + else { + /* folder merge required - do the files match? */ + if (! cabd_can_merge_folders(sys, lfol, rfol)) { + return self->error = MSPACK_ERR_DATAFORMAT; + } + + /* allocate a new folder data structure */ + if (!(data = (struct mscabd_folder_data *) sys->alloc(sys, sizeof(struct mscabd_folder_data)))) { + return self->error = MSPACK_ERR_NOMEMORY; + } + + /* attach cabs */ + lcab->nextcab = rcab; + rcab->prevcab = lcab; + + /* append rfol's data to lfol */ + ndata = &lfol->data; + while (ndata->next) ndata = ndata->next; + ndata->next = data; + *data = rfol->data; + rfol->data.next = NULL; + + /* lfol becomes rfol. + * NOTE: special case, don't merge if rfol is merge prev and next, + * rfol->merge_next is going to be deleted, so keep lfol's version + * instead */ + lfol->base.num_blocks += rfol->base.num_blocks - 1; + if ((rfol->merge_next == NULL) || + (rfol->merge_next->folder != (struct mscabd_folder *) rfol)) + { + lfol->merge_next = rfol->merge_next; + } + + /* attach the rfol's folder (except the merge folder) */ + while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; + lfol->base.next = rfol->base.next; + + /* free disused merge folder */ + sys->free(rfol); + + /* attach rfol's files */ + fi = lcab->files; + while (fi->next) fi = fi->next; + fi->next = rcab->files; + + /* delete all files from rfol's merge folder */ + lfi = NULL; + for (fi = lcab->files; fi ; fi = rfi) { + rfi = fi->next; + /* if file's folder matches the merge folder, unlink and free it */ + if (fi->folder == (struct mscabd_folder *) rfol) { + if (lfi) lfi->next = rfi; else lcab->files = rfi; + sys->free(fi->filename); + sys->free(fi); + } + else lfi = fi; + } + } + + /* all done! fix files and folders pointers in all cabs so they all + * point to the same list */ + for (cab = lcab->prevcab; cab; cab = cab->prevcab) { + cab->files = lcab->files; + cab->folders = lcab->folders; + } + + for (cab = lcab->nextcab; cab; cab = cab->nextcab) { + cab->files = lcab->files; + cab->folders = lcab->folders; + } + + return self->error = MSPACK_ERR_OK; +} + +/* decides if two folders are OK to merge */ +static int cabd_can_merge_folders(struct mspack_system *sys, + struct mscabd_folder_p *lfol, + struct mscabd_folder_p *rfol) +{ + struct mscabd_file *lfi, *rfi, *l, *r; + int matching = 1; + + /* check that both folders use the same compression method/settings */ + if (lfol->base.comp_type != rfol->base.comp_type) { + D(("folder merge: compression type mismatch")) + return 0; + } + + if (!(lfi = lfol->merge_next) || !(rfi = rfol->merge_prev)) { + D(("folder merge: one cabinet has no files to merge")) + return 0; + } + + /* for all files in lfol (which is the last folder in whichever cab and + * only has files to merge), compare them to the files from rfol. They + * should be identical in number and order. to verify this, check the + * offset and length of each file. */ + for (l=lfi, r=rfi; l; l=l->next, r=r->next) { + if (!r || (l->offset != r->offset) || (l->length != r->length)) { + matching = 0; + break; + } + } + + if (matching) return 1; + + /* if rfol does not begin with an identical copy of the files in lfol, make + * make a judgement call; if at least ONE file from lfol is in rfol, allow + * the merge with a warning about missing files. */ + matching = 0; + for (l = lfi; l; l = l->next) { + for (r = rfi; r; r = r->next) { + if (l->offset == r->offset && l->length == r->length) break; + } + if (r) matching = 1; else sys->message(NULL, + "WARNING; merged file %s not listed in both cabinets", l->filename); + } + return matching; +} + + +/*************************************** + * CABD_EXTRACT + *************************************** + * extracts a file from a cabinet + */ +static int cabd_extract(struct mscab_decompressor *base, + struct mscabd_file *file, const char *filename) +{ + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + struct mscabd_folder_p *fol; + struct mspack_system *sys; + struct mspack_file *fh; + + if (!self) return MSPACK_ERR_ARGS; + if (!file) return self->error = MSPACK_ERR_ARGS; + + sys = self->system; + fol = (struct mscabd_folder_p *) file->folder; + + /* check if file can be extracted */ + if ((!fol) || (fol->merge_prev) || + (((file->offset + file->length) / CAB_BLOCKMAX) > fol->base.num_blocks)) + { + sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " + "cabinet set is incomplete.", file->filename); + return self->error = MSPACK_ERR_DATAFORMAT; + } + + /* allocate generic decompression state */ + if (!self->d) { + self->d = (struct mscabd_decompress_state *) sys->alloc(sys, sizeof(struct mscabd_decompress_state)); + if (!self->d) return self->error = MSPACK_ERR_NOMEMORY; + self->d->folder = NULL; + self->d->data = NULL; + self->d->sys = *sys; + self->d->sys.read = &cabd_sys_read; + self->d->sys.write = &cabd_sys_write; + self->d->state = NULL; + self->d->infh = NULL; + self->d->incab = NULL; + } + + /* do we need to change folder or reset the current folder? */ + if ((self->d->folder != fol) || (self->d->offset > file->offset)) { + /* do we need to open a new cab file? */ + if (!self->d->infh || (fol->data.cab != self->d->incab)) { + /* close previous file handle if from a different cab */ + if (self->d->infh) sys->close(self->d->infh); + self->d->incab = fol->data.cab; + self->d->infh = sys->open(sys, fol->data.cab->base.filename, + MSPACK_SYS_OPEN_READ); + if (!self->d->infh) return self->error = MSPACK_ERR_OPEN; + } + /* seek to start of data blocks */ + if (sys->seek(self->d->infh, fol->data.offset, MSPACK_SYS_SEEK_START)) { + return self->error = MSPACK_ERR_SEEK; + } + + /* set up decompressor */ + if (cabd_init_decomp(self, (unsigned int) fol->base.comp_type)) { + return self->error; + } + + /* initialise new folder state */ + self->d->folder = fol; + self->d->data = &fol->data; + self->d->offset = 0; + self->d->block = 0; + self->d->i_ptr = self->d->i_end = &self->d->input[0]; + + /* read_error lasts for the lifetime of a decompressor */ + self->read_error = MSPACK_ERR_OK; + } + + /* open file for output */ + if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { + return self->error = MSPACK_ERR_OPEN; + } + + self->error = MSPACK_ERR_OK; + + /* if file has more than 0 bytes */ + if (file->length) { + off_t bytes; + int error; + /* get to correct offset. + * - use NULL fh to say 'no writing' to cabd_sys_write() + * - if cabd_sys_read() has an error, it will set self->read_error + * and pass back MSPACK_ERR_READ + */ + self->d->outfh = NULL; + if ((bytes = file->offset - self->d->offset)) { + error = self->d->decompress(self->d->state, bytes); + self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; + } + + /* if getting to the correct offset was error free, unpack file */ + if (!self->error) { + self->d->outfh = fh; + error = self->d->decompress(self->d->state, (off_t) file->length); + self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; + } + } + + /* close output file */ + sys->close(fh); + self->d->outfh = NULL; + + return self->error; +} + +/*************************************** + * CABD_INIT_DECOMP, CABD_FREE_DECOMP + *************************************** + * cabd_init_decomp initialises decompression state, according to which + * decompression method was used. relies on self->d->folder being the same + * as when initialised. + * + * cabd_free_decomp frees decompression state, according to which method + * was used. + */ +static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct) +{ + struct mspack_file *fh = (struct mspack_file *) self; + + assert(self && self->d); + + /* free any existing decompressor */ + cabd_free_decomp(self); + + self->d->comp_type = ct; + + switch (ct & cffoldCOMPTYPE_MASK) { + case cffoldCOMPTYPE_NONE: + self->d->decompress = (int (*)(void *, off_t)) &noned_decompress; + self->d->state = noned_init(&self->d->sys, fh, fh, + self->param[MSCABD_PARAM_DECOMPBUF]); + break; + case cffoldCOMPTYPE_MSZIP: + self->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress; + self->d->state = mszipd_init(&self->d->sys, fh, fh, + self->param[MSCABD_PARAM_DECOMPBUF], + self->param[MSCABD_PARAM_FIXMSZIP]); + break; + case cffoldCOMPTYPE_QUANTUM: + self->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress; + self->d->state = qtmd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, + self->param[MSCABD_PARAM_DECOMPBUF]); + break; + case cffoldCOMPTYPE_LZX: + self->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress; + self->d->state = lzxd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, 0, + self->param[MSCABD_PARAM_DECOMPBUF], (off_t) 0); + break; + default: + return self->error = MSPACK_ERR_DATAFORMAT; + } + return self->error = (self->d->state) ? MSPACK_ERR_OK : MSPACK_ERR_NOMEMORY; +} + +static void cabd_free_decomp(struct mscab_decompressor_p *self) { + if (!self || !self->d || !self->d->folder || !self->d->state) return; + + switch (self->d->comp_type & cffoldCOMPTYPE_MASK) { + case cffoldCOMPTYPE_NONE: noned_free((struct noned_state *) self->d->state); break; + case cffoldCOMPTYPE_MSZIP: mszipd_free((struct mszipd_stream *) self->d->state); break; + case cffoldCOMPTYPE_QUANTUM: qtmd_free((struct qtmd_stream *) self->d->state); break; + case cffoldCOMPTYPE_LZX: lzxd_free((struct lzxd_stream *) self->d->state); break; + } + self->d->decompress = NULL; + self->d->state = NULL; +} + +/*************************************** + * CABD_SYS_READ, CABD_SYS_WRITE + *************************************** + * cabd_sys_read is the internal reader function which the decompressors + * use. will read data blocks (and merge split blocks) from the cabinet + * and serve the read bytes to the decompressors + * + * cabd_sys_write is the internal writer function which the decompressors + * use. it either writes data to disk (self->d->outfh) with the real + * sys->write() function, or does nothing with the data when + * self->d->outfh == NULL. advances self->d->offset + */ +static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) { + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; + unsigned char *buf = (unsigned char *) buffer; + struct mspack_system *sys = self->system; + int avail, todo, outlen, ignore_cksum; + + ignore_cksum = self->param[MSCABD_PARAM_FIXMSZIP] && + ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP); + + todo = bytes; + while (todo > 0) { + avail = self->d->i_end - self->d->i_ptr; + + /* if out of input data, read a new block */ + if (avail) { + /* copy as many input bytes available as possible */ + if (avail > todo) avail = todo; + sys->copy(self->d->i_ptr, buf, (size_t) avail); + self->d->i_ptr += avail; + buf += avail; + todo -= avail; + } + else { + /* out of data, read a new block */ + + /* check if we're out of input blocks, advance block counter */ + if (self->d->block++ >= self->d->folder->base.num_blocks) { + self->read_error = MSPACK_ERR_DATAFORMAT; + break; + } + + /* read a block */ + self->read_error = cabd_sys_read_block(sys, self->d, &outlen, ignore_cksum); + if (self->read_error) return -1; + + /* special Quantum hack -- trailer byte to allow the decompressor + * to realign itself. CAB Quantum blocks, unlike LZX blocks, can have + * anything from 0 to 4 trailing null bytes. */ + if ((self->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) { + *self->d->i_end++ = 0xFF; + } + + /* is this the last block? */ + if (self->d->block >= self->d->folder->base.num_blocks) { + /* last block */ + if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) { + /* special LZX hack -- on the last block, inform LZX of the + * size of the output data stream. */ + lzxd_set_output_length((struct lzxd_stream *) self->d->state, (off_t) + ((self->d->block-1) * CAB_BLOCKMAX + outlen)); + } + } + else { + /* not the last block */ + if (outlen != CAB_BLOCKMAX) { + self->system->message(self->d->infh, + "WARNING; non-maximal data block"); + } + } + } /* if (avail) */ + } /* while (todo > 0) */ + return bytes - todo; +} + +static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) { + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; + self->d->offset += bytes; + if (self->d->outfh) { + return self->system->write(self->d->outfh, buffer, bytes); + } + return bytes; +} + +/*************************************** + * CABD_SYS_READ_BLOCK + *************************************** + * reads a whole data block from a cab file. the block may span more than + * one cab file, if it does then the fragments will be reassembled + */ +static int cabd_sys_read_block(struct mspack_system *sys, + struct mscabd_decompress_state *d, + int *out, int ignore_cksum) +{ + unsigned char hdr[cfdata_SIZEOF]; + unsigned int cksum; + int len; + + /* reset the input block pointer and end of block pointer */ + d->i_ptr = d->i_end = &d->input[0]; + + do { + /* read the block header */ + if (sys->read(d->infh, &hdr[0], cfdata_SIZEOF) != cfdata_SIZEOF) { + return MSPACK_ERR_READ; + } + + /* skip any reserved block headers */ + if (d->data->cab->block_resv && + sys->seek(d->infh, (off_t) d->data->cab->block_resv, + MSPACK_SYS_SEEK_CUR)) + { + return MSPACK_ERR_SEEK; + } + + /* blocks must not be over CAB_INPUTMAX in size */ + len = EndGetI16(&hdr[cfdata_CompressedSize]); + if (((d->i_end - d->i_ptr) + len) > CAB_INPUTMAX) { + D(("block size > CAB_INPUTMAX (%ld + %d)", d->i_end - d->i_ptr, len)) + return MSPACK_ERR_DATAFORMAT; + } + + /* blocks must not expand to more than CAB_BLOCKMAX */ + if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) { + D(("block size > CAB_BLOCKMAX")) + return MSPACK_ERR_DATAFORMAT; + } + + /* read the block data */ + if (sys->read(d->infh, d->i_end, len) != len) { + return MSPACK_ERR_READ; + } + + /* perform checksum test on the block (if one is stored) */ + if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) { + unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0); + if (cabd_checksum(&hdr[4], 4, sum2) != cksum) { + if (!ignore_cksum) return MSPACK_ERR_CHECKSUM; + sys->message(d->infh, "WARNING; bad block checksum found"); + } + } + + /* advance end of block pointer to include newly read data */ + d->i_end += len; + + /* uncompressed size == 0 means this block was part of a split block + * and it continues as the first block of the next cabinet in the set. + * otherwise, this is the last part of the block, and no more block + * reading needs to be done. + */ + /* EXIT POINT OF LOOP -- uncompressed size != 0 */ + if ((*out = EndGetI16(&hdr[cfdata_UncompressedSize]))) { + return MSPACK_ERR_OK; + } + + /* otherwise, advance to next cabinet */ + + /* close current file handle */ + sys->close(d->infh); + d->infh = NULL; + + /* advance to next member in the cabinet set */ + if (!(d->data = d->data->next)) { + D(("ran out of splits in cabinet set")) + return MSPACK_ERR_DATAFORMAT; + } + + /* open next cab file */ + d->incab = d->data->cab; + if (!(d->infh = sys->open(sys, d->incab->base.filename, + MSPACK_SYS_OPEN_READ))) + { + return MSPACK_ERR_OPEN; + } + + /* seek to start of data blocks */ + if (sys->seek(d->infh, d->data->offset, MSPACK_SYS_SEEK_START)) { + return MSPACK_ERR_SEEK; + } + } while (1); + + /* not reached */ + return MSPACK_ERR_OK; +} + +static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes, + unsigned int cksum) +{ + unsigned int len, ul = 0; + + for (len = bytes >> 2; len--; data += 4) { + cksum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24)); + } + + switch (bytes & 3) { + case 3: ul |= *data++ << 16; + case 2: ul |= *data++ << 8; + case 1: ul |= *data; + } + cksum ^= ul; + + return cksum; +} + +/*************************************** + * NONED_INIT, NONED_DECOMPRESS, NONED_FREE + *************************************** + * the "not compressed" method decompressor + */ +struct noned_state { + struct mspack_system *sys; + struct mspack_file *i; + struct mspack_file *o; + unsigned char *buf; + int bufsize; +}; + +static struct noned_state *noned_init(struct mspack_system *sys, + struct mspack_file *in, + struct mspack_file *out, + int bufsize) +{ + struct noned_state *state = (struct noned_state *) sys->alloc(sys, sizeof(struct noned_state)); + unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) bufsize); + if (state && buf) { + state->sys = sys; + state->i = in; + state->o = out; + state->buf = buf; + state->bufsize = bufsize; + } + else { + sys->free(buf); + sys->free(state); + state = NULL; + } + return state; +} + +static int noned_decompress(struct noned_state *s, off_t bytes) { + int run; + while (bytes > 0) { + run = (bytes > s->bufsize) ? s->bufsize : (int) bytes; + if (s->sys->read(s->i, &s->buf[0], run) != run) return MSPACK_ERR_READ; + if (s->sys->write(s->o, &s->buf[0], run) != run) return MSPACK_ERR_WRITE; + bytes -= run; + } + return MSPACK_ERR_OK; +} + +static void noned_free(struct noned_state *state) { + struct mspack_system *sys; + if (state) { + sys = state->sys; + sys->free(state->buf); + sys->free(state); + } +} + + +/*************************************** + * CABD_PARAM + *************************************** + * allows a parameter to be set + */ +static int cabd_param(struct mscab_decompressor *base, int param, int value) { + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + if (!self) return MSPACK_ERR_ARGS; + + switch (param) { + case MSCABD_PARAM_SEARCHBUF: + if (value < 4) return MSPACK_ERR_ARGS; + self->param[MSCABD_PARAM_SEARCHBUF] = value; + break; + case MSCABD_PARAM_FIXMSZIP: + self->param[MSCABD_PARAM_FIXMSZIP] = value; + break; + case MSCABD_PARAM_DECOMPBUF: + if (value < 4) return MSPACK_ERR_ARGS; + self->param[MSCABD_PARAM_DECOMPBUF] = value; + break; + default: + return MSPACK_ERR_ARGS; + } + return MSPACK_ERR_OK; +} + +/*************************************** + * CABD_ERROR + *************************************** + * returns the last error that occurred + */ +static int cabd_error(struct mscab_decompressor *base) { + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + return (self) ? self->error : MSPACK_ERR_ARGS; +} diff --git a/rbutil/rbutilqt/mspack/chm.h b/rbutil/rbutilqt/mspack/chm.h new file mode 100644 index 0000000000..a85d2e1731 --- /dev/null +++ b/rbutil/rbutilqt/mspack/chm.h @@ -0,0 +1,122 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_CHM_H +#define MSPACK_CHM_H 1 + +#include + +/* generic CHM definitions */ + +#define chmhead_Signature (0x0000) +#define chmhead_Version (0x0004) +#define chmhead_HeaderLen (0x0008) +#define chmhead_Unknown1 (0x000C) +#define chmhead_Timestamp (0x0010) +#define chmhead_LanguageID (0x0014) +#define chmhead_GUID1 (0x0018) +#define chmhead_GUID2 (0x0028) +#define chmhead_SIZEOF (0x0038) + +#define chmhst_OffsetHS0 (0x0000) +#define chmhst_LengthHS0 (0x0008) +#define chmhst_OffsetHS1 (0x0010) +#define chmhst_LengthHS1 (0x0018) +#define chmhst_SIZEOF (0x0020) +#define chmhst3_OffsetCS0 (0x0020) +#define chmhst3_SIZEOF (0x0028) + +#define chmhs0_Unknown1 (0x0000) +#define chmhs0_Unknown2 (0x0004) +#define chmhs0_FileLen (0x0008) +#define chmhs0_Unknown3 (0x0010) +#define chmhs0_Unknown4 (0x0014) +#define chmhs0_SIZEOF (0x0018) + +#define chmhs1_Signature (0x0000) +#define chmhs1_Version (0x0004) +#define chmhs1_HeaderLen (0x0008) +#define chmhs1_Unknown1 (0x000C) +#define chmhs1_ChunkSize (0x0010) +#define chmhs1_Density (0x0014) +#define chmhs1_Depth (0x0018) +#define chmhs1_IndexRoot (0x001C) +#define chmhs1_FirstPMGL (0x0020) +#define chmhs1_LastPMGL (0x0024) +#define chmhs1_Unknown2 (0x0028) +#define chmhs1_NumChunks (0x002C) +#define chmhs1_LanguageID (0x0030) +#define chmhs1_GUID (0x0034) +#define chmhs1_Unknown3 (0x0044) +#define chmhs1_Unknown4 (0x0048) +#define chmhs1_Unknown5 (0x004C) +#define chmhs1_Unknown6 (0x0050) +#define chmhs1_SIZEOF (0x0054) + +#define pmgl_Signature (0x0000) +#define pmgl_QuickRefSize (0x0004) +#define pmgl_Unknown1 (0x0008) +#define pmgl_PrevChunk (0x000C) +#define pmgl_NextChunk (0x0010) +#define pmgl_Entries (0x0014) +#define pmgl_headerSIZEOF (0x0014) + +#define pmgi_Signature (0x0000) +#define pmgi_QuickRefSize (0x0004) +#define pmgi_Entries (0x0008) +#define pmgi_headerSIZEOF (0x000C) + +#define lzxcd_Length (0x0000) +#define lzxcd_Signature (0x0004) +#define lzxcd_Version (0x0008) +#define lzxcd_ResetInterval (0x000C) +#define lzxcd_WindowSize (0x0010) +#define lzxcd_CacheSize (0x0014) +#define lzxcd_Unknown1 (0x0018) +#define lzxcd_SIZEOF (0x001C) + +#define lzxrt_Unknown1 (0x0000) +#define lzxrt_NumEntries (0x0004) +#define lzxrt_EntrySize (0x0008) +#define lzxrt_TableOffset (0x000C) +#define lzxrt_UncompLen (0x0010) +#define lzxrt_CompLen (0x0018) +#define lzxrt_FrameLen (0x0020) +#define lzxrt_Entries (0x0028) +#define lzxrt_headerSIZEOF (0x0028) + +/* CHM compression definitions */ + +struct mschm_compressor_p { + struct mschm_compressor base; + struct mspack_system *system; + char *temp_file; + int use_temp_file; + int error; +}; + +/* CHM decompression definitions */ +struct mschmd_decompress_state { + struct mschmd_header *chm; /* CHM file being decompressed */ + off_t offset; /* uncompressed offset within folder */ + off_t inoffset; /* offset in input file */ + struct lzxd_stream *state; /* LZX decompressor state */ + struct mspack_system sys; /* special I/O code for decompressor */ + struct mspack_file *infh; /* input file handle */ + struct mspack_file *outfh; /* output file handle */ +}; + +struct mschm_decompressor_p { + struct mschm_decompressor base; + struct mspack_system *system; + struct mschmd_decompress_state *d; + int error; +}; + +#endif diff --git a/rbutil/rbutilqt/mspack/chmc.c b/rbutil/rbutilqt/mspack/chmc.c new file mode 100644 index 0000000000..72f6c5b389 --- /dev/null +++ b/rbutil/rbutilqt/mspack/chmc.c @@ -0,0 +1,24 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* CHM compression implementation */ + +#include +#include + +struct mschm_compressor * + mspack_create_chm_compressor(struct mspack_system *sys) +{ + /* todo */ + return NULL; +} + +void mspack_destroy_chm_compressor(struct mschm_compressor *self) { + /* todo */ +} diff --git a/rbutil/rbutilqt/mspack/chmd.c b/rbutil/rbutilqt/mspack/chmd.c new file mode 100644 index 0000000000..6e4a1bf24f --- /dev/null +++ b/rbutil/rbutilqt/mspack/chmd.c @@ -0,0 +1,1346 @@ +/* This file is part of libmspack. + * (C) 2003-2011 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* CHM decompression implementation */ + +#include +#include + +/* prototypes */ +static struct mschmd_header * chmd_open( + struct mschm_decompressor *base, const char *filename); +static struct mschmd_header * chmd_fast_open( + struct mschm_decompressor *base, const char *filename); +static struct mschmd_header *chmd_real_open( + struct mschm_decompressor *base, const char *filename, int entire); +static void chmd_close( + struct mschm_decompressor *base, struct mschmd_header *chm); +static int chmd_read_headers( + struct mspack_system *sys, struct mspack_file *fh, + struct mschmd_header *chm, int entire); +static int chmd_fast_find( + struct mschm_decompressor *base, struct mschmd_header *chm, + const char *filename, struct mschmd_file *f_ptr, int f_size); +static unsigned char *read_chunk( + struct mschm_decompressor_p *self, struct mschmd_header *chm, + struct mspack_file *fh, unsigned int chunk); +static int search_chunk( + struct mschmd_header *chm, const unsigned char *chunk, const char *filename, + const unsigned char **result, const unsigned char **result_end); +static inline int compare( + const char *s1, const char *s2, int l1, int l2); +static int chmd_extract( + struct mschm_decompressor *base, struct mschmd_file *file, + const char *filename); +static int chmd_sys_write( + struct mspack_file *file, void *buffer, int bytes); +static int chmd_init_decomp( + struct mschm_decompressor_p *self, struct mschmd_file *file); +static int read_reset_table( + struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec, + int entry, off_t *length_ptr, off_t *offset_ptr); +static int read_spaninfo( + struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec, + off_t *length_ptr); +static int find_sys_file( + struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec, + struct mschmd_file **f_ptr, const char *name); +static unsigned char *read_sys_file( + struct mschm_decompressor_p *self, struct mschmd_file *file); +static int chmd_error( + struct mschm_decompressor *base); +static int read_off64( + off_t *var, unsigned char *mem, struct mspack_system *sys, + struct mspack_file *fh); + +/* filenames of the system files used for decompression. + * Content and ControlData are essential. + * ResetTable is preferred, but SpanInfo can be used if not available + */ +static const char *content_name = "::DataSpace/Storage/MSCompressed/Content"; +static const char *control_name = "::DataSpace/Storage/MSCompressed/ControlData"; +static const char *spaninfo_name = "::DataSpace/Storage/MSCompressed/SpanInfo"; +static const char *rtable_name = "::DataSpace/Storage/MSCompressed/Transform/" + "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable"; + +/*************************************** + * MSPACK_CREATE_CHM_DECOMPRESSOR + *************************************** + * constructor + */ +struct mschm_decompressor * + mspack_create_chm_decompressor(struct mspack_system *sys) +{ + struct mschm_decompressor_p *self = NULL; + + if (!sys) sys = mspack_default_system; + if (!mspack_valid_system(sys)) return NULL; + + if ((self = (struct mschm_decompressor_p *) sys->alloc(sys, sizeof(struct mschm_decompressor_p)))) { + self->base.open = &chmd_open; + self->base.close = &chmd_close; + self->base.extract = &chmd_extract; + self->base.last_error = &chmd_error; + self->base.fast_open = &chmd_fast_open; + self->base.fast_find = &chmd_fast_find; + self->system = sys; + self->error = MSPACK_ERR_OK; + self->d = NULL; + } + return (struct mschm_decompressor *) self; +} + +/*************************************** + * MSPACK_DESTROY_CAB_DECOMPRESSOR + *************************************** + * destructor + */ +void mspack_destroy_chm_decompressor(struct mschm_decompressor *base) { + struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; + if (self) { + struct mspack_system *sys = self->system; + if (self->d) { + if (self->d->infh) sys->close(self->d->infh); + if (self->d->state) lzxd_free(self->d->state); + sys->free(self->d); + } + sys->free(self); + } +} + +/*************************************** + * CHMD_OPEN + *************************************** + * opens a file and tries to read it as a CHM file. + * Calls chmd_real_open() with entire=1. + */ +static struct mschmd_header *chmd_open(struct mschm_decompressor *base, + const char *filename) +{ + return chmd_real_open(base, filename, 1); +} + +/*************************************** + * CHMD_FAST_OPEN + *************************************** + * opens a file and tries to read it as a CHM file, but does not read + * the file headers. Calls chmd_real_open() with entire=0 + */ +static struct mschmd_header *chmd_fast_open(struct mschm_decompressor *base, + const char *filename) +{ + return chmd_real_open(base, filename, 0); +} + +/*************************************** + * CHMD_REAL_OPEN + *************************************** + * the real implementation of chmd_open() and chmd_fast_open(). It simply + * passes the "entire" parameter to chmd_read_headers(), which will then + * either read all headers, or a bare mininum. + */ +static struct mschmd_header *chmd_real_open(struct mschm_decompressor *base, + const char *filename, int entire) +{ + struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; + struct mschmd_header *chm = NULL; + struct mspack_system *sys; + struct mspack_file *fh; + int error; + + if (!base) return NULL; + sys = self->system; + + if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { + if ((chm = (struct mschmd_header *) sys->alloc(sys, sizeof(struct mschmd_header)))) { + chm->filename = filename; + error = chmd_read_headers(sys, fh, chm, entire); + if (error) { + /* if the error is DATAFORMAT, and there are some results, return + * partial results with a warning, rather than nothing */ + if (error == MSPACK_ERR_DATAFORMAT && (chm->files || chm->sysfiles)) { + sys->message(fh, "WARNING; contents are corrupt"); + error = MSPACK_ERR_OK; + } + else { + chmd_close(base, chm); + chm = NULL; + } + } + self->error = error; + } + else { + self->error = MSPACK_ERR_NOMEMORY; + } + sys->close(fh); + } + else { + self->error = MSPACK_ERR_OPEN; + } + return chm; +} + +/*************************************** + * CHMD_CLOSE + *************************************** + * frees all memory associated with a given mschmd_header + */ +static void chmd_close(struct mschm_decompressor *base, + struct mschmd_header *chm) +{ + struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; + struct mschmd_file *fi, *nfi; + struct mspack_system *sys; + unsigned int i; + + if (!base) return; + sys = self->system; + + self->error = MSPACK_ERR_OK; + + /* free files */ + for (fi = chm->files; fi; fi = nfi) { + nfi = fi->next; + sys->free(fi); + } + for (fi = chm->sysfiles; fi; fi = nfi) { + nfi = fi->next; + sys->free(fi); + } + + /* if this CHM was being decompressed, free decompression state */ + if (self->d && (self->d->chm == chm)) { + if (self->d->infh) sys->close(self->d->infh); + if (self->d->state) lzxd_free(self->d->state); + sys->free(self->d); + self->d = NULL; + } + + /* if this CHM had a chunk cache, free it and contents */ + if (chm->chunk_cache) { + for (i = 0; i < chm->num_chunks; i++) sys->free(chm->chunk_cache[i]); + sys->free(chm->chunk_cache); + } + + sys->free(chm); +} + +/*************************************** + * CHMD_READ_HEADERS + *************************************** + * reads the basic CHM file headers. If the "entire" parameter is + * non-zero, all file entries will also be read. fills out a pre-existing + * mschmd_header structure, allocates memory for files as necessary + */ + +/* The GUIDs found in CHM headers */ +static const unsigned char guids[32] = { + /* {7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC} */ + 0x10, 0xFD, 0x01, 0x7C, 0xAA, 0x7B, 0xD0, 0x11, + 0x9E, 0x0C, 0x00, 0xA0, 0xC9, 0x22, 0xE6, 0xEC, + /* {7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC} */ + 0x11, 0xFD, 0x01, 0x7C, 0xAA, 0x7B, 0xD0, 0x11, + 0x9E, 0x0C, 0x00, 0xA0, 0xC9, 0x22, 0xE6, 0xEC +}; + +/* reads an encoded integer into a variable; 7 bits of data per byte, + * the high bit is used to indicate that there is another byte */ +#define READ_ENCINT(var) do { \ + (var) = 0; \ + do { \ + if (p > end) goto chunk_end; \ + (var) = ((var) << 7) | (*p & 0x7F); \ + } while (*p++ & 0x80); \ +} while (0) + +static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, + struct mschmd_header *chm, int entire) +{ + unsigned int section, name_len, x, errors, num_chunks; + unsigned char buf[0x54], *chunk = NULL, *name, *p, *end; + struct mschmd_file *fi, *link = NULL; + off_t offset, length; + int num_entries; + + /* initialise pointers */ + chm->files = NULL; + chm->sysfiles = NULL; + chm->chunk_cache = NULL; + chm->sec0.base.chm = chm; + chm->sec0.base.id = 0; + chm->sec1.base.chm = chm; + chm->sec1.base.id = 1; + chm->sec1.content = NULL; + chm->sec1.control = NULL; + chm->sec1.spaninfo = NULL; + chm->sec1.rtable = NULL; + + /* read the first header */ + if (sys->read(fh, &buf[0], chmhead_SIZEOF) != chmhead_SIZEOF) { + return MSPACK_ERR_READ; + } + + /* check ITSF signature */ + if (EndGetI32(&buf[chmhead_Signature]) != 0x46535449) { + return MSPACK_ERR_SIGNATURE; + } + + /* check both header GUIDs */ + if (mspack_memcmp(&buf[chmhead_GUID1], &guids[0], 32L) != 0) { + D(("incorrect GUIDs")) + return MSPACK_ERR_SIGNATURE; + } + + chm->version = EndGetI32(&buf[chmhead_Version]); + chm->timestamp = EndGetM32(&buf[chmhead_Timestamp]); + chm->language = EndGetI32(&buf[chmhead_LanguageID]); + if (chm->version > 3) { + sys->message(fh, "WARNING; CHM version > 3"); + } + + /* read the header section table */ + if (sys->read(fh, &buf[0], chmhst3_SIZEOF) != chmhst3_SIZEOF) { + return MSPACK_ERR_READ; + } + + /* chmhst3_OffsetCS0 does not exist in version 1 or 2 CHM files. + * The offset will be corrected later, once HS1 is read. + */ + if (read_off64(&offset, &buf[chmhst_OffsetHS0], sys, fh) || + read_off64(&chm->dir_offset, &buf[chmhst_OffsetHS1], sys, fh) || + read_off64(&chm->sec0.offset, &buf[chmhst3_OffsetCS0], sys, fh)) + { + return MSPACK_ERR_DATAFORMAT; + } + + /* seek to header section 0 */ + if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { + return MSPACK_ERR_SEEK; + } + + /* read header section 0 */ + if (sys->read(fh, &buf[0], chmhs0_SIZEOF) != chmhs0_SIZEOF) { + return MSPACK_ERR_READ; + } + if (read_off64(&chm->length, &buf[chmhs0_FileLen], sys, fh)) { + return MSPACK_ERR_DATAFORMAT; + } + + /* seek to header section 1 */ + if (sys->seek(fh, chm->dir_offset, MSPACK_SYS_SEEK_START)) { + return MSPACK_ERR_SEEK; + } + + /* read header section 1 */ + if (sys->read(fh, &buf[0], chmhs1_SIZEOF) != chmhs1_SIZEOF) { + return MSPACK_ERR_READ; + } + + chm->dir_offset = sys->tell(fh); + chm->chunk_size = EndGetI32(&buf[chmhs1_ChunkSize]); + chm->density = EndGetI32(&buf[chmhs1_Density]); + chm->depth = EndGetI32(&buf[chmhs1_Depth]); + chm->index_root = EndGetI32(&buf[chmhs1_IndexRoot]); + chm->num_chunks = EndGetI32(&buf[chmhs1_NumChunks]); + chm->first_pmgl = EndGetI32(&buf[chmhs1_FirstPMGL]); + chm->last_pmgl = EndGetI32(&buf[chmhs1_LastPMGL]); + + if (chm->version < 3) { + /* versions before 3 don't have chmhst3_OffsetCS0 */ + chm->sec0.offset = chm->dir_offset + (chm->chunk_size * chm->num_chunks); + } + + /* ensure chunk size is large enough for signature and num_entries */ + if (chm->chunk_size < (pmgl_Entries + 2)) { + return MSPACK_ERR_DATAFORMAT; + } + + /* if we are doing a quick read, stop here! */ + if (!entire) { + return MSPACK_ERR_OK; + } + + /* seek to the first PMGL chunk, and reduce the number of chunks to read */ + if ((x = chm->first_pmgl) != 0) { + if (sys->seek(fh,(off_t) (x * chm->chunk_size), MSPACK_SYS_SEEK_CUR)) { + return MSPACK_ERR_SEEK; + } + } + num_chunks = chm->last_pmgl - x + 1; + + if (!(chunk = (unsigned char *) sys->alloc(sys, (size_t)chm->chunk_size))) { + return MSPACK_ERR_NOMEMORY; + } + + /* read and process all chunks from FirstPMGL to LastPMGL */ + errors = 0; + while (num_chunks--) { + /* read next chunk */ + if (sys->read(fh, chunk, (int)chm->chunk_size) != (int)chm->chunk_size) { + sys->free(chunk); + return MSPACK_ERR_READ; + } + + /* process only directory (PMGL) chunks */ + if (EndGetI32(&chunk[pmgl_Signature]) != 0x4C474D50) continue; + + if (EndGetI32(&chunk[pmgl_QuickRefSize]) < 2) { + sys->message(fh, "WARNING; PMGL quickref area is too small"); + } + if (EndGetI32(&chunk[pmgl_QuickRefSize]) > + ((int)chm->chunk_size - pmgl_Entries)) + { + sys->message(fh, "WARNING; PMGL quickref area is too large"); + } + + p = &chunk[pmgl_Entries]; + end = &chunk[chm->chunk_size - 2]; + num_entries = EndGetI16(end); + + while (num_entries--) { + READ_ENCINT(name_len); name = p; p += name_len; + READ_ENCINT(section); + READ_ENCINT(offset); + READ_ENCINT(length); + + /* empty files and directory names are stored as a file entry at + * offset 0 with length 0. We want to keep empty files, but not + * directory names, which end with a "/" */ + if ((offset == 0) && (length == 0)) { + if ((name_len > 0) && (name[name_len-1] == '/')) continue; + } + + if (section > 1) { + sys->message(fh, "invalid section number '%u'.", section); + continue; + } + + if (!(fi = (struct mschmd_file *) sys->alloc(sys, sizeof(struct mschmd_file) + name_len + 1))) { + sys->free(chunk); + return MSPACK_ERR_NOMEMORY; + } + + fi->next = NULL; + fi->filename = (char *) &fi[1]; + fi->section = ((section == 0) ? (struct mschmd_section *) (&chm->sec0) + : (struct mschmd_section *) (&chm->sec1)); + fi->offset = offset; + fi->length = length; + sys->copy(name, fi->filename, (size_t) name_len); + fi->filename[name_len] = '\0'; + + if (name[0] == ':' && name[1] == ':') { + /* system file */ + if (mspack_memcmp(&name[2], &content_name[2], 31L) == 0) { + if (mspack_memcmp(&name[33], &content_name[33], 8L) == 0) { + chm->sec1.content = fi; + } + else if (mspack_memcmp(&name[33], &control_name[33], 11L) == 0) { + chm->sec1.control = fi; + } + else if (mspack_memcmp(&name[33], &spaninfo_name[33], 8L) == 0) { + chm->sec1.spaninfo = fi; + } + else if (mspack_memcmp(&name[33], &rtable_name[33], 72L) == 0) { + chm->sec1.rtable = fi; + } + } + fi->next = chm->sysfiles; + chm->sysfiles = fi; + } + else { + /* normal file */ + if (link) link->next = fi; else chm->files = fi; + link = fi; + } + } + + /* this is reached either when num_entries runs out, or if + * reading data from the chunk reached a premature end of chunk */ + chunk_end: + if (num_entries >= 0) { + D(("chunk ended before all entries could be read")) + errors++; + } + + } + sys->free(chunk); + return (errors > 0) ? MSPACK_ERR_DATAFORMAT : MSPACK_ERR_OK; +} + +/*************************************** + * CHMD_FAST_FIND + *************************************** + * uses PMGI index chunks and quickref data to quickly locate a file + * directly from the on-disk index. + * + * TODO: protect against infinite loops in chunks (where pgml_NextChunk + * or a PGMI index entry point to an already visited chunk) + */ +static int chmd_fast_find(struct mschm_decompressor *base, + struct mschmd_header *chm, const char *filename, + struct mschmd_file *f_ptr, int f_size) +{ + struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; + struct mspack_system *sys; + struct mspack_file *fh; + const unsigned char *chunk, *p, *end; + int err = MSPACK_ERR_OK, result = -1; + unsigned int n, sec; + + if (!self || !chm || !f_ptr || (f_size != sizeof(struct mschmd_file))) { + return MSPACK_ERR_ARGS; + } + sys = self->system; + + /* clear the results structure */ + memset(f_ptr, 0, f_size); + + if (!(fh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ))) { + return MSPACK_ERR_OPEN; + } + + /* go through PMGI chunk hierarchy to reach PMGL chunk */ + if (chm->index_root < chm->num_chunks) { + n = chm->index_root; + for (;;) { + if (!(chunk = read_chunk(self, chm, fh, n))) { + sys->close(fh); + return self->error; + } + + /* search PMGI/PMGL chunk. exit early if no entry found */ + if ((result = search_chunk(chm, chunk, filename, &p, &end)) <= 0) { + break; + } + + /* found result. loop around for next chunk if this is PMGI */ + if (chunk[3] == 0x4C) break; else READ_ENCINT(n); + } + } + else { + /* PMGL chunks only, search from first_pmgl to last_pmgl */ + for (n = chm->first_pmgl; n <= chm->last_pmgl; + n = EndGetI32(&chunk[pmgl_NextChunk])) + { + if (!(chunk = read_chunk(self, chm, fh, n))) { + err = self->error; + break; + } + + /* search PMGL chunk. exit if file found */ + if ((result = search_chunk(chm, chunk, filename, &p, &end)) > 0) { + break; + } + } + } + + /* if we found a file, read it */ + if (result > 0) { + READ_ENCINT(sec); + f_ptr->section = (sec == 0) ? (struct mschmd_section *) &chm->sec0 + : (struct mschmd_section *) &chm->sec1; + READ_ENCINT(f_ptr->offset); + READ_ENCINT(f_ptr->length); + } + else if (result < 0) { + err = MSPACK_ERR_DATAFORMAT; + } + + sys->close(fh); + return self->error = err; + + chunk_end: + D(("read beyond end of chunk entries")) + sys->close(fh); + return self->error = MSPACK_ERR_DATAFORMAT; +} + +/* reads the given chunk into memory, storing it in a chunk cache + * so it doesn't need to be read from disk more than once + */ +static unsigned char *read_chunk(struct mschm_decompressor_p *self, + struct mschmd_header *chm, + struct mspack_file *fh, + unsigned int chunk_num) +{ + struct mspack_system *sys = self->system; + unsigned char *buf; + + /* check arguments - most are already checked by chmd_fast_find */ + if (chunk_num > chm->num_chunks) return NULL; + + /* ensure chunk cache is available */ + if (!chm->chunk_cache) { + size_t size = sizeof(unsigned char *) * chm->num_chunks; + if (!(chm->chunk_cache = (unsigned char **) sys->alloc(sys, size))) { + self->error = MSPACK_ERR_NOMEMORY; + return NULL; + } + memset(chm->chunk_cache, 0, size); + } + + /* try to answer out of chunk cache */ + if (chm->chunk_cache[chunk_num]) return chm->chunk_cache[chunk_num]; + + /* need to read chunk - allocate memory for it */ + if (!(buf = (unsigned char *) sys->alloc(sys, chm->chunk_size))) { + self->error = MSPACK_ERR_NOMEMORY; + return NULL; + } + + /* seek to block and read it */ + if (sys->seek(fh, (off_t) (chm->dir_offset + (chunk_num * chm->chunk_size)), + MSPACK_SYS_SEEK_START)) + { + self->error = MSPACK_ERR_SEEK; + sys->free(buf); + return NULL; + } + if (sys->read(fh, buf, (int)chm->chunk_size) != (int)chm->chunk_size) { + self->error = MSPACK_ERR_READ; + sys->free(buf); + return NULL; + } + + /* check the signature. Is is PMGL or PMGI? */ + if (!((buf[0] == 0x50) && (buf[1] == 0x4D) && (buf[2] == 0x47) && + ((buf[3] == 0x4C) || (buf[3] == 0x49)))) + { + self->error = MSPACK_ERR_SEEK; + sys->free(buf); + return NULL; + } + + /* all OK. Store chunk in cache and return it */ + return chm->chunk_cache[chunk_num] = buf; +} + +/* searches a PMGI/PMGL chunk for a given filename entry. Returns -1 on + * data format error, 0 if entry definitely not found, 1 if entry + * found. In the latter case, *result and *result_end are set pointing + * to that entry's data (either the "next chunk" ENCINT for a PMGI or + * the section, offset and length ENCINTs for a PMGL). + * + * In the case of PMGL chunks, the entry has definitely been + * found. In the case of PMGI chunks, the entry which points to the + * chunk that may eventually contain that entry has been found. + */ +static int search_chunk(struct mschmd_header *chm, + const unsigned char *chunk, + const char *filename, + const unsigned char **result, + const unsigned char **result_end) +{ + const unsigned char *start, *end, *p; + unsigned int qr_size, num_entries, qr_entries, qr_density, name_len; + unsigned int L, R, M, sec, fname_len, entries_off, is_pmgl; + int cmp; + + fname_len = strlen(filename); + + /* PMGL chunk or PMGI chunk? (note: read_chunk() has already + * checked the rest of the characters in the chunk signature) */ + if (chunk[3] == 0x4C) { + is_pmgl = 1; + entries_off = pmgl_Entries; + } + else { + is_pmgl = 0; + entries_off = pmgi_Entries; + } + + /* Step 1: binary search first filename of each QR entry + * - target filename == entry + * found file + * - target filename < all entries + * file not found + * - target filename > all entries + * proceed to step 2 using final entry + * - target filename between two searched entries + * proceed to step 2 + */ + qr_size = EndGetI32(&chunk[pmgl_QuickRefSize]); + start = &chunk[chm->chunk_size - 2]; + end = &chunk[chm->chunk_size - qr_size]; + num_entries = EndGetI16(start); + qr_density = 1 + (1 << chm->density); + qr_entries = (num_entries + qr_density-1) / qr_density; + + if (num_entries == 0) { + D(("chunk has no entries")) + return -1; + } + + if (qr_size > chm->chunk_size) { + D(("quickref size > chunk size")) + return -1; + } + + *result_end = end; + + if (((int)qr_entries * 2) > (start - end)) { + D(("WARNING; more quickrefs than quickref space")) + qr_entries = 0; /* but we can live with it */ + } + + if (qr_entries > 0) { + L = 0; + R = qr_entries - 1; + do { + /* pick new midpoint */ + M = (L + R) >> 1; + + /* compare filename with entry QR points to */ + p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; + READ_ENCINT(name_len); + if (p + name_len > end) goto chunk_end; + cmp = compare(filename, (char *)p, fname_len, name_len); + + if (cmp == 0) break; + else if (cmp < 0) { if (M) R = M - 1; else return 0; } + else if (cmp > 0) L = M + 1; + } while (L <= R); + M = (L + R) >> 1; + + if (cmp == 0) { + /* exact match! */ + p += name_len; + *result = p; + return 1; + } + + /* otherwise, read the group of entries for QR entry M */ + p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; + num_entries -= (M * qr_density); + if (num_entries > qr_density) num_entries = qr_density; + } + else { + p = &chunk[entries_off]; + } + + /* Step 2: linear search through the set of entries reached in step 1. + * - filename == any entry + * found entry + * - filename < all entries (PMGI) or any entry (PMGL) + * entry not found, stop now + * - filename > all entries + * entry not found (PMGL) / maybe found (PMGI) + * - + */ + *result = NULL; + while (num_entries-- > 0) { + READ_ENCINT(name_len); + if (p + name_len > end) goto chunk_end; + cmp = compare(filename, (char *)p, fname_len, name_len); + p += name_len; + + if (cmp == 0) { + /* entry found */ + *result = p; + return 1; + } + + if (cmp < 0) { + /* entry not found (PMGL) / maybe found (PMGI) */ + break; + } + + /* read and ignore the rest of this entry */ + if (is_pmgl) { + READ_ENCINT(R); /* skip section */ + READ_ENCINT(R); /* skip offset */ + READ_ENCINT(R); /* skip length */ + } + else { + *result = p; /* store potential final result */ + READ_ENCINT(R); /* skip chunk number */ + } + } + + /* PMGL? not found. PMGI? maybe found */ + return (is_pmgl) ? 0 : (*result ? 1 : 0); + + chunk_end: + D(("reached end of chunk data while searching")) + return -1; +} + +#if HAVE_TOWLOWER +# if HAVE_WCTYPE_H +# include +# endif +# define TOLOWER(x) towlower(x) +#elif HAVE_TOLOWER +# if HAVE_CTYPE_H +# include +# endif +# define TOLOWER(x) tolower(x) +#else +# define TOLOWER(x) (((x)<0||(x)>256)?(x):mspack_tolower_map[(x)]) +/* Map of char -> lowercase char for the first 256 chars. Generated with: + * LC_CTYPE=en_GB.utf-8 perl -Mlocale -le 'print map{ord(lc chr).","} 0..255' + */ +static const unsigned char mspack_tolower_map[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27, + 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52, + 53,54,55,56,57,58,59,60,61,62,63,64,97,98,99,100,101,102,103,104,105,106, + 107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,92,93,94, + 95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114, + 115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133, + 134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152, + 153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171, + 172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190, + 191,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241, + 242,243,244,245,246,215,248,249,250,251,252,253,254,223,224,225,226,227,228, + 229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255 +}; +#endif + +/* decodes a UTF-8 character from s[] into c. Will not read past e. */ +#define GET_UTF8_CHAR(s, e, c) do { \ + unsigned char x = *s++; \ + if (x < 0x80) c = x; \ + else if (x < 0xC0) c = -1; \ + else if (x < 0xE0) { \ + c = (s >= e) ? -1 : ((x & 0x1F) << 6) | (*s++ & 0x3F); \ + } \ + else if (x < 0xF0) { \ + c = (s+2 > e) ? -1 : ((x & 0x0F) << 12) | ((s[0] & 0x3F) << 6) \ + | (s[1] & 0x3F); \ + s += 2; \ + } \ + else if (x < 0xF8) { \ + c = (s+3 > e) ? -1 : ((x & 0x07) << 18) | ((s[0] & 0x3F) << 12) \ + | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F); \ + s += 3; \ + } \ + else if (x < 0xFC) { \ + c = (s+4 > e) ? -1 : ((x & 0x03) << 24) | ((s[0] & 0x3F) << 18) \ + | ((s[1] & 0x3F) << 12)|((s[2] & 0x3F) << 6)|(s[3] & 0x3F); \ + s += 4; \ + } \ + else if (x < 0xFE) { \ + c = (s+5>e)?-1:((x&1)<<30)|((s[0]&0x3F)<<24)|((s[1]&0x3F)<<18)| \ + ((s[2] & 0x3F) << 12) | ((s[3] & 0x3F) << 6)|(s[4] & 0x3F); \ + s += 5; \ + } \ + else c = -1; \ +} while (0) + +/* case-insensitively compares two UTF8 encoded strings. String length for + * both strings must be provided, null bytes are not terminators */ +static inline int compare(const char *s1, const char *s2, int l1, int l2) { + register const unsigned char *p1 = (const unsigned char *) s1; + register const unsigned char *p2 = (const unsigned char *) s2; + register const unsigned char *e1 = p1 + l1, *e2 = p2 + l2; + int c1, c2; + + while (p1 < e1 && p2 < e2) { + GET_UTF8_CHAR(p1, e1, c1); + GET_UTF8_CHAR(p2, e2, c2); + if (c1 == c2) continue; + c1 = TOLOWER(c1); + c2 = TOLOWER(c2); + if (c1 != c2) return c1 - c2; + } + return l1 - l2; +} + + +/*************************************** + * CHMD_EXTRACT + *************************************** + * extracts a file from a CHM helpfile + */ +static int chmd_extract(struct mschm_decompressor *base, + struct mschmd_file *file, const char *filename) +{ + struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; + struct mspack_system *sys; + struct mschmd_header *chm; + struct mspack_file *fh; + off_t bytes; + + if (!self) return MSPACK_ERR_ARGS; + if (!file || !file->section) return self->error = MSPACK_ERR_ARGS; + sys = self->system; + chm = file->section->chm; + + /* create decompression state if it doesn't exist */ + if (!self->d) { + self->d = (struct mschmd_decompress_state *) sys->alloc(sys, sizeof(struct mschmd_decompress_state)); + if (!self->d) return self->error = MSPACK_ERR_NOMEMORY; + self->d->chm = chm; + self->d->offset = 0; + self->d->state = NULL; + self->d->sys = *sys; + self->d->sys.write = &chmd_sys_write; + self->d->infh = NULL; + self->d->outfh = NULL; + } + + /* open input chm file if not open, or the open one is a different chm */ + if (!self->d->infh || (self->d->chm != chm)) { + if (self->d->infh) sys->close(self->d->infh); + if (self->d->state) lzxd_free(self->d->state); + self->d->chm = chm; + self->d->offset = 0; + self->d->state = NULL; + self->d->infh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ); + if (!self->d->infh) return self->error = MSPACK_ERR_OPEN; + } + + /* open file for output */ + if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { + return self->error = MSPACK_ERR_OPEN; + } + + /* if file is empty, simply creating it is enough */ + if (!file->length) { + sys->close(fh); + return self->error = MSPACK_ERR_OK; + } + + self->error = MSPACK_ERR_OK; + + switch (file->section->id) { + case 0: /* Uncompressed section file */ + /* simple seek + copy */ + if (sys->seek(self->d->infh, file->section->chm->sec0.offset + + file->offset, MSPACK_SYS_SEEK_START)) + { + self->error = MSPACK_ERR_SEEK; + } + else { + unsigned char buf[512]; + off_t length = file->length; + while (length > 0) { + int run = sizeof(buf); + if ((off_t)run > length) run = (int)length; + if (sys->read(self->d->infh, &buf[0], run) != run) { + self->error = MSPACK_ERR_READ; + break; + } + if (sys->write(fh, &buf[0], run) != run) { + self->error = MSPACK_ERR_WRITE; + break; + } + length -= run; + } + } + break; + + case 1: /* MSCompressed section file */ + /* (re)initialise compression state if we it is not yet initialised, + * or we have advanced too far and have to backtrack + */ + if (!self->d->state || (file->offset < self->d->offset)) { + if (self->d->state) { + lzxd_free(self->d->state); + self->d->state = NULL; + } + if (chmd_init_decomp(self, file)) break; + } + + /* seek to input data */ + if (sys->seek(self->d->infh, self->d->inoffset, MSPACK_SYS_SEEK_START)) { + self->error = MSPACK_ERR_SEEK; + break; + } + + /* get to correct offset. */ + self->d->outfh = NULL; + if ((bytes = file->offset - self->d->offset)) { + self->error = lzxd_decompress(self->d->state, bytes); + } + + /* if getting to the correct offset was error free, unpack file */ + if (!self->error) { + self->d->outfh = fh; + self->error = lzxd_decompress(self->d->state, file->length); + } + + /* save offset in input source stream, in case there is a section 0 + * file between now and the next section 1 file extracted */ + self->d->inoffset = sys->tell(self->d->infh); + + /* if an LZX error occured, the LZX decompressor is now useless */ + if (self->error) { + if (self->d->state) lzxd_free(self->d->state); + self->d->state = NULL; + } + break; + } + + sys->close(fh); + return self->error; +} + +/*************************************** + * CHMD_SYS_WRITE + *************************************** + * chmd_sys_write is the internal writer function which the decompressor + * uses. If either writes data to disk (self->d->outfh) with the real + * sys->write() function, or does nothing with the data when + * self->d->outfh == NULL. advances self->d->offset. + */ +static int chmd_sys_write(struct mspack_file *file, void *buffer, int bytes) { + struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) file; + self->d->offset += bytes; + if (self->d->outfh) { + return self->system->write(self->d->outfh, buffer, bytes); + } + return bytes; +} + +/*************************************** + * CHMD_INIT_DECOMP + *************************************** + * Initialises the LZX decompressor to decompress the compressed stream, + * from the nearest reset offset and length that is needed for the given + * file. + */ +static int chmd_init_decomp(struct mschm_decompressor_p *self, + struct mschmd_file *file) +{ + int window_size, window_bits, reset_interval, entry, err; + struct mspack_system *sys = self->system; + struct mschmd_sec_mscompressed *sec; + unsigned char *data; + off_t length, offset; + + sec = (struct mschmd_sec_mscompressed *) file->section; + + /* ensure we have a mscompressed content section */ + err = find_sys_file(self, sec, &sec->content, content_name); + if (err) return self->error = err; + + /* ensure we have a ControlData file */ + err = find_sys_file(self, sec, &sec->control, control_name); + if (err) return self->error = err; + + /* read ControlData */ + if (sec->control->length < lzxcd_SIZEOF) { + D(("ControlData file is too short")) + return self->error = MSPACK_ERR_DATAFORMAT; + } + if (!(data = read_sys_file(self, sec->control))) { + D(("can't read mscompressed control data file")) + return self->error; + } + + /* check LZXC signature */ + if (EndGetI32(&data[lzxcd_Signature]) != 0x43585A4C) { + sys->free(data); + return self->error = MSPACK_ERR_SIGNATURE; + } + + /* read reset_interval and window_size and validate version number */ + switch (EndGetI32(&data[lzxcd_Version])) { + case 1: + reset_interval = EndGetI32(&data[lzxcd_ResetInterval]); + window_size = EndGetI32(&data[lzxcd_WindowSize]); + break; + case 2: + reset_interval = EndGetI32(&data[lzxcd_ResetInterval]) * LZX_FRAME_SIZE; + window_size = EndGetI32(&data[lzxcd_WindowSize]) * LZX_FRAME_SIZE; + break; + default: + D(("bad controldata version")) + sys->free(data); + return self->error = MSPACK_ERR_DATAFORMAT; + } + + /* free ControlData */ + sys->free(data); + + /* find window_bits from window_size */ + switch (window_size) { + case 0x008000: window_bits = 15; break; + case 0x010000: window_bits = 16; break; + case 0x020000: window_bits = 17; break; + case 0x040000: window_bits = 18; break; + case 0x080000: window_bits = 19; break; + case 0x100000: window_bits = 20; break; + case 0x200000: window_bits = 21; break; + default: + D(("bad controldata window size")) + return self->error = MSPACK_ERR_DATAFORMAT; + } + + /* validate reset_interval */ + if (reset_interval % LZX_FRAME_SIZE) { + D(("bad controldata reset interval")) + return self->error = MSPACK_ERR_DATAFORMAT; + } + + /* which reset table entry would we like? */ + entry = file->offset / reset_interval; + /* convert from reset interval multiple (usually 64k) to 32k frames */ + entry *= reset_interval / LZX_FRAME_SIZE; + + /* read the reset table entry */ + if (read_reset_table(self, sec, entry, &length, &offset)) { + /* the uncompressed length given in the reset table is dishonest. + * the uncompressed data is always padded out from the given + * uncompressed length up to the next reset interval */ + length += reset_interval - 1; + length &= -reset_interval; + } + else { + /* if we can't read the reset table entry, just start from + * the beginning. Use spaninfo to get the uncompressed length */ + entry = 0; + offset = 0; + err = read_spaninfo(self, sec, &length); + } + if (err) return self->error = err; + + /* get offset of compressed data stream: + * = offset of uncompressed section from start of file + * + offset of compressed stream from start of uncompressed section + * + offset of chosen reset interval from start of compressed stream */ + self->d->inoffset = file->section->chm->sec0.offset + sec->content->offset + offset; + + /* set start offset and overall remaining stream length */ + self->d->offset = entry * LZX_FRAME_SIZE; + length -= self->d->offset; + + /* initialise LZX stream */ + self->d->state = lzxd_init(&self->d->sys, self->d->infh, + (struct mspack_file *) self, window_bits, + reset_interval / LZX_FRAME_SIZE, + 4096, length); + if (!self->d->state) self->error = MSPACK_ERR_NOMEMORY; + return self->error; +} + +/*************************************** + * READ_RESET_TABLE + *************************************** + * Reads one entry out of the reset table. Also reads the uncompressed + * data length. Writes these to offset_ptr and length_ptr respectively. + * Returns non-zero for success, zero for failure. + */ +static int read_reset_table(struct mschm_decompressor_p *self, + struct mschmd_sec_mscompressed *sec, + int entry, off_t *length_ptr, off_t *offset_ptr) +{ + struct mspack_system *sys = self->system; + unsigned char *data; + int pos, entrysize; + + /* do we have a ResetTable file? */ + int err = find_sys_file(self, sec, &sec->rtable, rtable_name); + if (err) return 0; + + /* read ResetTable file */ + if (sec->rtable->length < lzxrt_headerSIZEOF) { + D(("ResetTable file is too short")) + return 0; + } + if (!(data = read_sys_file(self, sec->rtable))) { + D(("can't read reset table")) + return 0; + } + + /* check sanity of reset table */ + if (EndGetI32(&data[lzxrt_FrameLen]) != LZX_FRAME_SIZE) { + D(("bad reset table frame length")) + sys->free(data); + return 0; + } + + /* get the uncompressed length of the LZX stream */ + if (read_off64(length_ptr, data, sys, self->d->infh)) { + sys->free(data); + return 0; + } + + entrysize = EndGetI32(&data[lzxrt_EntrySize]); + pos = EndGetI32(&data[lzxrt_TableOffset]) + (entry * entrysize); + + /* ensure reset table entry for this offset exists */ + if (entry < EndGetI32(&data[lzxrt_NumEntries]) && + ((pos + entrysize) <= sec->rtable->length)) + { + switch (entrysize) { + case 4: + *offset_ptr = EndGetI32(&data[pos]); + err = 0; + break; + case 8: + err = read_off64(offset_ptr, &data[pos], sys, self->d->infh); + break; + default: + D(("reset table entry size neither 4 nor 8")) + err = 1; + break; + } + } + else { + D(("bad reset interval")) + err = 1; + } + + /* free the reset table */ + sys->free(data); + + /* return success */ + return (err == 0); +} + +/*************************************** + * READ_SPANINFO + *************************************** + * Reads the uncompressed data length from the spaninfo file. + * Returns zero for success or a non-zero error code for failure. + */ +static int read_spaninfo(struct mschm_decompressor_p *self, + struct mschmd_sec_mscompressed *sec, + off_t *length_ptr) +{ + struct mspack_system *sys = self->system; + unsigned char *data; + + /* find SpanInfo file */ + int err = find_sys_file(self, sec, &sec->spaninfo, spaninfo_name); + if (err) return MSPACK_ERR_DATAFORMAT; + + /* check it's large enough */ + if (sec->spaninfo->length != 8) { + D(("SpanInfo file is wrong size")) + return MSPACK_ERR_DATAFORMAT; + } + + /* read the SpanInfo file */ + if (!(data = read_sys_file(self, sec->spaninfo))) { + D(("can't read SpanInfo file")) + return self->error; + } + + /* get the uncompressed length of the LZX stream */ + err = read_off64(length_ptr, data, sys, self->d->infh); + + sys->free(data); + return (err) ? MSPACK_ERR_DATAFORMAT : MSPACK_ERR_OK; +} + +/*************************************** + * FIND_SYS_FILE + *************************************** + * Uses chmd_fast_find to locate a system file, and fills out that system + * file's entry and links it into the list of system files. Returns zero + * for success, non-zero for both failure and the file not existing. + */ +static int find_sys_file(struct mschm_decompressor_p *self, + struct mschmd_sec_mscompressed *sec, + struct mschmd_file **f_ptr, const char *name) +{ + struct mspack_system *sys = self->system; + struct mschmd_file result; + + /* already loaded */ + if (*f_ptr) return MSPACK_ERR_OK; + + /* try using fast_find to find the file - return DATAFORMAT error if + * it fails, or successfully doesn't find the file */ + if (chmd_fast_find((struct mschm_decompressor *) self, sec->base.chm, + name, &result, (int)sizeof(result)) || !result.section) + { + return MSPACK_ERR_DATAFORMAT; + } + + if (!(*f_ptr = (struct mschmd_file *) sys->alloc(sys, sizeof(result)))) { + return MSPACK_ERR_NOMEMORY; + } + + /* copy result */ + *(*f_ptr) = result; + (*f_ptr)->filename = (char *) name; + + /* link file into sysfiles list */ + (*f_ptr)->next = sec->base.chm->sysfiles; + sec->base.chm->sysfiles = *f_ptr; + return MSPACK_ERR_OK; +} + +/*************************************** + * READ_SYS_FILE + *************************************** + * Allocates memory for a section 0 (uncompressed) file and reads it into + * memory. + */ +static unsigned char *read_sys_file(struct mschm_decompressor_p *self, + struct mschmd_file *file) +{ + struct mspack_system *sys = self->system; + unsigned char *data = NULL; + int len; + + if (!file || !file->section || (file->section->id != 0)) { + self->error = MSPACK_ERR_DATAFORMAT; + return NULL; + } + + len = (int) file->length; + + if (!(data = (unsigned char *) sys->alloc(sys, (size_t) len))) { + self->error = MSPACK_ERR_NOMEMORY; + return NULL; + } + if (sys->seek(self->d->infh, file->section->chm->sec0.offset + + file->offset, MSPACK_SYS_SEEK_START)) + { + self->error = MSPACK_ERR_SEEK; + sys->free(data); + return NULL; + } + if (sys->read(self->d->infh, data, len) != len) { + self->error = MSPACK_ERR_READ; + sys->free(data); + return NULL; + } + return data; +} + +/*************************************** + * CHMD_ERROR + *************************************** + * returns the last error that occurred + */ +static int chmd_error(struct mschm_decompressor *base) { + struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; + return (self) ? self->error : MSPACK_ERR_ARGS; +} + +/*************************************** + * READ_OFF64 + *************************************** + * Reads a 64-bit signed integer from memory in Intel byte order. + * If running on a system with a 64-bit off_t, this is simply done. + * If running on a system with a 32-bit off_t, offsets up to 0x7FFFFFFF + * are accepted, offsets beyond that cause an error message. + */ +static int read_off64(off_t *var, unsigned char *mem, + struct mspack_system *sys, struct mspack_file *fh) +{ +#ifdef LARGEFILE_SUPPORT + *var = EndGetI64(mem); +#else + *var = EndGetI32(mem); + if ((*var & 0x80000000) || EndGetI32(mem+4)) { + sys->message(fh, (char *)largefile_msg); + return 1; + } +#endif + return 0; +} diff --git a/rbutil/rbutilqt/mspack/des.h b/rbutil/rbutilqt/mspack/des.h new file mode 100644 index 0000000000..64a1ed27e4 --- /dev/null +++ b/rbutil/rbutilqt/mspack/des.h @@ -0,0 +1,15 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_DES_H +#define MSPACK_DES_H 1 + +/* DES encryption / decryption definitions */ + +#endif diff --git a/rbutil/rbutilqt/mspack/hlp.h b/rbutil/rbutilqt/mspack/hlp.h new file mode 100644 index 0000000000..b7486fa160 --- /dev/null +++ b/rbutil/rbutilqt/mspack/hlp.h @@ -0,0 +1,33 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_HLP_H +#define MSPACK_HLP_H 1 + +#include + +/* generic HLP definitions */ + +/* HLP compression definitions */ + +struct mshlp_compressor_p { + struct mshlp_compressor base; + struct mspack_system *system; + /* todo */ +}; + +/* HLP decompression definitions */ + +struct mshlp_decompressor_p { + struct mshlp_decompressor base; + struct mspack_system *system; + /* todo */ +}; + +#endif diff --git a/rbutil/rbutilqt/mspack/hlpc.c b/rbutil/rbutilqt/mspack/hlpc.c new file mode 100644 index 0000000000..60eabfe207 --- /dev/null +++ b/rbutil/rbutilqt/mspack/hlpc.c @@ -0,0 +1,24 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* HLP compression implementation */ + +#include +#include + +struct mshlp_compressor * + mspack_create_hlp_compressor(struct mspack_system *sys) +{ + /* todo */ + return NULL; +} + +void mspack_destroy_hlp_compressor(struct mshlp_compressor *self) { + /* todo */ +} diff --git a/rbutil/rbutilqt/mspack/hlpd.c b/rbutil/rbutilqt/mspack/hlpd.c new file mode 100644 index 0000000000..43354f008f --- /dev/null +++ b/rbutil/rbutilqt/mspack/hlpd.c @@ -0,0 +1,24 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* HLP decompression implementation */ + +#include +#include + +struct mshlp_decompressor * + mspack_create_hlp_decompressor(struct mspack_system *sys) +{ + /* todo */ + return NULL; +} + +void mspack_destroy_hlp_decompressor(struct mshlp_decompressor *self) { + /* todo */ +} diff --git a/rbutil/rbutilqt/mspack/kwaj.h b/rbutil/rbutilqt/mspack/kwaj.h new file mode 100644 index 0000000000..09673c0779 --- /dev/null +++ b/rbutil/rbutilqt/mspack/kwaj.h @@ -0,0 +1,118 @@ +/* This file is part of libmspack. + * (C) 2003-2010 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_KWAJ_H +#define MSPACK_KWAJ_H 1 + +#include + +/* generic KWAJ definitions */ +#define kwajh_Signature1 (0x00) +#define kwajh_Signature2 (0x04) +#define kwajh_CompMethod (0x08) +#define kwajh_DataOffset (0x0a) +#define kwajh_Flags (0x0c) +#define kwajh_SIZEOF (0x0e) + +/* KWAJ compression definitions */ + +struct mskwaj_compressor_p { + struct mskwaj_compressor base; + struct mspack_system *system; + /* todo */ + int param[2]; /* !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!! */ + int error; +}; + +/* KWAJ decompression definitions */ + +struct mskwaj_decompressor_p { + struct mskwaj_decompressor base; + struct mspack_system *system; + int error; +}; + +struct mskwajd_header_p { + struct mskwajd_header base; + struct mspack_file *fh; +}; + +/* input buffer size during decompression - not worth parameterising IMHO */ +#define KWAJ_INPUT_SIZE (2048) + +/* huffman codes that are 9 bits or less are decoded immediately */ +#define KWAJ_TABLEBITS (9) + +/* number of codes in each huffman table */ +#define KWAJ_MATCHLEN1_SYMS (16) +#define KWAJ_MATCHLEN2_SYMS (16) +#define KWAJ_LITLEN_SYMS (32) +#define KWAJ_OFFSET_SYMS (64) +#define KWAJ_LITERAL_SYMS (256) + +/* define decoding table sizes */ +#define KWAJ_TABLESIZE (1 << KWAJ_TABLEBITS) +#if KWAJ_TABLESIZE < (KWAJ_MATCHLEN1_SYMS * 2) +# define KWAJ_MATCHLEN1_TBLSIZE (KWAJ_MATCHLEN1_SYMS * 4) +#else +# define KWAJ_MATCHLEN1_TBLSIZE (KWAJ_TABLESIZE + (KWAJ_MATCHLEN1_SYMS * 2)) +#endif +#if KWAJ_TABLESIZE < (KWAJ_MATCHLEN2_SYMS * 2) +# define KWAJ_MATCHLEN2_TBLSIZE (KWAJ_MATCHLEN2_SYMS * 4) +#else +# define KWAJ_MATCHLEN2_TBLSIZE (KWAJ_TABLESIZE + (KWAJ_MATCHLEN2_SYMS * 2)) +#endif +#if KWAJ_TABLESIZE < (KWAJ_LITLEN_SYMS * 2) +# define KWAJ_LITLEN_TBLSIZE (KWAJ_LITLEN_SYMS * 4) +#else +# define KWAJ_LITLEN_TBLSIZE (KWAJ_TABLESIZE + (KWAJ_LITLEN_SYMS * 2)) +#endif +#if KWAJ_TABLESIZE < (KWAJ_OFFSET_SYMS * 2) +# define KWAJ_OFFSET_TBLSIZE (KWAJ_OFFSET_SYMS * 4) +#else +# define KWAJ_OFFSET_TBLSIZE (KWAJ_TABLESIZE + (KWAJ_OFFSET_SYMS * 2)) +#endif +#if KWAJ_TABLESIZE < (KWAJ_LITERAL_SYMS * 2) +# define KWAJ_LITERAL_TBLSIZE (KWAJ_LITERAL_SYMS * 4) +#else +# define KWAJ_LITERAL_TBLSIZE (KWAJ_TABLESIZE + (KWAJ_LITERAL_SYMS * 2)) +#endif + +struct kwajd_stream { + /* I/O buffering */ + struct mspack_system *sys; + struct mspack_file *input; + struct mspack_file *output; + unsigned char *i_ptr, *i_end; + unsigned int bit_buffer, bits_left; + int input_end; + + /* huffman code lengths */ + unsigned char MATCHLEN1_len [KWAJ_MATCHLEN1_SYMS]; + unsigned char MATCHLEN2_len [KWAJ_MATCHLEN2_SYMS]; + unsigned char LITLEN_len [KWAJ_LITLEN_SYMS]; + unsigned char OFFSET_len [KWAJ_OFFSET_SYMS]; + unsigned char LITERAL_len [KWAJ_LITERAL_SYMS]; + + /* huffman decoding tables */ + unsigned short MATCHLEN1_table [KWAJ_MATCHLEN1_TBLSIZE]; + unsigned short MATCHLEN2_table [KWAJ_MATCHLEN2_TBLSIZE]; + unsigned short LITLEN_table [KWAJ_LITLEN_TBLSIZE]; + unsigned short OFFSET_table [KWAJ_OFFSET_TBLSIZE]; + unsigned short LITERAL_table [KWAJ_LITERAL_TBLSIZE]; + + /* input buffer */ + unsigned char inbuf[KWAJ_INPUT_SIZE]; + + /* history window */ + unsigned char window[LZSS_WINDOW_SIZE]; +}; + + +#endif diff --git a/rbutil/rbutilqt/mspack/kwajc.c b/rbutil/rbutilqt/mspack/kwajc.c new file mode 100644 index 0000000000..b88ed7690a --- /dev/null +++ b/rbutil/rbutilqt/mspack/kwajc.c @@ -0,0 +1,24 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* KWAJ compression implementation */ + +#include +#include + +struct mskwaj_compressor * + mspack_create_kwaj_compressor(struct mspack_system *sys) +{ + /* todo */ + return NULL; +} + +void mspack_destroy_kwaj_compressor(struct mskwaj_compressor *self) { + /* todo */ +} diff --git a/rbutil/rbutilqt/mspack/kwajd.c b/rbutil/rbutilqt/mspack/kwajd.c new file mode 100644 index 0000000000..d891b6a7e3 --- /dev/null +++ b/rbutil/rbutilqt/mspack/kwajd.c @@ -0,0 +1,555 @@ +/* This file is part of libmspack. + * (C) 2003-2010 Stuart Caie. + * + * KWAJ is a format very similar to SZDD. KWAJ method 3 (LZH) was + * written by Jeff Johnson. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* KWAJ decompression implementation */ + +#include +#include + +/* prototypes */ +static struct mskwajd_header *kwajd_open( + struct mskwaj_decompressor *base, const char *filename); +static void kwajd_close( + struct mskwaj_decompressor *base, struct mskwajd_header *hdr); +static int kwajd_read_headers( + struct mspack_system *sys, struct mspack_file *fh, + struct mskwajd_header *hdr); +static int kwajd_extract( + struct mskwaj_decompressor *base, struct mskwajd_header *hdr, + const char *filename); +static int kwajd_decompress( + struct mskwaj_decompressor *base, const char *input, const char *output); +static int kwajd_error( + struct mskwaj_decompressor *base); + +static struct kwajd_stream *lzh_init( + struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out); +static int lzh_decompress( + struct kwajd_stream *kwaj); +static void lzh_free( + struct kwajd_stream *kwaj); +static int lzh_read_lens( + struct kwajd_stream *kwaj, + unsigned int type, unsigned int numsyms, + unsigned char *lens, unsigned short *table); +static int lzh_read_input( + struct kwajd_stream *kwaj); + + +/*************************************** + * MSPACK_CREATE_KWAJ_DECOMPRESSOR + *************************************** + * constructor + */ +struct mskwaj_decompressor * + mspack_create_kwaj_decompressor(struct mspack_system *sys) +{ + struct mskwaj_decompressor_p *self = NULL; + + if (!sys) sys = mspack_default_system; + if (!mspack_valid_system(sys)) return NULL; + + if ((self = (struct mskwaj_decompressor_p *) sys->alloc(sys, sizeof(struct mskwaj_decompressor_p)))) { + self->base.open = &kwajd_open; + self->base.close = &kwajd_close; + self->base.extract = &kwajd_extract; + self->base.decompress = &kwajd_decompress; + self->base.last_error = &kwajd_error; + self->system = sys; + self->error = MSPACK_ERR_OK; + } + return (struct mskwaj_decompressor *) self; +} + +/*************************************** + * MSPACK_DESTROY_KWAJ_DECOMPRESSOR + *************************************** + * destructor + */ +void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *base) +{ + struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; + if (self) { + struct mspack_system *sys = self->system; + sys->free(self); + } +} + +/*************************************** + * KWAJD_OPEN + *************************************** + * opens a KWAJ file without decompressing, reads header + */ +static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base, + const char *filename) +{ + struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; + struct mskwajd_header *hdr; + struct mspack_system *sys; + struct mspack_file *fh; + + if (!self) return NULL; + sys = self->system; + + fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ); + hdr = (struct mskwajd_header *) sys->alloc(sys, sizeof(struct mskwajd_header_p)); + if (fh && hdr) { + ((struct mskwajd_header_p *) hdr)->fh = fh; + self->error = kwajd_read_headers(sys, fh, hdr); + } + else { + if (!fh) self->error = MSPACK_ERR_OPEN; + if (!hdr) self->error = MSPACK_ERR_NOMEMORY; + } + + if (self->error) { + if (fh) sys->close(fh); + if (hdr) sys->free(hdr); + hdr = NULL; + } + + return hdr; +} + +/*************************************** + * KWAJD_CLOSE + *************************************** + * closes a KWAJ file + */ +static void kwajd_close(struct mskwaj_decompressor *base, + struct mskwajd_header *hdr) +{ + struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; + struct mskwajd_header_p *hdr_p = (struct mskwajd_header_p *) hdr; + + if (!self || !self->system) return; + + /* close the file handle associated */ + self->system->close(hdr_p->fh); + + /* free the memory associated */ + self->system->free(hdr); + + self->error = MSPACK_ERR_OK; +} + +/*************************************** + * KWAJD_READ_HEADERS + *************************************** + * reads the headers of a KWAJ format file + */ +static int kwajd_read_headers(struct mspack_system *sys, + struct mspack_file *fh, + struct mskwajd_header *hdr) +{ + unsigned char buf[16]; + int i; + + /* read in the header */ + if (sys->read(fh, &buf[0], kwajh_SIZEOF) != kwajh_SIZEOF) { + return MSPACK_ERR_READ; + } + + /* check for "KWAJ" signature */ + if (((unsigned int) EndGetI32(&buf[kwajh_Signature1]) != 0x4A41574B) || + ((unsigned int) EndGetI32(&buf[kwajh_Signature2]) != 0xD127F088)) + { + return MSPACK_ERR_SIGNATURE; + } + + /* basic header fields */ + hdr->comp_type = EndGetI16(&buf[kwajh_CompMethod]); + hdr->data_offset = EndGetI16(&buf[kwajh_DataOffset]); + hdr->headers = EndGetI16(&buf[kwajh_Flags]); + hdr->length = 0; + hdr->filename = NULL; + hdr->extra = NULL; + hdr->extra_length = 0; + + /* optional headers */ + + /* 4 bytes: length of unpacked file */ + if (hdr->headers & MSKWAJ_HDR_HASLENGTH) { + if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ; + hdr->length = EndGetI32(&buf[0]); + } + + /* 2 bytes: unknown purpose */ + if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN1) { + if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; + } + + /* 2 bytes: length of section, then [length] bytes: unknown purpose */ + if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN2) { + if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; + i = EndGetI16(&buf[0]); + if (sys->seek(fh, (off_t)i, MSPACK_SYS_SEEK_CUR)) return MSPACK_ERR_SEEK; + } + + /* filename and extension */ + if (hdr->headers & (MSKWAJ_HDR_HASFILENAME | MSKWAJ_HDR_HASFILEEXT)) { + off_t pos = sys->tell(fh); + char *fn = (char *) sys->alloc(sys, (size_t) 13); + + /* allocate memory for maximum length filename */ + if (! fn) return MSPACK_ERR_NOMEMORY; + hdr->filename = fn; + + /* copy filename if present */ + if (hdr->headers & MSKWAJ_HDR_HASFILENAME) { + if (sys->read(fh, &buf[0], 9) != 9) return MSPACK_ERR_READ; + for (i = 0; i < 9; i++, fn++) if (!(*fn = buf[i])) break; + pos += (i < 9) ? i+1 : 9; + if (sys->seek(fh, pos, MSPACK_SYS_SEEK_START)) + return MSPACK_ERR_SEEK; + } + + /* copy extension if present */ + if (hdr->headers & MSKWAJ_HDR_HASFILEEXT) { + *fn++ = '.'; + if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ; + for (i = 0; i < 4; i++, fn++) if (!(*fn = buf[i])) break; + pos += (i < 4) ? i+1 : 4; + if (sys->seek(fh, pos, MSPACK_SYS_SEEK_START)) + return MSPACK_ERR_SEEK; + } + *fn = '\0'; + } + + /* 2 bytes: extra text length then [length] bytes of extra text data */ + if (hdr->headers & MSKWAJ_HDR_HASEXTRATEXT) { + if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; + i = EndGetI16(&buf[0]); + hdr->extra = (char *) sys->alloc(sys, (size_t)i+1); + if (! hdr->extra) return MSPACK_ERR_NOMEMORY; + if (sys->read(fh, hdr->extra, i) != i) return MSPACK_ERR_READ; + hdr->extra[i] = '\0'; + hdr->extra_length = i; + } + return MSPACK_ERR_OK; +} + +/*************************************** + * KWAJD_EXTRACT + *************************************** + * decompresses a KWAJ file + */ +static int kwajd_extract(struct mskwaj_decompressor *base, + struct mskwajd_header *hdr, const char *filename) +{ + struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; + struct mspack_system *sys; + struct mspack_file *fh, *outfh; + + if (!self) return MSPACK_ERR_ARGS; + if (!hdr) return self->error = MSPACK_ERR_ARGS; + + sys = self->system; + fh = ((struct mskwajd_header_p *) hdr)->fh; + + /* seek to the compressed data */ + if (sys->seek(fh, hdr->data_offset, MSPACK_SYS_SEEK_START)) { + return self->error = MSPACK_ERR_SEEK; + } + + /* open file for output */ + if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { + return self->error = MSPACK_ERR_OPEN; + } + + self->error = MSPACK_ERR_OK; + + /* decompress based on format */ + if (hdr->comp_type == MSKWAJ_COMP_NONE || + hdr->comp_type == MSKWAJ_COMP_XOR) + { + /* NONE is a straight copy. XOR is a copy xored with 0xFF */ + unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) KWAJ_INPUT_SIZE); + if (buf) { + int read, i; + while ((read = sys->read(fh, buf, KWAJ_INPUT_SIZE)) > 0) { + if (hdr->comp_type == MSKWAJ_COMP_XOR) { + for (i = 0; i < read; i++) buf[i] ^= 0xFF; + } + if (sys->write(outfh, buf, read) != read) { + self->error = MSPACK_ERR_WRITE; + break; + } + } + if (read < 0) self->error = MSPACK_ERR_READ; + sys->free(buf); + } + else { + self->error = MSPACK_ERR_NOMEMORY; + } + } + else if (hdr->comp_type == MSKWAJ_COMP_SZDD) { + self->error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE, + LZSS_MODE_EXPAND); + } + else if (hdr->comp_type == MSKWAJ_COMP_LZH) { + struct kwajd_stream *lzh = lzh_init(sys, fh, outfh); + self->error = (lzh) ? lzh_decompress(lzh) : MSPACK_ERR_NOMEMORY; + lzh_free(lzh); + } + else { + self->error = MSPACK_ERR_DATAFORMAT; + } + + /* close output file */ + sys->close(outfh); + + return self->error; +} + +/*************************************** + * KWAJD_DECOMPRESS + *************************************** + * unpacks directly from input to output + */ +static int kwajd_decompress(struct mskwaj_decompressor *base, + const char *input, const char *output) +{ + struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; + struct mskwajd_header *hdr; + int error; + + if (!self) return MSPACK_ERR_ARGS; + + if (!(hdr = kwajd_open(base, input))) return self->error; + error = kwajd_extract(base, hdr, output); + kwajd_close(base, hdr); + return self->error = error; +} + +/*************************************** + * KWAJD_ERROR + *************************************** + * returns the last error that occurred + */ +static int kwajd_error(struct mskwaj_decompressor *base) +{ + struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; + return (self) ? self->error : MSPACK_ERR_ARGS; +} + +/*************************************** + * LZH_INIT, LZH_DECOMPRESS, LZH_FREE + *************************************** + * unpacks KWAJ method 3 files + */ + +/* import bit-reading macros and code */ +#define BITS_TYPE struct kwajd_stream +#define BITS_VAR lzh +#define BITS_ORDER_MSB +#define BITS_NO_READ_INPUT +#define READ_BYTES do { \ + if (i_ptr >= i_end) { \ + if ((err = lzh_read_input(lzh))) return err; \ + i_ptr = lzh->i_ptr; \ + i_end = lzh->i_end; \ + } \ + INJECT_BITS(*i_ptr++, 8); \ +} while (0) +#include + +/* import huffman-reading macros and code */ +#define TABLEBITS(tbl) KWAJ_TABLEBITS +#define MAXSYMBOLS(tbl) KWAJ_##tbl##_SYMS +#define HUFF_TABLE(tbl,idx) lzh->tbl##_table[idx] +#define HUFF_LEN(tbl,idx) lzh->tbl##_len[idx] +#define HUFF_ERROR return MSPACK_ERR_DATAFORMAT +#include + +/* In the KWAJ LZH format, there is no special 'eof' marker, it just + * ends. Depending on how many bits are left in the final byte when + * the stream ends, that might be enough to start another literal or + * match. The only easy way to detect that we've come to an end is to + * guard all bit-reading. We allow fake bits to be read once we reach + * the end of the stream, but we check if we then consumed any of + * those fake bits, after doing the READ_BITS / READ_HUFFSYM. This + * isn't how the default readbits.h read_input() works (it simply lets + * 2 fake bytes in then stops), so we implement our own. + */ +#define READ_BITS_SAFE(val, n) do { \ + READ_BITS(val, n); \ + if (lzh->input_end && bits_left < lzh->input_end) \ + return MSPACK_ERR_OK; \ +} while (0) + +#define READ_HUFFSYM_SAFE(tbl, val) do { \ + READ_HUFFSYM(tbl, val); \ + if (lzh->input_end && bits_left < lzh->input_end) \ + return MSPACK_ERR_OK; \ +} while (0) + +#define BUILD_TREE(tbl, type) \ + STORE_BITS; \ + err = lzh_read_lens(lzh, type, MAXSYMBOLS(tbl), \ + &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0)); \ + if (err) return err; \ + RESTORE_BITS; \ + if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ + &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ + return MSPACK_ERR_DATAFORMAT; + +#define WRITE_BYTE do { \ + if (lzh->sys->write(lzh->output, &lzh->window[pos], 1) != 1) \ + return MSPACK_ERR_WRITE; \ +} while (0) + +static struct kwajd_stream *lzh_init(struct mspack_system *sys, + struct mspack_file *in, struct mspack_file *out) +{ + struct kwajd_stream *lzh; + + if (!sys || !in || !out) return NULL; + if (!(lzh = (struct kwajd_stream *) sys->alloc(sys, sizeof(struct kwajd_stream)))) return NULL; + + lzh->sys = sys; + lzh->input = in; + lzh->output = out; + return lzh; +} + +static int lzh_decompress(struct kwajd_stream *lzh) +{ + register unsigned int bit_buffer; + register int bits_left, i; + register unsigned short sym; + unsigned char *i_ptr, *i_end, lit_run = 0; + int j, pos = 0, len, offset, err; + unsigned int types[6]; + + /* reset global state */ + INIT_BITS; + RESTORE_BITS; + memset(&lzh->window[0], LZSS_WINDOW_FILL, (size_t) LZSS_WINDOW_SIZE); + + /* read 6 encoding types (for byte alignment) but only 5 are needed */ + for (i = 0; i < 6; i++) READ_BITS_SAFE(types[i], 4); + + /* read huffman table symbol lengths and build huffman trees */ + BUILD_TREE(MATCHLEN1, types[0]); + BUILD_TREE(MATCHLEN2, types[1]); + BUILD_TREE(LITLEN, types[2]); + BUILD_TREE(OFFSET, types[3]); + BUILD_TREE(LITERAL, types[4]); + + while (!lzh->input_end) { + if (lit_run) READ_HUFFSYM_SAFE(MATCHLEN2, len); + else READ_HUFFSYM_SAFE(MATCHLEN1, len); + + if (len > 0) { + len += 2; + lit_run = 0; /* not the end of a literal run */ + READ_HUFFSYM_SAFE(OFFSET, j); offset = j << 6; + READ_BITS_SAFE(j, 6); offset |= j; + + /* copy match as output and into the ring buffer */ + while (len-- > 0) { + lzh->window[pos] = lzh->window[(pos+4096-offset) & 4095]; + WRITE_BYTE; + pos++; pos &= 4095; + } + } + else { + READ_HUFFSYM_SAFE(LITLEN, len); len++; + lit_run = (len == 32) ? 0 : 1; /* end of a literal run? */ + while (len-- > 0) { + READ_HUFFSYM_SAFE(LITERAL, j); + /* copy as output and into the ring buffer */ + lzh->window[pos] = j; + WRITE_BYTE; + pos++; pos &= 4095; + } + } + } + return MSPACK_ERR_OK; +} + +static void lzh_free(struct kwajd_stream *lzh) +{ + struct mspack_system *sys; + if (!lzh || !lzh->sys) return; + sys = lzh->sys; + sys->free(lzh); +} + +static int lzh_read_lens(struct kwajd_stream *lzh, + unsigned int type, unsigned int numsyms, + unsigned char *lens, unsigned short *table) +{ + register unsigned int bit_buffer; + register int bits_left; + unsigned char *i_ptr, *i_end; + unsigned int i, c, sel; + int err; + + RESTORE_BITS; + switch (type) { + case 0: + i = numsyms; c = (i==16)?4: (i==32)?5: (i==64)?6: (i==256)?8 :0; + for (i = 0; i < numsyms; i++) lens[i] = c; + break; + + case 1: + READ_BITS_SAFE(c, 4); lens[0] = c; + for (i = 1; i < numsyms; i++) { + READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = c; + else { READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = ++c; + else { READ_BITS_SAFE(c, 4); lens[i] = c; }} + } + break; + + case 2: + READ_BITS_SAFE(c, 4); lens[0] = c; + for (i = 1; i < numsyms; i++) { + READ_BITS_SAFE(sel, 2); + if (sel == 3) READ_BITS_SAFE(c, 4); else c += (char) sel-1; + lens[i] = c; + } + break; + + case 3: + for (i = 0; i < numsyms; i++) { + READ_BITS_SAFE(c, 4); lens[i] = c; + } + break; + } + STORE_BITS; + return MSPACK_ERR_OK; +} + +static int lzh_read_input(struct kwajd_stream *lzh) { + int read; + if (lzh->input_end) { + lzh->input_end += 8; + lzh->inbuf[0] = 0; + read = 1; + } + else { + read = lzh->sys->read(lzh->input, &lzh->inbuf[0], KWAJ_INPUT_SIZE); + if (read < 0) return MSPACK_ERR_READ; + if (read == 0) { + lzh->input_end = 8; + lzh->inbuf[0] = 0; + read = 1; + } + } + + /* update i_ptr and i_end */ + lzh->i_ptr = &lzh->inbuf[0]; + lzh->i_end = &lzh->inbuf[read]; + return MSPACK_ERR_OK; +} diff --git a/rbutil/rbutilqt/mspack/lit.h b/rbutil/rbutilqt/mspack/lit.h new file mode 100644 index 0000000000..79ba44d877 --- /dev/null +++ b/rbutil/rbutilqt/mspack/lit.h @@ -0,0 +1,35 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_LIT_H +#define MSPACK_LIT_H 1 + +#include +#include +#include + +/* generic LIT definitions */ + +/* LIT compression definitions */ + +struct mslit_compressor_p { + struct mslit_compressor base; + struct mspack_system *system; + /* todo */ +}; + +/* LIT decompression definitions */ + +struct mslit_decompressor_p { + struct mslit_decompressor base; + struct mspack_system *system; + /* todo */ +}; + +#endif diff --git a/rbutil/rbutilqt/mspack/litc.c b/rbutil/rbutilqt/mspack/litc.c new file mode 100644 index 0000000000..a8a709af07 --- /dev/null +++ b/rbutil/rbutilqt/mspack/litc.c @@ -0,0 +1,24 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* LIT compression implementation */ + +#include +#include + +struct mslit_compressor * + mspack_create_lit_compressor(struct mspack_system *sys) +{ + /* todo */ + return NULL; +} + +void mspack_destroy_lit_compressor(struct mslit_compressor *self) { + /* todo */ +} diff --git a/rbutil/rbutilqt/mspack/litd.c b/rbutil/rbutilqt/mspack/litd.c new file mode 100644 index 0000000000..6e0dc9af27 --- /dev/null +++ b/rbutil/rbutilqt/mspack/litd.c @@ -0,0 +1,24 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* LIT decompression implementation */ + +#include +#include + +struct mslit_decompressor * + mspack_create_lit_decompressor(struct mspack_system *sys) +{ + /* todo */ + return NULL; +} + +void mspack_destroy_lit_decompressor(struct mslit_decompressor *self) { + /* todo */ +} diff --git a/rbutil/rbutilqt/mspack/lzss.h b/rbutil/rbutilqt/mspack/lzss.h new file mode 100644 index 0000000000..55e761b5bf --- /dev/null +++ b/rbutil/rbutilqt/mspack/lzss.h @@ -0,0 +1,66 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_LZSS_H +#define MSPACK_LZSS_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* LZSS compression / decompression definitions */ + +#define LZSS_WINDOW_SIZE (4096) +#define LZSS_WINDOW_FILL (0x20) + +#define LZSS_MODE_EXPAND (0) +#define LZSS_MODE_MSHELP (1) +#define LZSS_MODE_QBASIC (2) + +/** + * Decompresses an LZSS stream. + * + * Input bytes will be read in as necessary using the system->read() + * function with the input file handle given. This will continue until + * system->read() returns 0 bytes, or an error. Errors will be passed + * out of the function as MSPACK_ERR_READ errors. Input streams should + * convey an "end of input stream" by refusing to supply all the bytes + * that LZSS asks for when they reach the end of the stream, rather + * than return an error code. + * + * Output bytes will be passed to the system->write() function, using + * the output file handle given. More than one call may be made to + * system->write(). + * + * As EXPAND.EXE (SZDD/KWAJ), Microsoft Help and QBasic have slightly + * different encodings for the control byte and matches, a "mode" + * parameter is allowed, to choose the encoding. + * + * @param system an mspack_system structure used to read from + * the input stream and write to the output + * stream, also to allocate and free memory. + * @param input an input stream with the LZSS data. + * @param output an output stream to write the decoded data to. + * @param input_buffer_size the number of bytes to use as an input + * bitstream buffer. + * @param mode one of #LZSS_MODE_EXPAND, #LZSS_MODE_MSHELP or + * #LZSS_MODE_QBASIC + * @return an error code, or MSPACK_ERR_OK if successful + */ +extern int lzss_decompress(struct mspack_system *system, + struct mspack_file *input, + struct mspack_file *output, + int input_buffer_size, + int mode); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/rbutil/rbutilqt/mspack/lzssd.c b/rbutil/rbutilqt/mspack/lzssd.c new file mode 100644 index 0000000000..df294bc132 --- /dev/null +++ b/rbutil/rbutilqt/mspack/lzssd.c @@ -0,0 +1,93 @@ +/* This file is part of libmspack. + * (C) 2003-2010 Stuart Caie. + * + * LZSS is a derivative of LZ77 and was created by James Storer and + * Thomas Szymanski in 1982. Haruhiko Okumura wrote a very popular C + * implementation. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#include +#include + +#define ENSURE_BYTES do { \ + if (i_ptr >= i_end) { \ + read = system->read(input, &inbuf[0], \ + input_buffer_size); \ + if (read <= 0) { \ + system->free(window); \ + return (read < 0) ? MSPACK_ERR_READ \ + : MSPACK_ERR_OK; \ + } \ + i_ptr = &inbuf[0]; i_end = &inbuf[read]; \ + } \ +} while (0) + +#define WRITE_BYTE do { \ + if (system->write(output, &window[pos], 1) != 1) { \ + system->free(window); \ + return MSPACK_ERR_WRITE; \ + } \ +} while (0) + +int lzss_decompress(struct mspack_system *system, + struct mspack_file *input, + struct mspack_file *output, + int input_buffer_size, + int mode) +{ + unsigned char *window, *inbuf, *i_ptr, *i_end; + unsigned int pos, i, c, invert, mpos, len; + int read; + + /* check parameters */ + if (!system || input_buffer_size < 1 || (mode != LZSS_MODE_EXPAND && + mode != LZSS_MODE_MSHELP && mode != LZSS_MODE_QBASIC)) + { + return MSPACK_ERR_ARGS; + } + + /* allocate memory */ + window = (unsigned char *) system->alloc(system, LZSS_WINDOW_SIZE + input_buffer_size); + if (!window) return MSPACK_ERR_NOMEMORY; + + /* initialise decompression */ + inbuf = &window[LZSS_WINDOW_SIZE]; + memset(window, LZSS_WINDOW_FILL, (size_t) LZSS_WINDOW_SIZE); + pos = LZSS_WINDOW_SIZE - ((mode == LZSS_MODE_QBASIC) ? 18 : 16); + invert = (mode == LZSS_MODE_MSHELP) ? ~0 : 0; + i_ptr = i_end = &inbuf[0]; + + /* loop forever; exit condition is in ENSURE_BYTES macro */ + for (;;) { + ENSURE_BYTES; c = *i_ptr++ ^ invert; + for (i = 0x01; i & 0xFF; i <<= 1) { + if (c & i) { + /* literal */ + ENSURE_BYTES; window[pos] = *i_ptr++; + WRITE_BYTE; + pos++; pos &= LZSS_WINDOW_SIZE - 1; + } + else { + /* match */ + ENSURE_BYTES; mpos = *i_ptr++; + ENSURE_BYTES; mpos |= (*i_ptr & 0xF0) << 4; + len = (*i_ptr++ & 0x0F) + 3; + while (len--) { + window[pos] = window[mpos]; + WRITE_BYTE; + pos++; pos &= LZSS_WINDOW_SIZE - 1; + mpos++; mpos &= LZSS_WINDOW_SIZE - 1; + } + } + } + } + + /* not reached */ + system->free(window); + return MSPACK_ERR_OK; +} diff --git a/rbutil/rbutilqt/mspack/lzx.h b/rbutil/rbutilqt/mspack/lzx.h new file mode 100644 index 0000000000..fc69928c96 --- /dev/null +++ b/rbutil/rbutilqt/mspack/lzx.h @@ -0,0 +1,194 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted + * by Microsoft Corporation. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_LZX_H +#define MSPACK_LZX_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* LZX compression / decompression definitions */ + +/* some constants defined by the LZX specification */ +#define LZX_MIN_MATCH (2) +#define LZX_MAX_MATCH (257) +#define LZX_NUM_CHARS (256) +#define LZX_BLOCKTYPE_INVALID (0) /* also blocktypes 4-7 invalid */ +#define LZX_BLOCKTYPE_VERBATIM (1) +#define LZX_BLOCKTYPE_ALIGNED (2) +#define LZX_BLOCKTYPE_UNCOMPRESSED (3) +#define LZX_PRETREE_NUM_ELEMENTS (20) +#define LZX_ALIGNED_NUM_ELEMENTS (8) /* aligned offset tree #elements */ +#define LZX_NUM_PRIMARY_LENGTHS (7) /* this one missing from spec! */ +#define LZX_NUM_SECONDARY_LENGTHS (249) /* length tree #elements */ + +/* LZX huffman defines: tweak tablebits as desired */ +#define LZX_PRETREE_MAXSYMBOLS (LZX_PRETREE_NUM_ELEMENTS) +#define LZX_PRETREE_TABLEBITS (6) +#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 50*8) +#define LZX_MAINTREE_TABLEBITS (12) +#define LZX_LENGTH_MAXSYMBOLS (LZX_NUM_SECONDARY_LENGTHS+1) +#define LZX_LENGTH_TABLEBITS (12) +#define LZX_ALIGNED_MAXSYMBOLS (LZX_ALIGNED_NUM_ELEMENTS) +#define LZX_ALIGNED_TABLEBITS (7) +#define LZX_LENTABLE_SAFETY (64) /* table decoding overruns are allowed */ + +#define LZX_FRAME_SIZE (32768) /* the size of a frame in LZX */ + +struct lzxd_stream { + struct mspack_system *sys; /* I/O routines */ + struct mspack_file *input; /* input file handle */ + struct mspack_file *output; /* output file handle */ + + off_t offset; /* number of bytes actually output */ + off_t length; /* overall decompressed length of stream */ + + unsigned char *window; /* decoding window */ + unsigned int window_size; /* window size */ + unsigned int window_posn; /* decompression offset within window */ + unsigned int frame_posn; /* current frame offset within in window */ + unsigned int frame; /* the number of 32kb frames processed */ + unsigned int reset_interval; /* which frame do we reset the compressor? */ + + unsigned int R0, R1, R2; /* for the LRU offset system */ + unsigned int block_length; /* uncompressed length of this LZX block */ + unsigned int block_remaining; /* uncompressed bytes still left to decode */ + + signed int intel_filesize; /* magic header value used for transform */ + signed int intel_curpos; /* current offset in transform space */ + + unsigned char intel_started; /* has intel E8 decoding started? */ + unsigned char block_type; /* type of the current block */ + unsigned char header_read; /* have we started decoding at all yet? */ + unsigned char posn_slots; /* how many posn slots in stream? */ + unsigned char input_end; /* have we reached the end of input? */ + + int error; + + /* I/O buffering */ + unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end; + unsigned int bit_buffer, bits_left, inbuf_size; + + /* huffman code lengths */ + unsigned char PRETREE_len [LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + unsigned char MAINTREE_len [LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + unsigned char LENGTH_len [LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + unsigned char ALIGNED_len [LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + + /* huffman decoding tables */ + unsigned short PRETREE_table [(1 << LZX_PRETREE_TABLEBITS) + + (LZX_PRETREE_MAXSYMBOLS * 2)]; + unsigned short MAINTREE_table[(1 << LZX_MAINTREE_TABLEBITS) + + (LZX_MAINTREE_MAXSYMBOLS * 2)]; + unsigned short LENGTH_table [(1 << LZX_LENGTH_TABLEBITS) + + (LZX_LENGTH_MAXSYMBOLS * 2)]; + unsigned short ALIGNED_table [(1 << LZX_ALIGNED_TABLEBITS) + + (LZX_ALIGNED_MAXSYMBOLS * 2)]; + unsigned char LENGTH_empty; + + /* this is used purely for doing the intel E8 transform */ + unsigned char e8_buf[LZX_FRAME_SIZE]; +}; + +/** + * Allocates and initialises LZX decompression state for decoding an LZX + * stream. + * + * This routine uses system->alloc() to allocate memory. If memory + * allocation fails, or the parameters to this function are invalid, + * NULL is returned. + * + * @param system an mspack_system structure used to read from + * the input stream and write to the output + * stream, also to allocate and free memory. + * @param input an input stream with the LZX data. + * @param output an output stream to write the decoded data to. + * @param window_bits the size of the decoding window, which must be + * between 15 and 21 inclusive. + * @param reset_interval the interval at which the LZX bitstream is + * reset, in multiples of LZX frames (32678 + * bytes), e.g. a value of 2 indicates the input + * stream resets after every 65536 output bytes. + * A value of 0 indicates that the bistream never + * resets, such as in CAB LZX streams. + * @param input_buffer_size the number of bytes to use as an input + * bitstream buffer. + * @param output_length the length in bytes of the entirely + * decompressed output stream, if known in + * advance. It is used to correctly perform the + * Intel E8 transformation, which must stop 6 + * bytes before the very end of the + * decompressed stream. It is not otherwise used + * or adhered to. If the full decompressed + * length is known in advance, set it here. + * If it is NOT known, use the value 0, and call + * lzxd_set_output_length() once it is + * known. If never set, 4 of the final 6 bytes + * of the output stream may be incorrect. + * @return a pointer to an initialised lzxd_stream structure, or NULL if + * there was not enough memory or parameters to the function were wrong. + */ +extern struct lzxd_stream *lzxd_init(struct mspack_system *system, + struct mspack_file *input, + struct mspack_file *output, + int window_bits, + int reset_interval, + int input_buffer_size, + off_t output_length); + +/* see description of output_length in lzxd_init() */ +extern void lzxd_set_output_length(struct lzxd_stream *lzx, + off_t output_length); + +/** + * Decompresses entire or partial LZX streams. + * + * The number of bytes of data that should be decompressed is given as the + * out_bytes parameter. If more bytes are decoded than are needed, they + * will be kept over for a later invocation. + * + * The output bytes will be passed to the system->write() function given in + * lzxd_init(), using the output file handle given in lzxd_init(). More than + * one call may be made to system->write(). + + * Input bytes will be read in as necessary using the system->read() + * function given in lzxd_init(), using the input file handle given in + * lzxd_init(). This will continue until system->read() returns 0 bytes, + * or an error. Errors will be passed out of the function as + * MSPACK_ERR_READ errors. Input streams should convey an "end of input + * stream" by refusing to supply all the bytes that LZX asks for when they + * reach the end of the stream, rather than return an error code. + * + * If any error code other than MSPACK_ERR_OK is returned, the stream + * should be considered unusable and lzxd_decompress() should not be + * called again on this stream. + * + * @param lzx LZX decompression state, as allocated by lzxd_init(). + * @param out_bytes the number of bytes of data to decompress. + * @return an error code, or MSPACK_ERR_OK if successful + */ +extern int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes); + +/** + * Frees all state associated with an LZX data stream. This will call + * system->free() using the system pointer given in lzxd_init(). + * + * @param lzx LZX decompression state to free. + */ +void lzxd_free(struct lzxd_stream *lzx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/rbutil/rbutilqt/mspack/lzxc.c b/rbutil/rbutilqt/mspack/lzxc.c new file mode 100644 index 0000000000..1207a0d747 --- /dev/null +++ b/rbutil/rbutilqt/mspack/lzxc.c @@ -0,0 +1,18 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted + * by Microsoft Corporation. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* LZX compression implementation */ + +#include +#include + +/* todo */ diff --git a/rbutil/rbutilqt/mspack/lzxd.c b/rbutil/rbutilqt/mspack/lzxd.c new file mode 100644 index 0000000000..cebc4c23d3 --- /dev/null +++ b/rbutil/rbutilqt/mspack/lzxd.c @@ -0,0 +1,738 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted + * by Microsoft Corporation. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* LZX decompression implementation */ + +#include +#include + +/* Microsoft's LZX document (in cab-sdk.exe) and their implementation + * of the com.ms.util.cab Java package do not concur. + * + * In the LZX document, there is a table showing the correlation between + * window size and the number of position slots. It states that the 1MB + * window = 40 slots and the 2MB window = 42 slots. In the implementation, + * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the + * first slot whose position base is equal to or more than the required + * window size'. This would explain why other tables in the document refer + * to 50 slots rather than 42. + * + * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode + * is not defined in the specification. + * + * The LZX document does not state the uncompressed block has an + * uncompressed length field. Where does this length field come from, so + * we can know how large the block is? The implementation has it as the 24 + * bits following after the 3 blocktype bits, before the alignment + * padding. + * + * The LZX document states that aligned offset blocks have their aligned + * offset huffman tree AFTER the main and length trees. The implementation + * suggests that the aligned offset tree is BEFORE the main and length + * trees. + * + * The LZX document decoding algorithm states that, in an aligned offset + * block, if an extra_bits value is 1, 2 or 3, then that number of bits + * should be read and the result added to the match offset. This is + * correct for 1 and 2, but not 3, where just a huffman symbol (using the + * aligned tree) should be read. + * + * Regarding the E8 preprocessing, the LZX document states 'No translation + * may be performed on the last 6 bytes of the input block'. This is + * correct. However, the pseudocode provided checks for the *E8 leader* + * up to the last 6 bytes. If the leader appears between -10 and -7 bytes + * from the end, this would cause the next four bytes to be modified, at + * least one of which would be in the last 6 bytes, which is not allowed + * according to the spec. + * + * The specification states that the huffman trees must always contain at + * least one element. However, many CAB files contain blocks where the + * length tree is completely empty (because there are no matches), and + * this is expected to succeed. + * + * The errors in LZX documentation appear have been corrected in the + * new documentation for the LZX DELTA format. + * + * http://msdn.microsoft.com/en-us/library/cc483133.aspx + * + * However, this is a different format, an extension of regular LZX. + * I have noticed the following differences, there may be more: + * + * The maximum window size has increased from 2MB to 32MB. This also + * increases the maximum number of position slots, etc. + * + * The format now allows for "reference data", supplied by the caller. + * If match offsets go further back than the number of bytes + * decompressed so far, that is them accessing the reference data. + */ + +/* import bit-reading macros and code */ +#define BITS_TYPE struct lzxd_stream +#define BITS_VAR lzx +#define BITS_ORDER_MSB +#define READ_BYTES do { \ + unsigned char b0, b1; \ + READ_IF_NEEDED; b0 = *i_ptr++; \ + READ_IF_NEEDED; b1 = *i_ptr++; \ + INJECT_BITS((b1 << 8) | b0, 16); \ +} while (0) +#include + +/* import huffman-reading macros and code */ +#define TABLEBITS(tbl) LZX_##tbl##_TABLEBITS +#define MAXSYMBOLS(tbl) LZX_##tbl##_MAXSYMBOLS +#define HUFF_TABLE(tbl,idx) lzx->tbl##_table[idx] +#define HUFF_LEN(tbl,idx) lzx->tbl##_len[idx] +#define HUFF_ERROR return lzx->error = MSPACK_ERR_DECRUNCH +#include + +/* BUILD_TABLE(tbl) builds a huffman lookup table from code lengths */ +#define BUILD_TABLE(tbl) \ + if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ + &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ + { \ + D(("failed to build %s table", #tbl)) \ + return lzx->error = MSPACK_ERR_DECRUNCH; \ + } + +#define BUILD_TABLE_MAYBE_EMPTY(tbl) do { \ + lzx->tbl##_empty = 0; \ + if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ + &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ + { \ + for (i = 0; i < MAXSYMBOLS(tbl); i++) { \ + if (HUFF_LEN(tbl, i) > 0) { \ + D(("failed to build %s table", #tbl)) \ + return lzx->error = MSPACK_ERR_DECRUNCH; \ + } \ + } \ + /* empty tree - allow it, but don't decode symbols with it */ \ + lzx->tbl##_empty = 1; \ + } \ +} while (0) + +/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols + * first to last in the given table. The code lengths are stored in their + * own special LZX way. + */ +#define READ_LENGTHS(tbl, first, last) do { \ + STORE_BITS; \ + if (lzxd_read_lens(lzx, &HUFF_LEN(tbl, 0), (first), \ + (unsigned int)(last))) return lzx->error; \ + RESTORE_BITS; \ +} while (0) + +static int lzxd_read_lens(struct lzxd_stream *lzx, unsigned char *lens, + unsigned int first, unsigned int last) +{ + /* bit buffer and huffman symbol decode variables */ + register unsigned int bit_buffer; + register int bits_left, i; + register unsigned short sym; + unsigned char *i_ptr, *i_end; + + unsigned int x, y; + int z; + + RESTORE_BITS; + + /* read lengths for pretree (20 symbols, lengths stored in fixed 4 bits) */ + for (x = 0; x < 20; x++) { + READ_BITS(y, 4); + lzx->PRETREE_len[x] = y; + } + BUILD_TABLE(PRETREE); + + for (x = first; x < last; ) { + READ_HUFFSYM(PRETREE, z); + if (z == 17) { + /* code = 17, run of ([read 4 bits]+4) zeros */ + READ_BITS(y, 4); y += 4; + while (y--) lens[x++] = 0; + } + else if (z == 18) { + /* code = 18, run of ([read 5 bits]+20) zeros */ + READ_BITS(y, 5); y += 20; + while (y--) lens[x++] = 0; + } + else if (z == 19) { + /* code = 19, run of ([read 1 bit]+4) [read huffman symbol] */ + READ_BITS(y, 1); y += 4; + READ_HUFFSYM(PRETREE, z); + z = lens[x] - z; if (z < 0) z += 17; + while (y--) lens[x++] = z; + } + else { + /* code = 0 to 16, delta current length entry */ + z = lens[x] - z; if (z < 0) z += 17; + lens[x++] = z; + } + } + + STORE_BITS; + + return MSPACK_ERR_OK; +} + +/* LZX static data tables: + * + * LZX uses 'position slots' to represent match offsets. For every match, + * a small 'position slot' number and a small offset from that slot are + * encoded instead of one large offset. + * + * position_base[] is an index to the position slot bases + * + * extra_bits[] states how many bits of offset-from-base data is needed. + * + * They are generated like so: + * for (i = 0; i < 4; i++) extra_bits[i] = 0; + * for (i = 4, j = 0; i < 36; i+=2) extra_bits[i] = extra_bits[i+1] = j++; + * for (i = 36; i < 51; i++) extra_bits[i] = 17; + * for (i = 0, j = 0; i < 51; j += 1 << extra_bits[i++]) position_base[i] = j; + */ +static const unsigned int position_base[51] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, + 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, + 16384, 24576, 32768, 49152, 65536, 98304, 131072, 196608, 262144, + 393216, 524288, 655360, 786432, 917504, 1048576, 1179648, 1310720, + 1441792, 1572864, 1703936, 1835008, 1966080, 2097152 +}; +static const unsigned char extra_bits[51] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 +}; + +static void lzxd_reset_state(struct lzxd_stream *lzx) { + int i; + + lzx->R0 = 1; + lzx->R1 = 1; + lzx->R2 = 1; + lzx->header_read = 0; + lzx->block_remaining = 0; + lzx->block_type = LZX_BLOCKTYPE_INVALID; + + /* initialise tables to 0 (because deltas will be applied to them) */ + for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) lzx->MAINTREE_len[i] = 0; + for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) lzx->LENGTH_len[i] = 0; +} + +/*-------- main LZX code --------*/ + +struct lzxd_stream *lzxd_init(struct mspack_system *system, + struct mspack_file *input, + struct mspack_file *output, + int window_bits, + int reset_interval, + int input_buffer_size, + off_t output_length) +{ + unsigned int window_size = 1 << window_bits; + struct lzxd_stream *lzx; + + if (!system) return NULL; + + /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */ + if (window_bits < 15 || window_bits > 21) return NULL; + + input_buffer_size = (input_buffer_size + 1) & -2; + if (!input_buffer_size) return NULL; + + /* allocate decompression state */ + if (!(lzx = (struct lzxd_stream *) system->alloc(system, sizeof(struct lzxd_stream)))) { + return NULL; + } + + /* allocate decompression window and input buffer */ + lzx->window = (unsigned char *) system->alloc(system, (size_t) window_size); + lzx->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size); + if (!lzx->window || !lzx->inbuf) { + system->free(lzx->window); + system->free(lzx->inbuf); + system->free(lzx); + return NULL; + } + + /* initialise decompression state */ + lzx->sys = system; + lzx->input = input; + lzx->output = output; + lzx->offset = 0; + lzx->length = output_length; + + lzx->inbuf_size = input_buffer_size; + lzx->window_size = 1 << window_bits; + lzx->window_posn = 0; + lzx->frame_posn = 0; + lzx->frame = 0; + lzx->reset_interval = reset_interval; + lzx->intel_filesize = 0; + lzx->intel_curpos = 0; + lzx->intel_started = 0; + lzx->error = MSPACK_ERR_OK; + + /* window bits: 15 16 17 18 19 20 21 + * position slots: 30 32 34 36 38 42 50 */ + lzx->posn_slots = ((window_bits == 21) ? 50 : + ((window_bits == 20) ? 42 : (window_bits << 1))); + + lzx->o_ptr = lzx->o_end = &lzx->e8_buf[0]; + lzxd_reset_state(lzx); + INIT_BITS; + return lzx; +} + +void lzxd_set_output_length(struct lzxd_stream *lzx, off_t out_bytes) { + if (lzx) lzx->length = out_bytes; +} + +int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { + /* bitstream and huffman reading variables */ + register unsigned int bit_buffer; + register int bits_left, i=0; + unsigned char *i_ptr, *i_end; + register unsigned short sym; + + int match_length, length_footer, extra, verbatim_bits, bytes_todo; + int this_run, main_element, aligned_bits, j; + unsigned char *window, *runsrc, *rundest, buf[12]; + unsigned int frame_size=0, end_frame, match_offset, window_posn; + unsigned int R0, R1, R2; + + /* easy answers */ + if (!lzx || (out_bytes < 0)) return MSPACK_ERR_ARGS; + if (lzx->error) return lzx->error; + + /* flush out any stored-up bytes before we begin */ + i = lzx->o_end - lzx->o_ptr; + if ((off_t) i > out_bytes) i = (int) out_bytes; + if (i) { + if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) { + return lzx->error = MSPACK_ERR_WRITE; + } + lzx->o_ptr += i; + lzx->offset += i; + out_bytes -= i; + } + if (out_bytes == 0) return MSPACK_ERR_OK; + + /* restore local state */ + RESTORE_BITS; + window = lzx->window; + window_posn = lzx->window_posn; + R0 = lzx->R0; + R1 = lzx->R1; + R2 = lzx->R2; + + end_frame = (unsigned int)((lzx->offset + out_bytes) / LZX_FRAME_SIZE) + 1; + + while (lzx->frame < end_frame) { + /* have we reached the reset interval? (if there is one?) */ + if (lzx->reset_interval && ((lzx->frame % lzx->reset_interval) == 0)) { + if (lzx->block_remaining) { + D(("%d bytes remaining at reset interval", lzx->block_remaining)) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* re-read the intel header and reset the huffman lengths */ + lzxd_reset_state(lzx); + R0 = lzx->R0; + R1 = lzx->R1; + R2 = lzx->R2; + } + + /* read header if necessary */ + if (!lzx->header_read) { + /* read 1 bit. if bit=0, intel filesize = 0. + * if bit=1, read intel filesize (32 bits) */ + j = 0; READ_BITS(i, 1); if (i) { READ_BITS(i, 16); READ_BITS(j, 16); } + lzx->intel_filesize = (i << 16) | j; + lzx->header_read = 1; + } + + /* calculate size of frame: all frames are 32k except the final frame + * which is 32kb or less. this can only be calculated when lzx->length + * has been filled in. */ + frame_size = LZX_FRAME_SIZE; + if (lzx->length && (lzx->length - lzx->offset) < (off_t)frame_size) { + frame_size = lzx->length - lzx->offset; + } + + /* decode until one more frame is available */ + bytes_todo = lzx->frame_posn + frame_size - window_posn; + while (bytes_todo > 0) { + /* initialise new block, if one is needed */ + if (lzx->block_remaining == 0) { + /* realign if previous block was an odd-sized UNCOMPRESSED block */ + if ((lzx->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) && + (lzx->block_length & 1)) + { + READ_IF_NEEDED; + i_ptr++; + } + + /* read block type (3 bits) and block length (24 bits) */ + READ_BITS(lzx->block_type, 3); + READ_BITS(i, 16); READ_BITS(j, 8); + lzx->block_remaining = lzx->block_length = (i << 8) | j; + /*D(("new block t%d len %u", lzx->block_type, lzx->block_length))*/ + + /* read individual block headers */ + switch (lzx->block_type) { + case LZX_BLOCKTYPE_ALIGNED: + /* read lengths of and build aligned huffman decoding tree */ + for (i = 0; i < 8; i++) { READ_BITS(j, 3); lzx->ALIGNED_len[i] = j; } + BUILD_TABLE(ALIGNED); + /* no break -- rest of aligned header is same as verbatim */ + case LZX_BLOCKTYPE_VERBATIM: + /* read lengths of and build main huffman decoding tree */ + READ_LENGTHS(MAINTREE, 0, 256); + READ_LENGTHS(MAINTREE, 256, LZX_NUM_CHARS + (lzx->posn_slots << 3)); + BUILD_TABLE(MAINTREE); + /* if the literal 0xE8 is anywhere in the block... */ + if (lzx->MAINTREE_len[0xE8] != 0) lzx->intel_started = 1; + /* read lengths of and build lengths huffman decoding tree */ + READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS); + BUILD_TABLE_MAYBE_EMPTY(LENGTH); + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + /* because we can't assume otherwise */ + lzx->intel_started = 1; + + /* read 1-16 (not 0-15) bits to align to bytes */ + ENSURE_BITS(16); + if (bits_left > 16) i_ptr -= 2; + bits_left = 0; bit_buffer = 0; + + /* read 12 bytes of stored R0 / R1 / R2 values */ + for (rundest = &buf[0], i = 0; i < 12; i++) { + READ_IF_NEEDED; + *rundest++ = *i_ptr++; + } + R0 = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + R1 = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); + R2 = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24); + break; + + default: + D(("bad block type")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + } + + /* decode more of the block: + * run = min(what's available, what's needed) */ + this_run = lzx->block_remaining; + if (this_run > bytes_todo) this_run = bytes_todo; + + /* assume we decode exactly this_run bytes, for now */ + bytes_todo -= this_run; + lzx->block_remaining -= this_run; + + /* decode at least this_run bytes */ + switch (lzx->block_type) { + case LZX_BLOCKTYPE_VERBATIM: + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } + else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + /* get match length */ + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + if (lzx->LENGTH_empty) { + D(("LENGTH symbol needed but tree is empty")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + /* get match offset */ + switch ((match_offset = (main_element >> 3))) { + case 0: match_offset = R0; break; + case 1: match_offset = R1; R1=R0; R0 = match_offset; break; + case 2: match_offset = R2; R2=R0; R0 = match_offset; break; + case 3: match_offset = 1; R2=R1; R1=R0; R0 = match_offset; break; + default: + extra = extra_bits[match_offset]; + READ_BITS(verbatim_bits, extra); + match_offset = position_base[match_offset] - 2 + verbatim_bits; + R2 = R1; R1 = R0; R0 = match_offset; + } + + if ((window_posn + match_length) > lzx->window_size) { + D(("match ran over window wrap")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* copy match */ + rundest = &window[window_posn]; + i = match_length; + /* does match offset wrap the window? */ + if (match_offset > window_posn) { + /* j = length from match offset to end of window */ + j = match_offset - window_posn; + if (j > (int) lzx->window_size) { + D(("match offset beyond window boundaries")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + runsrc = &window[lzx->window_size - j]; + if (j < i) { + /* if match goes over the window edge, do two copy runs */ + i -= j; while (j-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + while (i-- > 0) *rundest++ = *runsrc++; + } + else { + runsrc = rundest - match_offset; + while (i-- > 0) *rundest++ = *runsrc++; + } + + this_run -= match_length; + window_posn += match_length; + } + } /* while (this_run > 0) */ + break; + + case LZX_BLOCKTYPE_ALIGNED: + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } + else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + /* get match length */ + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + if (lzx->LENGTH_empty) { + D(("LENGTH symbol needed but tree is empty")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + /* get match offset */ + switch ((match_offset = (main_element >> 3))) { + case 0: match_offset = R0; break; + case 1: match_offset = R1; R1 = R0; R0 = match_offset; break; + case 2: match_offset = R2; R2 = R0; R0 = match_offset; break; + default: + extra = extra_bits[match_offset]; + match_offset = position_base[match_offset] - 2; + if (extra > 3) { + /* verbatim and aligned bits */ + extra -= 3; + READ_BITS(verbatim_bits, extra); + match_offset += (verbatim_bits << 3); + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } + else if (extra == 3) { + /* aligned bits only */ + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } + else if (extra > 0) { /* extra==1, extra==2 */ + /* verbatim bits only */ + READ_BITS(verbatim_bits, extra); + match_offset += verbatim_bits; + } + else /* extra == 0 */ { + /* ??? not defined in LZX specification! */ + match_offset = 1; + } + /* update repeated offset LRU queue */ + R2 = R1; R1 = R0; R0 = match_offset; + } + + if ((window_posn + match_length) > lzx->window_size) { + D(("match ran over window wrap")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* copy match */ + rundest = &window[window_posn]; + i = match_length; + /* does match offset wrap the window? */ + if (match_offset > window_posn) { + /* j = length from match offset to end of window */ + j = match_offset - window_posn; + if (j > (int) lzx->window_size) { + D(("match offset beyond window boundaries")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + runsrc = &window[lzx->window_size - j]; + if (j < i) { + /* if match goes over the window edge, do two copy runs */ + i -= j; while (j-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + while (i-- > 0) *rundest++ = *runsrc++; + } + else { + runsrc = rundest - match_offset; + while (i-- > 0) *rundest++ = *runsrc++; + } + + this_run -= match_length; + window_posn += match_length; + } + } /* while (this_run > 0) */ + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + /* as this_run is limited not to wrap a frame, this also means it + * won't wrap the window (as the window is a multiple of 32k) */ + rundest = &window[window_posn]; + window_posn += this_run; + while (this_run > 0) { + if ((i = i_end - i_ptr) == 0) { + READ_IF_NEEDED; + } + else { + if (i > this_run) i = this_run; + lzx->sys->copy(i_ptr, rundest, (size_t) i); + rundest += i; + i_ptr += i; + this_run -= i; + } + } + break; + + default: + return lzx->error = MSPACK_ERR_DECRUNCH; /* might as well */ + } + + /* did the final match overrun our desired this_run length? */ + if (this_run < 0) { + if ((unsigned int)(-this_run) > lzx->block_remaining) { + D(("overrun went past end of block by %d (%d remaining)", + -this_run, lzx->block_remaining )) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + lzx->block_remaining -= -this_run; + } + } /* while (bytes_todo > 0) */ + + /* streams don't extend over frame boundaries */ + if ((window_posn - lzx->frame_posn) != frame_size) { + D(("decode beyond output frame limits! %d != %d", + window_posn - lzx->frame_posn, frame_size)) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* re-align input bitstream */ + if (bits_left > 0) ENSURE_BITS(16); + if (bits_left & 15) REMOVE_BITS(bits_left & 15); + + /* check that we've used all of the previous frame first */ + if (lzx->o_ptr != lzx->o_end) { + D(("%ld avail bytes, new %d frame", lzx->o_end-lzx->o_ptr, frame_size)) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* does this intel block _really_ need decoding? */ + if (lzx->intel_started && lzx->intel_filesize && + (lzx->frame <= 32768) && (frame_size > 10)) + { + unsigned char *data = &lzx->e8_buf[0]; + unsigned char *dataend = &lzx->e8_buf[frame_size - 10]; + signed int curpos = lzx->intel_curpos; + signed int filesize = lzx->intel_filesize; + signed int abs_off, rel_off; + + /* copy e8 block to the e8 buffer and tweak if needed */ + lzx->o_ptr = data; + lzx->sys->copy(&lzx->window[lzx->frame_posn], data, frame_size); + + while (data < dataend) { + if (*data++ != 0xE8) { curpos++; continue; } + abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); + if ((abs_off >= -curpos) && (abs_off < filesize)) { + rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; + data[0] = (unsigned char) rel_off; + data[1] = (unsigned char) (rel_off >> 8); + data[2] = (unsigned char) (rel_off >> 16); + data[3] = (unsigned char) (rel_off >> 24); + } + data += 4; + curpos += 5; + } + lzx->intel_curpos += frame_size; + } + else { + lzx->o_ptr = &lzx->window[lzx->frame_posn]; + if (lzx->intel_filesize) lzx->intel_curpos += frame_size; + } + lzx->o_end = &lzx->o_ptr[frame_size]; + + /* write a frame */ + i = (out_bytes < (off_t)frame_size) ? (unsigned int)out_bytes : frame_size; + if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) { + return lzx->error = MSPACK_ERR_WRITE; + } + lzx->o_ptr += i; + lzx->offset += i; + out_bytes -= i; + + /* advance frame start position */ + lzx->frame_posn += frame_size; + lzx->frame++; + + /* wrap window / frame position pointers */ + if (window_posn == lzx->window_size) window_posn = 0; + if (lzx->frame_posn == lzx->window_size) lzx->frame_posn = 0; + + } /* while (lzx->frame < end_frame) */ + + if (out_bytes) { + D(("bytes left to output")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* store local state */ + STORE_BITS; + lzx->window_posn = window_posn; + lzx->R0 = R0; + lzx->R1 = R1; + lzx->R2 = R2; + + return MSPACK_ERR_OK; +} + +void lzxd_free(struct lzxd_stream *lzx) { + struct mspack_system *sys; + if (lzx) { + sys = lzx->sys; + sys->free(lzx->inbuf); + sys->free(lzx->window); + sys->free(lzx); + } +} diff --git a/rbutil/rbutilqt/mspack/mspack.h b/rbutil/rbutilqt/mspack/mspack.h new file mode 100644 index 0000000000..7f6bdf1465 --- /dev/null +++ b/rbutil/rbutilqt/mspack/mspack.h @@ -0,0 +1,2203 @@ +/* libmspack -- a library for working with Microsoft compression formats. + * (C) 2003-2011 Stuart Caie + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** \mainpage + * + * \section intro Introduction + * + * libmspack is a library which provides compressors and decompressors, + * archivers and dearchivers for Microsoft compression formats. + * + * \section formats Formats supported + * + * The following file formats are supported: + * - SZDD files, which use LZSS compression + * - KWAJ files, which use LZSS, LZSS+Huffman or deflate compression + * - .HLP (MS Help) files, which use LZSS compression + * - .CAB (MS Cabinet) files, which use deflate, LZX or Quantum compression + * - .CHM (HTML Help) files, which use LZX compression + * - .LIT (MS EBook) files, which use LZX compression and DES encryption + * + * To determine the capabilities of the library, and the binary + * compatibility version of any particular compressor or decompressor, use + * the mspack_version() function. The UNIX library interface version is + * defined as the highest-versioned library component. + * + * \section starting Getting started + * + * The macro MSPACK_SYS_SELFTEST() should be used to ensure the library can + * be used. In particular, it checks if the caller is using 32-bit file I/O + * when the library is compiled for 64-bit file I/O and vice versa. + * + * If compiled normally, the library includes basic file I/O and memory + * management functionality using the standard C library. This can be + * customised and replaced entirely by creating a mspack_system structure. + * + * A compressor or decompressor for the required format must be + * instantiated before it can be used. Each construction function takes + * one parameter, which is either a pointer to a custom mspack_system + * structure, or NULL to use the default. The instantiation returned, if + * not NULL, contains function pointers (methods) to work with the given + * file format. + * + * For compression: + * - mspack_create_cab_compressor() creates a mscab_compressor + * - mspack_create_chm_compressor() creates a mschm_compressor + * - mspack_create_lit_compressor() creates a mslit_compressor + * - mspack_create_hlp_compressor() creates a mshlp_compressor + * - mspack_create_szdd_compressor() creates a msszdd_compressor + * - mspack_create_kwaj_compressor() creates a mskwaj_compressor + * + * For decompression: + * - mspack_create_cab_decompressor() creates a mscab_decompressor + * - mspack_create_chm_decompressor() creates a mschm_decompressor + * - mspack_create_lit_decompressor() creates a mslit_decompressor + * - mspack_create_hlp_decompressor() creates a mshlp_decompressor + * - mspack_create_szdd_decompressor() creates a msszdd_decompressor + * - mspack_create_kwaj_decompressor() creates a mskwaj_decompressor + * + * Once finished working with a format, each kind of + * compressor/decompressor has its own specific destructor: + * - mspack_destroy_cab_compressor() + * - mspack_destroy_cab_decompressor() + * - mspack_destroy_chm_compressor() + * - mspack_destroy_chm_decompressor() + * - mspack_destroy_lit_compressor() + * - mspack_destroy_lit_decompressor() + * - mspack_destroy_hlp_compressor() + * - mspack_destroy_hlp_decompressor() + * - mspack_destroy_szdd_compressor() + * - mspack_destroy_szdd_decompressor() + * - mspack_destroy_kwaj_compressor() + * - mspack_destroy_kwaj_decompressor() + * + * Destroying a compressor or decompressor does not destroy any objects, + * structures or handles that have been created using that compressor or + * decompressor. Ensure that everything created or opened is destroyed or + * closed before compressor/decompressor is itself destroyed. + * + * \section errors Error codes + * + * All compressors and decompressors use the same set of error codes. Most + * methods return an error code directly. For methods which do not + * return error codes directly, the error code can be obtained with the + * last_error() method. + * + * - #MSPACK_ERR_OK is used to indicate success. This error code is defined + * as zero, all other code are non-zero. + * - #MSPACK_ERR_ARGS indicates that a method was called with inappropriate + * arguments. + * - #MSPACK_ERR_OPEN indicates that mspack_system::open() failed. + * - #MSPACK_ERR_READ indicates that mspack_system::read() failed. + * - #MSPACK_ERR_WRITE indicates that mspack_system::write() failed. + * - #MSPACK_ERR_SEEK indicates that mspack_system::seek() failed. + * - #MSPACK_ERR_NOMEMORY indicates that mspack_system::alloc() failed. + * - #MSPACK_ERR_SIGNATURE indicates that the file being read does not + * have the correct "signature". It is probably not a valid file for + * whatever format is being read. + * - #MSPACK_ERR_DATAFORMAT indicates that the file being used or read + * is corrupt. + * - #MSPACK_ERR_CHECKSUM indicates that a data checksum has failed. + * - #MSPACK_ERR_CRUNCH indicates an error occured during compression. + * - #MSPACK_ERR_DECRUNCH indicates an error occured during decompression. + * + * \section threading Multi-threading + * + * libmspack methods are reentrant and multithreading-safe when each + * thread has its own compressor or decompressor. + + * You should not call multiple methods simultaneously on a single + * compressor or decompressor instance. + * + * If this may happen, you can either use one compressor or + * decompressor per thread, or you can use your preferred lock, + * semaphore or mutex library to ensure no more than one method on a + * compressor/decompressor is called simultaneously. libmspack will + * not do this locking for you. + * + * Example of incorrect behaviour: + * - thread 1 calls mspack_create_cab_decompressor() + * - thread 1 calls open() + * - thread 1 calls extract() for one file + * - thread 2 simultaneously calls extract() for another file + * + * Correct behaviour: + * - thread 1 calls mspack_create_cab_decompressor() + * - thread 2 calls mspack_create_cab_decompressor() + * - thread 1 calls its own open() / extract() + * - thread 2 simultaneously calls its own open() / extract() + * + * Also correct behaviour: + * - thread 1 calls mspack_create_cab_decompressor() + * - thread 1 locks a mutex for with the decompressor before + * calling any methods on it, and unlocks the mutex after each + * method returns. + * - thread 1 can share the results of open() with thread 2, and both + * can call extract(), provided they both guard against simultaneous + * use of extract(), and any other methods, with the mutex + */ + +#ifndef LIB_MSPACK_H +#define LIB_MSPACK_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * System self-test function, to ensure both library and calling program + * can use one another. + * + * A result of MSPACK_ERR_OK means the library and caller are + * compatible. Any other result indicates that the library and caller are + * not compatible and should not be used. In particular, a value of + * MSPACK_ERR_SEEK means the library and caller use different off_t + * datatypes. + * + * It should be used like so: + * + * @code + * int selftest_result; + * MSPACK_SYS_SELFTEST(selftest_result); + * if (selftest_result != MSPACK_ERR_OK) { + * fprintf(stderr, "incompatible with this build of libmspack\n"); + * exit(0); + * } + * @endcode + * + * @param result an int variable to store the result of the self-test + */ +#define MSPACK_SYS_SELFTEST(result) do { \ + (result) = mspack_sys_selftest_internal(sizeof(off_t)); \ +} while (0) + +/** Part of the MSPACK_SYS_SELFTEST() macro, must not be used directly. */ +extern int mspack_sys_selftest_internal(int); + +/** + * Enquire about the binary compatibility version of a specific interface in + * the library. Currently, the following interfaces are defined: + * + * - #MSPACK_VER_LIBRARY: the overall library + * - #MSPACK_VER_SYSTEM: the mspack_system interface + * - #MSPACK_VER_MSCABD: the mscab_decompressor interface + * - #MSPACK_VER_MSCABC: the mscab_compressor interface + * - #MSPACK_VER_MSCHMD: the mschm_decompressor interface + * - #MSPACK_VER_MSCHMC: the mschm_compressor interface + * - #MSPACK_VER_MSLITD: the mslit_decompressor interface + * - #MSPACK_VER_MSLITC: the mslit_compressor interface + * - #MSPACK_VER_MSHLPD: the mshlp_decompressor interface + * - #MSPACK_VER_MSHLPC: the mshlp_compressor interface + * - #MSPACK_VER_MSSZDDD: the msszdd_decompressor interface + * - #MSPACK_VER_MSSZDDC: the msszdd_compressor interface + * - #MSPACK_VER_MSKWAJD: the mskwaj_decompressor interface + * - #MSPACK_VER_MSKWAJC: the mskwaj_compressor interface + * + * The result of the function should be interpreted as follows: + * - -1: this interface is completely unknown to the library + * - 0: this interface is known, but non-functioning + * - 1: this interface has all basic functionality + * - 2, 3, ...: this interface has additional functionality, clearly marked + * in the documentation as "version 2", "version 3" and so on. + * + * @param entity the interface to request current version of + * @return the version of the requested interface + */ +extern int mspack_version(int entity); + +/** Pass to mspack_version() to get the overall library version */ +#define MSPACK_VER_LIBRARY (0) +/** Pass to mspack_version() to get the mspack_system version */ +#define MSPACK_VER_SYSTEM (1) +/** Pass to mspack_version() to get the mscab_decompressor version */ +#define MSPACK_VER_MSCABD (2) +/** Pass to mspack_version() to get the mscab_compressor version */ +#define MSPACK_VER_MSCABC (3) +/** Pass to mspack_version() to get the mschm_decompressor version */ +#define MSPACK_VER_MSCHMD (4) +/** Pass to mspack_version() to get the mschm_compressor version */ +#define MSPACK_VER_MSCHMC (5) +/** Pass to mspack_version() to get the mslit_decompressor version */ +#define MSPACK_VER_MSLITD (6) +/** Pass to mspack_version() to get the mslit_compressor version */ +#define MSPACK_VER_MSLITC (7) +/** Pass to mspack_version() to get the mshlp_decompressor version */ +#define MSPACK_VER_MSHLPD (8) +/** Pass to mspack_version() to get the mshlp_compressor version */ +#define MSPACK_VER_MSHLPC (9) +/** Pass to mspack_version() to get the msszdd_decompressor version */ +#define MSPACK_VER_MSSZDDD (10) +/** Pass to mspack_version() to get the msszdd_compressor version */ +#define MSPACK_VER_MSSZDDC (11) +/** Pass to mspack_version() to get the mskwaj_decompressor version */ +#define MSPACK_VER_MSKWAJD (12) +/** Pass to mspack_version() to get the mskwaj_compressor version */ +#define MSPACK_VER_MSKWAJC (13) + +/* --- file I/O abstraction ------------------------------------------------ */ + +/** + * A structure which abstracts file I/O and memory management. + * + * The library always uses the mspack_system structure for interaction + * with the file system and to allocate, free and copy all memory. It also + * uses it to send literal messages to the library user. + * + * When the library is compiled normally, passing NULL to a compressor or + * decompressor constructor will result in a default mspack_system being + * used, where all methods are implemented with the standard C library. + * However, all constructors support being given a custom created + * mspack_system structure, with the library user's own methods. This + * allows for more abstract interaction, such as reading and writing files + * directly to memory, or from a network socket or pipe. + * + * Implementors of an mspack_system structure should read all + * documentation entries for every structure member, and write methods + * which conform to those standards. + */ +struct mspack_system { + /** + * Opens a file for reading, writing, appending or updating. + * + * @param self a self-referential pointer to the mspack_system + * structure whose open() method is being called. If + * this pointer is required by close(), read(), write(), + * seek() or tell(), it should be stored in the result + * structure at this time. + * @param filename the file to be opened. It is passed directly from the + * library caller without being modified, so it is up to + * the caller what this parameter actually represents. + * @param mode one of #MSPACK_SYS_OPEN_READ (open an existing file + * for reading), #MSPACK_SYS_OPEN_WRITE (open a new file + * for writing), #MSPACK_SYS_OPEN_UPDATE (open an existing + * file for reading/writing from the start of the file) or + * #MSPACK_SYS_OPEN_APPEND (open an existing file for + * reading/writing from the end of the file) + * @return a pointer to a mspack_file structure. This structure officially + * contains no members, its true contents are up to the + * mspack_system implementor. It should contain whatever is needed + * for other mspack_system methods to operate. Returning the NULL + * pointer indicates an error condition. + * @see close(), read(), write(), seek(), tell(), message() + */ + struct mspack_file * (*open)(struct mspack_system *self, + const char *filename, + int mode); + + /** + * Closes a previously opened file. If any memory was allocated for this + * particular file handle, it should be freed at this time. + * + * @param file the file to close + * @see open() + */ + void (*close)(struct mspack_file *file); + + /** + * Reads a given number of bytes from an open file. + * + * @param file the file to read from + * @param buffer the location where the read bytes should be stored + * @param bytes the number of bytes to read from the file. + * @return the number of bytes successfully read (this can be less than + * the number requested), zero to mark the end of file, or less + * than zero to indicate an error. + * @see open(), write() + */ + int (*read)(struct mspack_file *file, + void *buffer, + int bytes); + + /** + * Writes a given number of bytes to an open file. + * + * @param file the file to write to + * @param buffer the location where the written bytes should be read from + * @param bytes the number of bytes to write to the file. + * @return the number of bytes successfully written, this can be less + * than the number requested. Zero or less can indicate an error + * where no bytes at all could be written. All cases where less + * bytes were written than requested are considered by the library + * to be an error. + * @see open(), read() + */ + int (*write)(struct mspack_file *file, + void *buffer, + int bytes); + + /** + * Seeks to a specific file offset within an open file. + * + * Sometimes the library needs to know the length of a file. It does + * this by seeking to the end of the file with seek(file, 0, + * MSPACK_SYS_SEEK_END), then calling tell(). Implementations may want + * to make a special case for this. + * + * Due to the potentially varying 32/64 bit datatype off_t on some + * architectures, the #MSPACK_SYS_SELFTEST macro MUST be used before + * using the library. If not, the error caused by the library passing an + * inappropriate stackframe to seek() is subtle and hard to trace. + * + * @param file the file to be seeked + * @param offset an offset to seek, measured in bytes + * @param mode one of #MSPACK_SYS_SEEK_START (the offset should be + * measured from the start of the file), #MSPACK_SYS_SEEK_CUR + * (the offset should be measured from the current file offset) + * or #MSPACK_SYS_SEEK_END (the offset should be measured from + * the end of the file) + * @return zero for success, non-zero for an error + * @see open(), tell() + */ + int (*seek)(struct mspack_file *file, + off_t offset, + int mode); + + /** + * Returns the current file position (in bytes) of the given file. + * + * @param file the file whose file position is wanted + * @return the current file position of the file + * @see open(), seek() + */ + off_t (*tell)(struct mspack_file *file); + + /** + * Used to send messages from the library to the user. + * + * Occasionally, the library generates warnings or other messages in + * plain english to inform the human user. These are informational only + * and can be ignored if not wanted. + * + * @param file may be a file handle returned from open() if this message + * pertains to a specific open file, or NULL if not related to + * a specific file. + * @param format a printf() style format string. It does NOT include a + * trailing newline. + * @see open() + */ + void (*message)(struct mspack_file *file, + const char *format, + ...); + + /** + * Allocates memory. + * + * @param self a self-referential pointer to the mspack_system + * structure whose alloc() method is being called. + * @param bytes the number of bytes to allocate + * @result a pointer to the requested number of bytes, or NULL if + * not enough memory is available + * @see free() + */ + void * (*alloc)(struct mspack_system *self, + size_t bytes); + + /** + * Frees memory. + * + * @param ptr the memory to be freed. + * @see alloc() + */ + void (*free)(void *ptr); + + /** + * Copies from one region of memory to another. + * + * The regions of memory are guaranteed not to overlap, are usually less + * than 256 bytes, and may not be aligned. Please note that the source + * parameter comes before the destination parameter, unlike the standard + * C function memcpy(). + * + * @param src the region of memory to copy from + * @param dest the region of memory to copy to + * @param bytes the size of the memory region, in bytes + */ + void (*copy)(void *src, + void *dest, + size_t bytes); + + /** + * A null pointer to mark the end of mspack_system. It must equal NULL. + * + * Should the mspack_system structure extend in the future, this NULL + * will be seen, rather than have an invalid method pointer called. + */ + void *null_ptr; +}; + +/** mspack_system::open() mode: open existing file for reading. */ +#define MSPACK_SYS_OPEN_READ (0) +/** mspack_system::open() mode: open new file for writing */ +#define MSPACK_SYS_OPEN_WRITE (1) +/** mspack_system::open() mode: open existing file for writing */ +#define MSPACK_SYS_OPEN_UPDATE (2) +/** mspack_system::open() mode: open existing file for writing */ +#define MSPACK_SYS_OPEN_APPEND (3) + +/** mspack_system::seek() mode: seek relative to start of file */ +#define MSPACK_SYS_SEEK_START (0) +/** mspack_system::seek() mode: seek relative to current offset */ +#define MSPACK_SYS_SEEK_CUR (1) +/** mspack_system::seek() mode: seek relative to end of file */ +#define MSPACK_SYS_SEEK_END (2) + +/** + * A structure which represents an open file handle. The contents of this + * structure are determined by the implementation of the + * mspack_system::open() method. + */ +struct mspack_file { + int dummy; +}; + +/* --- error codes --------------------------------------------------------- */ + +/** Error code: no error */ +#define MSPACK_ERR_OK (0) +/** Error code: bad arguments to method */ +#define MSPACK_ERR_ARGS (1) +/** Error code: error opening file */ +#define MSPACK_ERR_OPEN (2) +/** Error code: error reading file */ +#define MSPACK_ERR_READ (3) +/** Error code: error writing file */ +#define MSPACK_ERR_WRITE (4) +/** Error code: seek error */ +#define MSPACK_ERR_SEEK (5) +/** Error code: out of memory */ +#define MSPACK_ERR_NOMEMORY (6) +/** Error code: bad "magic id" in file */ +#define MSPACK_ERR_SIGNATURE (7) +/** Error code: bad or corrupt file format */ +#define MSPACK_ERR_DATAFORMAT (8) +/** Error code: bad checksum or CRC */ +#define MSPACK_ERR_CHECKSUM (9) +/** Error code: error during compression */ +#define MSPACK_ERR_CRUNCH (10) +/** Error code: error during decompression */ +#define MSPACK_ERR_DECRUNCH (11) + +/* --- functions available in library -------------------------------------- */ + +/** Creates a new CAB compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mscab_compressor or NULL + */ +extern struct mscab_compressor * + mspack_create_cab_compressor(struct mspack_system *sys); + +/** Creates a new CAB decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mscab_decompressor or NULL + */ +extern struct mscab_decompressor * + mspack_create_cab_decompressor(struct mspack_system *sys); + +/** Destroys an existing CAB compressor. + * @param self the #mscab_compressor to destroy + */ +extern void mspack_destroy_cab_compressor(struct mscab_compressor *self); + +/** Destroys an existing CAB decompressor. + * @param self the #mscab_decompressor to destroy + */ +extern void mspack_destroy_cab_decompressor(struct mscab_decompressor *self); + + +/** Creates a new CHM compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mschm_compressor or NULL + */ +extern struct mschm_compressor * + mspack_create_chm_compressor(struct mspack_system *sys); + +/** Creates a new CHM decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mschm_decompressor or NULL + */ +extern struct mschm_decompressor * + mspack_create_chm_decompressor(struct mspack_system *sys); + +/** Destroys an existing CHM compressor. + * @param self the #mschm_compressor to destroy + */ +extern void mspack_destroy_chm_compressor(struct mschm_compressor *self); + +/** Destroys an existing CHM decompressor. + * @param self the #mschm_decompressor to destroy + */ +extern void mspack_destroy_chm_decompressor(struct mschm_decompressor *self); + + +/** Creates a new LIT compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mslit_compressor or NULL + */ +extern struct mslit_compressor * + mspack_create_lit_compressor(struct mspack_system *sys); + +/** Creates a new LIT decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mslit_decompressor or NULL + */ +extern struct mslit_decompressor * + mspack_create_lit_decompressor(struct mspack_system *sys); + +/** Destroys an existing LIT compressor. + * @param self the #mslit_compressor to destroy + */ +extern void mspack_destroy_lit_compressor(struct mslit_compressor *self); + +/** Destroys an existing LIT decompressor. + * @param self the #mslit_decompressor to destroy + */ +extern void mspack_destroy_lit_decompressor(struct mslit_decompressor *self); + + +/** Creates a new HLP compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mshlp_compressor or NULL + */ +extern struct mshlp_compressor * + mspack_create_hlp_compressor(struct mspack_system *sys); + +/** Creates a new HLP decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mshlp_decompressor or NULL + */ +extern struct mshlp_decompressor * + mspack_create_hlp_decompressor(struct mspack_system *sys); + +/** Destroys an existing hlp compressor. + * @param self the #mshlp_compressor to destroy + */ +extern void mspack_destroy_hlp_compressor(struct mshlp_compressor *self); + +/** Destroys an existing hlp decompressor. + * @param self the #mshlp_decompressor to destroy + */ +extern void mspack_destroy_hlp_decompressor(struct mshlp_decompressor *self); + + +/** Creates a new SZDD compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #msszdd_compressor or NULL + */ +extern struct msszdd_compressor * + mspack_create_szdd_compressor(struct mspack_system *sys); + +/** Creates a new SZDD decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #msszdd_decompressor or NULL + */ +extern struct msszdd_decompressor * + mspack_create_szdd_decompressor(struct mspack_system *sys); + +/** Destroys an existing SZDD compressor. + * @param self the #msszdd_compressor to destroy + */ +extern void mspack_destroy_szdd_compressor(struct msszdd_compressor *self); + +/** Destroys an existing SZDD decompressor. + * @param self the #msszdd_decompressor to destroy + */ +extern void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *self); + + +/** Creates a new KWAJ compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mskwaj_compressor or NULL + */ +extern struct mskwaj_compressor * + mspack_create_kwaj_compressor(struct mspack_system *sys); + +/** Creates a new KWAJ decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mskwaj_decompressor or NULL + */ +extern struct mskwaj_decompressor * + mspack_create_kwaj_decompressor(struct mspack_system *sys); + +/** Destroys an existing KWAJ compressor. + * @param self the #mskwaj_compressor to destroy + */ +extern void mspack_destroy_kwaj_compressor(struct mskwaj_compressor *self); + +/** Destroys an existing KWAJ decompressor. + * @param self the #mskwaj_decompressor to destroy + */ +extern void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *self); + + +/* --- support for .CAB (MS Cabinet) file format --------------------------- */ + +/** + * A structure which represents a single cabinet file. + * + * All fields are READ ONLY. + * + * If this cabinet is part of a merged cabinet set, the #files and #folders + * fields are common to all cabinets in the set, and will be identical. + * + * @see mscab_decompressor::open(), mscab_decompressor::close(), + * mscab_decompressor::search() + */ +struct mscabd_cabinet { + /** + * The next cabinet in a chained list, if this cabinet was opened with + * mscab_decompressor::search(). May be NULL to mark the end of the + * list. + */ + struct mscabd_cabinet *next; + + /** + * The filename of the cabinet. More correctly, the filename of the + * physical file that the cabinet resides in. This is given by the + * library user and may be in any format. + */ + const char *filename; + + /** The file offset of cabinet within the physical file it resides in. */ + off_t base_offset; + + /** The length of the cabinet file in bytes. */ + unsigned int length; + + /** The previous cabinet in a cabinet set, or NULL. */ + struct mscabd_cabinet *prevcab; + + /** The next cabinet in a cabinet set, or NULL. */ + struct mscabd_cabinet *nextcab; + + /** The filename of the previous cabinet in a cabinet set, or NULL. */ + char *prevname; + + /** The filename of the next cabinet in a cabinet set, or NULL. */ + char *nextname; + + /** The name of the disk containing the previous cabinet in a cabinet + * set, or NULL. + */ + char *previnfo; + + /** The name of the disk containing the next cabinet in a cabinet set, + * or NULL. + */ + char *nextinfo; + + /** A list of all files in the cabinet or cabinet set. */ + struct mscabd_file *files; + + /** A list of all folders in the cabinet or cabinet set. */ + struct mscabd_folder *folders; + + /** + * The set ID of the cabinet. All cabinets in the same set should have + * the same set ID. + */ + unsigned short set_id; + + /** + * The index number of the cabinet within the set. Numbering should + * start from 0 for the first cabinet in the set, and increment by 1 for + * each following cabinet. + */ + unsigned short set_index; + + /** + * The number of bytes reserved in the header area of the cabinet. + * + * If this is non-zero and flags has MSCAB_HDR_RESV set, this data can + * be read by the calling application. It is of the given length, + * located at offset (base_offset + MSCAB_HDR_RESV_OFFSET) in the + * cabinet file. + * + * @see flags + */ + unsigned short header_resv; + + /** + * Header flags. + * + * - MSCAB_HDR_PREVCAB indicates the cabinet is part of a cabinet set, and + * has a predecessor cabinet. + * - MSCAB_HDR_NEXTCAB indicates the cabinet is part of a cabinet set, and + * has a successor cabinet. + * - MSCAB_HDR_RESV indicates the cabinet has reserved header space. + * + * @see prevname, previnfo, nextname, nextinfo, header_resv + */ + int flags; +}; + +/** Offset from start of cabinet to the reserved header data (if present). */ +#define MSCAB_HDR_RESV_OFFSET (0x28) + +/** Cabinet header flag: cabinet has a predecessor */ +#define MSCAB_HDR_PREVCAB (0x01) +/** Cabinet header flag: cabinet has a successor */ +#define MSCAB_HDR_NEXTCAB (0x02) +/** Cabinet header flag: cabinet has reserved header space */ +#define MSCAB_HDR_RESV (0x04) + +/** + * A structure which represents a single folder in a cabinet or cabinet set. + * + * All fields are READ ONLY. + * + * A folder is a single compressed stream of data. When uncompressed, it + * holds the data of one or more files. A folder may be split across more + * than one cabinet. + */ +struct mscabd_folder { + /** + * A pointer to the next folder in this cabinet or cabinet set, or NULL + * if this is the final folder. + */ + struct mscabd_folder *next; + + /** + * The compression format used by this folder. + * + * The macro MSCABD_COMP_METHOD() should be used on this field to get + * the algorithm used. The macro MSCABD_COMP_LEVEL() should be used to get + * the "compression level". + * + * @see MSCABD_COMP_METHOD(), MSCABD_COMP_LEVEL() + */ + int comp_type; + + /** + * The total number of data blocks used by this folder. This includes + * data blocks present in other files, if this folder spans more than + * one cabinet. + */ + unsigned int num_blocks; +}; + +/** + * Returns the compression method used by a folder. + * + * @param comp_type a mscabd_folder::comp_type value + * @return one of #MSCAB_COMP_NONE, #MSCAB_COMP_MSZIP, #MSCAB_COMP_QUANTUM + * or #MSCAB_COMP_LZX + */ +#define MSCABD_COMP_METHOD(comp_type) ((comp_type) & 0x0F) +/** + * Returns the compression level used by a folder. + * + * @param comp_type a mscabd_folder::comp_type value + * @return the compression level. This is only defined by LZX and Quantum + * compression + */ +#define MSCABD_COMP_LEVEL(comp_type) (((comp_type) >> 8) & 0x1F) + +/** Compression mode: no compression. */ +#define MSCAB_COMP_NONE (0) +/** Compression mode: MSZIP (deflate) compression. */ +#define MSCAB_COMP_MSZIP (1) +/** Compression mode: Quantum compression */ +#define MSCAB_COMP_QUANTUM (2) +/** Compression mode: LZX compression */ +#define MSCAB_COMP_LZX (3) + +/** + * A structure which represents a single file in a cabinet or cabinet set. + * + * All fields are READ ONLY. + */ +struct mscabd_file { + /** + * The next file in the cabinet or cabinet set, or NULL if this is the + * final file. + */ + struct mscabd_file *next; + + /** + * The filename of the file. + * + * A null terminated string of up to 255 bytes in length, it may be in + * either ISO-8859-1 or UTF8 format, depending on the file attributes. + * + * @see attribs + */ + char *filename; + + /** The uncompressed length of the file, in bytes. */ + unsigned int length; + + /** + * File attributes. + * + * The following attributes are defined: + * - #MSCAB_ATTRIB_RDONLY indicates the file is write protected. + * - #MSCAB_ATTRIB_HIDDEN indicates the file is hidden. + * - #MSCAB_ATTRIB_SYSTEM indicates the file is a operating system file. + * - #MSCAB_ATTRIB_ARCH indicates the file is "archived". + * - #MSCAB_ATTRIB_EXEC indicates the file is an executable program. + * - #MSCAB_ATTRIB_UTF_NAME indicates the filename is in UTF8 format rather + * than ISO-8859-1. + */ + int attribs; + + /** File's last modified time, hour field. */ + char time_h; + /** File's last modified time, minute field. */ + char time_m; + /** File's last modified time, second field. */ + char time_s; + + /** File's last modified date, day field. */ + char date_d; + /** File's last modified date, month field. */ + char date_m; + /** File's last modified date, year field. */ + int date_y; + + /** A pointer to the folder that contains this file. */ + struct mscabd_folder *folder; + + /** The uncompressed offset of this file in its folder. */ + unsigned int offset; +}; + +/** mscabd_file::attribs attribute: file is read-only. */ +#define MSCAB_ATTRIB_RDONLY (0x01) +/** mscabd_file::attribs attribute: file is hidden. */ +#define MSCAB_ATTRIB_HIDDEN (0x02) +/** mscabd_file::attribs attribute: file is an operating system file. */ +#define MSCAB_ATTRIB_SYSTEM (0x04) +/** mscabd_file::attribs attribute: file is "archived". */ +#define MSCAB_ATTRIB_ARCH (0x20) +/** mscabd_file::attribs attribute: file is an executable program. */ +#define MSCAB_ATTRIB_EXEC (0x40) +/** mscabd_file::attribs attribute: filename is UTF8, not ISO-8859-1. */ +#define MSCAB_ATTRIB_UTF_NAME (0x80) + +/** mscab_decompressor::set_param() parameter: search buffer size. */ +#define MSCABD_PARAM_SEARCHBUF (0) +/** mscab_decompressor::set_param() parameter: repair MS-ZIP streams? */ +#define MSCABD_PARAM_FIXMSZIP (1) +/** mscab_decompressor::set_param() parameter: size of decompression buffer */ +#define MSCABD_PARAM_DECOMPBUF (2) + +/** TODO */ +struct mscab_compressor { + int dummy; +}; + +/** + * A decompressor for .CAB (Microsoft Cabinet) files + * + * All fields are READ ONLY. + * + * @see mspack_create_cab_decompressor(), mspack_destroy_cab_decompressor() + */ +struct mscab_decompressor { + /** + * Opens a cabinet file and reads its contents. + * + * If the file opened is a valid cabinet file, all headers will be read + * and a mscabd_cabinet structure will be returned, with a full list of + * folders and files. + * + * In the case of an error occuring, NULL is returned and the error code + * is available from last_error(). + * + * The filename pointer should be considered "in use" until close() is + * called on the cabinet. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param filename the filename of the cabinet file. This is passed + * directly to mspack_system::open(). + * @return a pointer to a mscabd_cabinet structure, or NULL on failure + * @see close(), search(), last_error() + */ + struct mscabd_cabinet * (*open) (struct mscab_decompressor *self, + const char *filename); + + /** + * Closes a previously opened cabinet or cabinet set. + * + * This closes a cabinet, all cabinets associated with it via the + * mscabd_cabinet::next, mscabd_cabinet::prevcab and + * mscabd_cabinet::nextcab pointers, and all folders and files. All + * memory used by these entities is freed. + * + * The cabinet pointer is now invalid and cannot be used again. All + * mscabd_folder and mscabd_file pointers from that cabinet or cabinet + * set are also now invalid, and cannot be used again. + * + * If the cabinet pointer given was created using search(), it MUST be + * the cabinet pointer returned by search() and not one of the later + * cabinet pointers further along the mscabd_cabinet::next chain. + + * If extra cabinets have been added using append() or prepend(), these + * will all be freed, even if the cabinet pointer given is not the first + * cabinet in the set. Do NOT close() more than one cabinet in the set. + * + * The mscabd_cabinet::filename is not freed by the library, as it is + * not allocated by the library. The caller should free this itself if + * necessary, before it is lost forever. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param cab the cabinet to close + * @see open(), search(), append(), prepend() + */ + void (*close)(struct mscab_decompressor *self, + struct mscabd_cabinet *cab); + + /** + * Searches a regular file for embedded cabinets. + * + * This opens a normal file with the given filename and will search the + * entire file for embedded cabinet files + * + * If any cabinets are found, the equivalent of open() is called on each + * potential cabinet file at the offset it was found. All successfully + * open()ed cabinets are kept in a list. + * + * The first cabinet found will be returned directly as the result of + * this method. Any further cabinets found will be chained in a list + * using the mscabd_cabinet::next field. + * + * In the case of an error occuring anywhere other than the simulated + * open(), NULL is returned and the error code is available from + * last_error(). + * + * If no error occurs, but no cabinets can be found in the file, NULL is + * returned and last_error() returns MSPACK_ERR_OK. + * + * The filename pointer should be considered in use until close() is + * called on the cabinet. + * + * close() should only be called on the result of search(), not on any + * subsequent cabinets in the mscabd_cabinet::next chain. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param filename the filename of the file to search for cabinets. This + * is passed directly to mspack_system::open(). + * @return a pointer to a mscabd_cabinet structure, or NULL + * @see close(), open(), last_error() + */ + struct mscabd_cabinet * (*search) (struct mscab_decompressor *self, + const char *filename); + + /** + * Appends one mscabd_cabinet to another, forming or extending a cabinet + * set. + * + * This will attempt to append one cabinet to another such that + * (cab->nextcab == nextcab) && (nextcab->prevcab == cab) and + * any folders split between the two cabinets are merged. + * + * The cabinets MUST be part of a cabinet set -- a cabinet set is a + * cabinet that spans more than one physical cabinet file on disk -- and + * must be appropriately matched. + * + * It can be determined if a cabinet has further parts to load by + * examining the mscabd_cabinet::flags field: + * + * - if (flags & MSCAB_HDR_PREVCAB) is non-zero, there is a + * predecessor cabinet to open() and prepend(). Its MS-DOS + * case-insensitive filename is mscabd_cabinet::prevname + * - if (flags & MSCAB_HDR_NEXTCAB) is non-zero, there is a + * successor cabinet to open() and append(). Its MS-DOS case-insensitive + * filename is mscabd_cabinet::nextname + * + * If the cabinets do not match, an error code will be returned. Neither + * cabinet has been altered, and both should be closed seperately. + * + * Files and folders in a cabinet set are a single entity. All cabinets + * in a set use the same file list, which is updated as cabinets in the + * set are added. All pointers to mscabd_folder and mscabd_file + * structures in either cabinet must be discarded and re-obtained after + * merging. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param cab the cabinet which will be appended to, + * predecessor of nextcab + * @param nextcab the cabinet which will be appended, + * successor of cab + * @return an error code, or MSPACK_ERR_OK if successful + * @see prepend(), open(), close() + */ + int (*append) (struct mscab_decompressor *self, + struct mscabd_cabinet *cab, + struct mscabd_cabinet *nextcab); + + /** + * Prepends one mscabd_cabinet to another, forming or extending a + * cabinet set. + * + * This will attempt to prepend one cabinet to another, such that + * (cab->prevcab == prevcab) && (prevcab->nextcab == cab). In + * all other respects, it is identical to append(). See append() for the + * full documentation. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param cab the cabinet which will be prepended to, + * successor of prevcab + * @param prevcab the cabinet which will be prepended, + * predecessor of cab + * @return an error code, or MSPACK_ERR_OK if successful + * @see append(), open(), close() + */ + int (*prepend) (struct mscab_decompressor *self, + struct mscabd_cabinet *cab, + struct mscabd_cabinet *prevcab); + + /** + * Extracts a file from a cabinet or cabinet set. + * + * This extracts a compressed file in a cabinet and writes it to the given + * filename. + * + * The MS-DOS filename of the file, mscabd_file::filename, is NOT USED + * by extract(). The caller must examine this MS-DOS filename, copy and + * change it as necessary, create directories as necessary, and provide + * the correct filename as a parameter, which will be passed unchanged + * to the decompressor's mspack_system::open() + * + * If the file belongs to a split folder in a multi-part cabinet set, + * and not enough parts of the cabinet set have been loaded and appended + * or prepended, an error will be returned immediately. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param file the file to be decompressed + * @param filename the filename of the file being written to + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*extract)(struct mscab_decompressor *self, + struct mscabd_file *file, + const char *filename); + + /** + * Sets a CAB decompression engine parameter. + * + * The following parameters are defined: + * - #MSCABD_PARAM_SEARCHBUF: How many bytes should be allocated as a + * buffer when using search()? The minimum value is 4. The default + * value is 32768. + * - #MSCABD_PARAM_FIXMSZIP: If non-zero, extract() will ignore bad + * checksums and recover from decompression errors in MS-ZIP + * compressed folders. The default value is 0 (don't recover). + * - #MSCABD_PARAM_DECOMPBUF: How many bytes should be used as an input + * bit buffer by decompressors? The minimum value is 4. The default + * value is 4096. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param param the parameter to set + * @param value the value to set the parameter to + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there + * is a problem with either parameter or value. + * @see search(), extract() + */ + int (*set_param)(struct mscab_decompressor *self, + int param, + int value); + + /** + * Returns the error code set by the most recently called method. + * + * This is useful for open() and search(), which do not return an error + * code directly. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @return the most recent error code + * @see open(), search() + */ + int (*last_error)(struct mscab_decompressor *self); +}; + +/* --- support for .CHM (HTMLHelp) file format ----------------------------- */ + +/** + * A structure which represents a file to be placed in a CHM helpfile. + * + * A contiguous array of these structures should be passed to + * mschm_compressor::generate(). The array list is terminated with an + * entry whose mschmc_file::section field is set to #MSCHMC_ENDLIST, the + * other fields in this entry are ignored. + */ +struct mschmc_file { + /** One of #MSCHMC_ENDLIST, #MSCHMC_UNCOMP or #MSCHMC_MSCOMP. */ + int section; + + /** The filename of the source file that will be added to the CHM. This + * is passed directly to mspack_system::open(). */ + const char *filename; + + /** The full path and filename of the file within the CHM helpfile, a + * UTF-1 encoded null-terminated string. */ + char *chm_filename; + + /** The length of the file, in bytes. This will be adhered to strictly + * and a read error will be issued if this many bytes cannot be read + * from the real file at CHM generation time. */ + off_t length; +}; + +/** + * A structure which represents a section of a CHM helpfile. + * + * All fields are READ ONLY. + * + * Not used directly, but used as a generic base type for + * mschmd_sec_uncompressed and mschmd_sec_mscompressed. + */ +struct mschmd_section { + /** A pointer to the CHM helpfile that contains this section. */ + struct mschmd_header *chm; + + /** + * The section ID. Either 0 for the uncompressed section + * mschmd_sec_uncompressed, or 1 for the LZX compressed section + * mschmd_sec_mscompressed. No other section IDs are known. + */ + unsigned int id; +}; + +/** + * A structure which represents the uncompressed section of a CHM helpfile. + * + * All fields are READ ONLY. + */ +struct mschmd_sec_uncompressed { + /** Generic section data. */ + struct mschmd_section base; + + /** The file offset of where this section begins in the CHM helpfile. */ + off_t offset; +}; + +/** + * A structure which represents the LZX compressed section of a CHM helpfile. + * + * All fields are READ ONLY. + */ +struct mschmd_sec_mscompressed { + /** Generic section data. */ + struct mschmd_section base; + + /** A pointer to the meta-file which represents all LZX compressed data. */ + struct mschmd_file *content; + + /** A pointer to the file which contains the LZX control data. */ + struct mschmd_file *control; + + /** A pointer to the file which contains the LZX reset table. */ + struct mschmd_file *rtable; + + /** A pointer to the file which contains the LZX span information. + * Available only in CHM decoder version 2 and above. + */ + struct mschmd_file *spaninfo; +}; + +/** + * A structure which represents a CHM helpfile. + * + * All fields are READ ONLY. + */ +struct mschmd_header { + /** The version of the CHM file format used in this file. */ + unsigned int version; + + /** + * The "timestamp" of the CHM helpfile. + * + * It is the lower 32 bits of a 64-bit value representing the number of + * centiseconds since 1601-01-01 00:00:00 UTC, plus 42. It is not useful + * as a timestamp, but it is useful as a semi-unique ID. + */ + unsigned int timestamp; + + /** + * The default Language and Country ID (LCID) of the user who ran the + * HTMLHelp Compiler. This is not the language of the CHM file itself. + */ + unsigned int language; + + /** + * The filename of the CHM helpfile. This is given by the library user + * and may be in any format. + */ + const char *filename; + + /** The length of the CHM helpfile, in bytes. */ + off_t length; + + /** A list of all non-system files in the CHM helpfile. */ + struct mschmd_file *files; + + /** + * A list of all system files in the CHM helpfile. + * + * System files are files which begin with "::". They are meta-files + * generated by the CHM creation process. + */ + struct mschmd_file *sysfiles; + + /** The section 0 (uncompressed) data in this CHM helpfile. */ + struct mschmd_sec_uncompressed sec0; + + /** The section 1 (MSCompressed) data in this CHM helpfile. */ + struct mschmd_sec_mscompressed sec1; + + /** The file offset of the first PMGL/PMGI directory chunk. */ + off_t dir_offset; + + /** The number of PMGL/PMGI directory chunks in this CHM helpfile. */ + unsigned int num_chunks; + + /** The size of each PMGL/PMGI chunk, in bytes. */ + unsigned int chunk_size; + + /** The "density" of the quick-reference section in PMGL/PMGI chunks. */ + unsigned int density; + + /** The depth of the index tree. + * + * - if 1, there are no PMGI chunks, only PMGL chunks. + * - if 2, there is 1 PMGI chunk. All chunk indices point to PMGL chunks. + * - if 3, the root PMGI chunk points to secondary PMGI chunks, which in + * turn point to PMGL chunks. + * - and so on... + */ + unsigned int depth; + + /** + * The number of the root PMGI chunk. + * + * If there is no index in the CHM helpfile, this will be 0xFFFFFFFF. + */ + unsigned int index_root; + + /** + * The number of the first PMGL chunk. Usually zero. + * Available only in CHM decoder version 2 and above. + */ + unsigned int first_pmgl; + + /** + * The number of the last PMGL chunk. Usually num_chunks-1. + * Available only in CHM decoder version 2 and above. + */ + unsigned int last_pmgl; + + /** + * A cache of loaded chunks, filled in by mschm_decoder::fast_find(). + * Available only in CHM decoder version 2 and above. + */ + unsigned char **chunk_cache; +}; + +/** + * A structure which represents a file stored in a CHM helpfile. + * + * All fields are READ ONLY. + */ +struct mschmd_file { + /** + * A pointer to the next file in the list, or NULL if this is the final + * file. + */ + struct mschmd_file *next; + + /** + * A pointer to the section that this file is located in. Indirectly, + * it also points to the CHM helpfile the file is located in. + */ + struct mschmd_section *section; + + /** The offset within the section data that this file is located at. */ + off_t offset; + + /** The length of this file, in bytes */ + off_t length; + + /** The filename of this file -- a null terminated string in UTF-8. */ + char *filename; +}; + +/** mschmc_file::section value: end of CHM file list */ +#define MSCHMC_ENDLIST (0) +/** mschmc_file::section value: this file is in the Uncompressed section */ +#define MSCHMC_UNCOMP (1) +/** mschmc_file::section value: this file is in the MSCompressed section */ +#define MSCHMC_MSCOMP (2) + +/** mschm_compressor::set_param() parameter: "timestamp" header */ +#define MSCHMC_PARAM_TIMESTAMP (0) +/** mschm_compressor::set_param() parameter: "language" header */ +#define MSCHMC_PARAM_LANGUAGE (1) +/** mschm_compressor::set_param() parameter: LZX window size */ +#define MSCHMC_PARAM_LZXWINDOW (2) +/** mschm_compressor::set_param() parameter: intra-chunk quickref density */ +#define MSCHMC_PARAM_DENSITY (3) +/** mschm_compressor::set_param() parameter: whether to create indices */ +#define MSCHMC_PARAM_INDEX (4) + +/** + * A compressor for .CHM (Microsoft HTMLHelp) files. + * + * All fields are READ ONLY. + * + * @see mspack_create_chm_compressor(), mspack_destroy_chm_compressor() + */ +struct mschm_compressor { + /** + * Generates a CHM help file. + * + * The help file will contain up to two sections, an Uncompressed + * section and potentially an MSCompressed (LZX compressed) + * section. + * + * While the contents listing of a CHM file is always in lexical order, + * the file list passed in will be taken as the correct order for files + * within the sections. It is in your interest to place similar files + * together for better compression. + * + * There are two modes of generation, to use a temporary file or not to + * use one. See use_temporary_file() for the behaviour of generate() in + * these two different modes. + * + * @param self a self-referential pointer to the mschm_compressor + * instance being called + * @param file_list an array of mschmc_file structures, terminated + * with an entry whose mschmc_file::section field is + * #MSCHMC_ENDLIST. The order of the list is + * preserved within each section. The length of any + * mschmc_file::chm_filename string cannot exceed + * roughly 4096 bytes. Each source file must be able + * to supply as many bytes as given in the + * mschmc_file::length field. + * @param output_file the file to write the generated CHM helpfile to. + * This is passed directly to mspack_system::open() + * @return an error code, or MSPACK_ERR_OK if successful + * @see use_temporary_file() set_param() + */ + int (*generate)(struct mschm_compressor *self, + struct mschmc_file file_list[], + const char *output_file); + + /** + * Specifies whether a temporary file is used during CHM generation. + * + * The CHM file format includes data about the compressed section (such + * as its overall size) that is stored in the output CHM file prior to + * the compressed section itself. This unavoidably requires that the + * compressed section has to be generated, before these details can be + * set. There are several ways this can be handled. Firstly, the + * compressed section could be generated entirely in memory before + * writing any of the output CHM file. This approach is not used in + * libmspack, as the compressed section can exceed the addressable + * memory space on most architectures. + * + * libmspack has two options, either to write these unknowable sections + * with blank data, generate the compressed section, then re-open the + * output file for update once the compressed section has been + * completed, or to write the compressed section to a temporary file, + * then write the entire output file at once, performing a simple + * file-to-file copy for the compressed section. + * + * The simple solution of buffering the entire compressed section in + * memory can still be used, if desired. As the temporary file's + * filename is passed directly to mspack_system::open(), it is possible + * for a custom mspack_system implementation to hold this file in memory, + * without writing to a disk. + * + * If a temporary file is set, generate() performs the following + * sequence of events: the temporary file is opened for writing, the + * compression algorithm writes to the temporary file, the temporary + * file is closed. Then the output file is opened for writing and the + * temporary file is re-opened for reading. The output file is written + * and the temporary file is read from. Both files are then closed. The + * temporary file itself is not deleted. If that is desired, the + * temporary file should be deleted after the completion of generate(), + * if it exists. + * + * If a temporary file is set not to be used, generate() performs the + * following sequence of events: the output file is opened for writing, + * then it is written and closed. The output file is then re-opened for + * update, the appropriate sections are seek()ed to and re-written, then + * the output file is closed. + * + * @param self a self-referential pointer to the + * mschm_compressor instance being called + * @param use_temp_file non-zero if the temporary file should be used, + * zero if the temporary file should not be used. + * @param temp_file a file to temporarily write compressed data to, + * before opening it for reading and copying the + * contents to the output file. This is passed + * directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + * @see generate() + */ + int (*use_temporary_file)(struct mschm_compressor *self, + int use_temp_file, + const char *temp_file); + /** + * Sets a CHM compression engine parameter. + * + * The following parameters are defined: + + * - #MSCHMC_PARAM_TIMESTAMP: Sets the "timestamp" of the CHM file + * generated. This is not a timestamp, see mschmd_header::timestamp + * for a description. If this timestamp is 0, generate() will use its + * own algorithm for making a unique ID, based on the lengths and + * names of files in the CHM itself. Defaults to 0, any value between + * 0 and (2^32)-1 is valid. + * - #MSCHMC_PARAM_LANGUAGE: Sets the "language" of the CHM file + * generated. This is not the language used in the CHM file, but the + * language setting of the user who ran the HTMLHelp compiler. It + * defaults to 0x0409. The valid range is between 0x0000 and 0x7F7F. + * - #MSCHMC_PARAM_LZXWINDOW: Sets the size of the LZX history window, + * which is also the interval at which the compressed data stream can be + * randomly accessed. The value is not a size in bytes, but a power of + * two. The default value is 16 (which makes the window 2^16 bytes, or + * 64 kilobytes), the valid range is from 15 (32 kilobytes) to 21 (2 + * megabytes). + * - #MSCHMC_PARAM_DENSITY: Sets the "density" of quick reference + * entries stored at the end of directory listing chunk. Each chunk is + * 4096 bytes in size, and contains as many file entries as there is + * room for. At the other end of the chunk, a list of "quick reference" + * pointers is included. The offset of every 'N'th file entry is given a + * quick reference, where N = (2^density) + 1. The default density is + * 2. The smallest density is 0 (N=2), the maximum is 10 (N=1025). As + * each file entry requires at least 5 bytes, the maximum number of + * entries in a single chunk is roughly 800, so the maximum value 10 + * can be used to indicate there are no quickrefs at all. + * - #MSCHMC_PARAM_INDEX: Sets whether or not to include quick lookup + * index chunk(s), in addition to normal directory listing chunks. A + * value of zero means no index chunks will be created, a non-zero value + * means index chunks will be created. The default is zero, "don't + * create an index". + * + * @param self a self-referential pointer to the mschm_compressor + * instance being called + * @param param the parameter to set + * @param value the value to set the parameter to + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there + * is a problem with either parameter or value. + * @see generate() + */ + int (*set_param)(struct mschm_compressor *self, + int param, + unsigned int value); + + /** + * Returns the error code set by the most recently called method. + * + * @param self a self-referential pointer to the mschm_compressor + * instance being called + * @return the most recent error code + * @see set_param(), generate() + */ + int (*last_error)(struct mschm_compressor *self); +}; + +/** + * A decompressor for .CHM (Microsoft HTMLHelp) files + * + * All fields are READ ONLY. + * + * @see mspack_create_chm_decompressor(), mspack_destroy_chm_decompressor() + */ +struct mschm_decompressor { + /** + * Opens a CHM helpfile and reads its contents. + * + * If the file opened is a valid CHM helpfile, all headers will be read + * and a mschmd_header structure will be returned, with a full list of + * files. + * + * In the case of an error occuring, NULL is returned and the error code + * is available from last_error(). + * + * The filename pointer should be considered "in use" until close() is + * called on the CHM helpfile. + * + * @param self a self-referential pointer to the mschm_decompressor + * instance being called + * @param filename the filename of the CHM helpfile. This is passed + * directly to mspack_system::open(). + * @return a pointer to a mschmd_header structure, or NULL on failure + * @see close() + */ + struct mschmd_header *(*open)(struct mschm_decompressor *self, + const char *filename); + + /** + * Closes a previously opened CHM helpfile. + * + * This closes a CHM helpfile, frees the mschmd_header and all + * mschmd_file structures associated with it (if any). This works on + * both helpfiles opened with open() and helpfiles opened with + * fast_open(). + * + * The CHM header pointer is now invalid and cannot be used again. All + * mschmd_file pointers referencing that CHM are also now invalid, and + * cannot be used again. + * + * @param self a self-referential pointer to the mschm_decompressor + * instance being called + * @param chm the CHM helpfile to close + * @see open(), fast_open() + */ + void (*close)(struct mschm_decompressor *self, + struct mschmd_header *chm); + + /** + * Extracts a file from a CHM helpfile. + * + * This extracts a file from a CHM helpfile and writes it to the given + * filename. The filename of the file, mscabd_file::filename, is not + * used by extract(), but can be used by the caller as a guide for + * constructing an appropriate filename. + * + * This method works both with files found in the mschmd_header::files + * and mschmd_header::sysfiles list and mschmd_file structures generated + * on the fly by fast_find(). + * + * @param self a self-referential pointer to the mschm_decompressor + * instance being called + * @param file the file to be decompressed + * @param filename the filename of the file being written to + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*extract)(struct mschm_decompressor *self, + struct mschmd_file *file, + const char *filename); + + /** + * Returns the error code set by the most recently called method. + * + * This is useful for open() and fast_open(), which do not return an + * error code directly. + * + * @param self a self-referential pointer to the mschm_decompressor + * instance being called + * @return the most recent error code + * @see open(), extract() + */ + int (*last_error)(struct mschm_decompressor *self); + + /** + * Opens a CHM helpfile quickly. + * + * If the file opened is a valid CHM helpfile, only essential headers + * will be read. A mschmd_header structure will be still be returned, as + * with open(), but the mschmd_header::files field will be NULL. No + * files details will be automatically read. The fast_find() method + * must be used to obtain file details. + * + * In the case of an error occuring, NULL is returned and the error code + * is available from last_error(). + * + * The filename pointer should be considered "in use" until close() is + * called on the CHM helpfile. + * + * @param self a self-referential pointer to the mschm_decompressor + * instance being called + * @param filename the filename of the CHM helpfile. This is passed + * directly to mspack_system::open(). + * @return a pointer to a mschmd_header structure, or NULL on failure + * @see open(), close(), fast_find(), extract() + */ + struct mschmd_header *(*fast_open)(struct mschm_decompressor *self, + const char *filename); + + /** + * Finds file details quickly. + * + * Instead of reading all CHM helpfile headers and building a list of + * files, fast_open() and fast_find() are intended for finding file + * details only when they are needed. The CHM file format includes an + * on-disk file index to allow this. + * + * Given a case-sensitive filename, fast_find() will search the on-disk + * index for that file. + * + * If the file was found, the caller-provided mschmd_file structure will + * be filled out like so: + * - section: the correct value for the found file + * - offset: the correct value for the found file + * - length: the correct value for the found file + * - all other structure elements: NULL or 0 + * + * If the file was not found, MSPACK_ERR_OK will still be returned as the + * result, but the caller-provided structure will be filled out like so: + * - section: NULL + * - offset: 0 + * - length: 0 + * - all other structure elements: NULL or 0 + * + * This method is intended to be used in conjunction with CHM helpfiles + * opened with fast_open(), but it also works with helpfiles opened + * using the regular open(). + * + * @param self a self-referential pointer to the mschm_decompressor + * instance being called + * @param chm the CHM helpfile to search for the file + * @param filename the filename of the file to search for + * @param f_ptr a pointer to a caller-provded mschmd_file structure + * @param f_size sizeof(struct mschmd_file) + * @return an error code, or MSPACK_ERR_OK if successful + * @see open(), close(), fast_find(), extract() + */ + int (*fast_find)(struct mschm_decompressor *self, + struct mschmd_header *chm, + const char *filename, + struct mschmd_file *f_ptr, + int f_size); +}; + +/* --- support for .LIT (EBook) file format -------------------------------- */ + +/** TODO */ +struct mslit_compressor { + int dummy; +}; + +/** TODO */ +struct mslit_decompressor { + int dummy; +}; + + +/* --- support for .HLP (MS Help) file format ------------------------------ */ + +/** TODO */ +struct mshlp_compressor { + int dummy; +}; + +/** TODO */ +struct mshlp_decompressor { + int dummy; +}; + + +/* --- support for SZDD file format ---------------------------------------- */ + +/** msszdd_compressor::set_param() parameter: the missing character */ +#define MSSZDDC_PARAM_MISSINGCHAR (0) + +/** msszddd_header::format value - a regular SZDD file */ +#define MSSZDD_FMT_NORMAL (0) + +/** msszddd_header::format value - a special QBasic SZDD file */ +#define MSSZDD_FMT_QBASIC (1) + +/** + * A structure which represents an SZDD compressed file. + * + * All fields are READ ONLY. + */ +struct msszddd_header { + /** The file format; either #MSSZDD_FMT_NORMAL or #MSSZDD_FMT_QBASIC */ + int format; + + /** The amount of data in the SZDD file once uncompressed. */ + off_t length; + + /** + * The last character in the filename, traditionally replaced with an + * underscore to show the file is compressed. The null character is used + * to show that this character has not been stored (e.g. because the + * filename is not known). Generally, only characters that may appear in + * an MS-DOS filename (except ".") are valid. + */ + char missing_char; +}; + +/** + * A compressor for the SZDD file format. + * + * All fields are READ ONLY. + * + * @see mspack_create_szdd_compressor(), mspack_destroy_szdd_compressor() + */ +struct msszdd_compressor { + /** + * Reads an input file and creates a compressed output file in the + * SZDD compressed file format. The SZDD compression format is quick + * but gives poor compression. It is possible for the compressed output + * file to be larger than the input file. + * + * Conventionally, SZDD compressed files have the final character in + * their filename replaced with an underscore, to show they are + * compressed. The missing character is stored in the compressed file + * itself. This is due to the restricted filename conventions of MS-DOS, + * most operating systems, such as UNIX, simply append another file + * extension to the existing filename. As mspack does not deal with + * filenames, this is left up to you. If you wish to set the missing + * character stored in the file header, use set_param() with the + * #MSSZDDC_PARAM_MISSINGCHAR parameter. + * + * "Stream" compression (where the length of the input data is not + * known) is not possible. The length of the input data is stored in the + * header of the SZDD file and must therefore be known before any data + * is compressed. Due to technical limitations of the file format, the + * maximum size of uncompressed file that will be accepted is 2147483647 + * bytes. + * + * @param self a self-referential pointer to the msszdd_compressor + * instance being called + * @param input the name of the file to compressed. This is passed + * passed directly to mspack_system::open() + * @param output the name of the file to write compressed data to. + * This is passed directly to mspack_system::open(). + * @param length the length of the uncompressed file, or -1 to indicate + * that this should be determined automatically by using + * mspack_system::seek() on the input file. + * @return an error code, or MSPACK_ERR_OK if successful + * @see set_param() + */ + int (*compress)(struct msszdd_compressor *self, + const char *input, + const char *output, + off_t length); + + /** + * Sets an SZDD compression engine parameter. + * + * The following parameters are defined: + + * - #MSSZDDC_PARAM_CHARACTER: the "missing character", the last character + * in the uncompressed file's filename, which is traditionally replaced + * with an underscore to show the file is compressed. Traditionally, + * this can only be a character that is a valid part of an MS-DOS, + * filename, but libmspack permits any character between 0x00 and 0xFF + * to be stored. 0x00 is the default, and it represents "no character + * stored". + * + * @param self a self-referential pointer to the msszdd_compressor + * instance being called + * @param param the parameter to set + * @param value the value to set the parameter to + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there + * is a problem with either parameter or value. + * @see compress() + */ + int (*set_param)(struct msszdd_compressor *self, + int param, + unsigned int value); + + /** + * Returns the error code set by the most recently called method. + * + * @param self a self-referential pointer to the msszdd_compressor + * instance being called + * @return the most recent error code + * @see compress() + */ + int (*last_error)(struct mschm_decompressor *self); +}; + +/** + * A decompressor for SZDD compressed files. + * + * All fields are READ ONLY. + * + * @see mspack_create_szdd_decompressor(), mspack_destroy_szdd_decompressor() + */ +struct msszdd_decompressor { + /** + * Opens a SZDD file and reads the header. + * + * If the file opened is a valid SZDD file, all headers will be read and + * a msszddd_header structure will be returned. + * + * In the case of an error occuring, NULL is returned and the error code + * is available from last_error(). + * + * The filename pointer should be considered "in use" until close() is + * called on the SZDD file. + * + * @param self a self-referential pointer to the msszdd_decompressor + * instance being called + * @param filename the filename of the SZDD compressed file. This is + * passed directly to mspack_system::open(). + * @return a pointer to a msszddd_header structure, or NULL on failure + * @see close() + */ + struct msszddd_header *(*open)(struct msszdd_decompressor *self, + const char *filename); + + /** + * Closes a previously opened SZDD file. + * + * This closes a SZDD file and frees the msszddd_header associated with + * it. + * + * The SZDD header pointer is now invalid and cannot be used again. + * + * @param self a self-referential pointer to the msszdd_decompressor + * instance being called + * @param szdd the SZDD file to close + * @see open() + */ + void (*close)(struct msszdd_decompressor *self, + struct msszddd_header *szdd); + + /** + * Extracts the compressed data from a SZDD file. + * + * This decompresses the compressed SZDD data stream and writes it to + * an output file. + * + * @param self a self-referential pointer to the msszdd_decompressor + * instance being called + * @param szdd the SZDD file to extract data from + * @param filename the filename to write the decompressed data to. This + * is passed directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*extract)(struct msszdd_decompressor *self, + struct msszddd_header *szdd, + const char *filename); + + /** + * Decompresses an SZDD file to an output file in one step. + * + * This opens an SZDD file as input, reads the header, then decompresses + * the compressed data immediately to an output file, finally closing + * both the input and output file. It is more convenient to use than + * open() then extract() then close(), if you do not need to know the + * SZDD output size or missing character. + * + * @param self a self-referential pointer to the msszdd_decompressor + * instance being called + * @param input the filename of the input SZDD file. This is passed + * directly to mspack_system::open(). + * @param output the filename to write the decompressed data to. This + * is passed directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*decompress)(struct msszdd_decompressor *self, + const char *input, + const char *output); + + /** + * Returns the error code set by the most recently called method. + * + * This is useful for open() which does not return an + * error code directly. + * + * @param self a self-referential pointer to the msszdd_decompressor + * instance being called + * @return the most recent error code + * @see open(), extract(), decompress() + */ + int (*last_error)(struct msszdd_decompressor *self); +}; + +/* --- support for KWAJ file format ---------------------------------------- */ + +/** mskwaj_compressor::set_param() parameter: compression type */ +#define MSKWAJC_PARAM_COMP_TYPE (0) + +/** mskwaj_compressor::set_param() parameter: include the length of the + * uncompressed file in the header? + */ +#define MSKWAJC_PARAM_INCLUDE_LENGTH (1) + +/** KWAJ compression type: no compression. */ +#define MSKWAJ_COMP_NONE (0) +/** KWAJ compression type: no compression, 0xFF XOR "encryption". */ +#define MSKWAJ_COMP_XOR (1) +/** KWAJ compression type: LZSS (same method as SZDD) */ +#define MSKWAJ_COMP_SZDD (2) +/** KWAJ compression type: LZ+Huffman compression */ +#define MSKWAJ_COMP_LZH (3) + +/** KWAJ optional header flag: decompressed file length is included */ +#define MSKWAJ_HDR_HASLENGTH (0x01) + +/** KWAJ optional header flag: unknown 2-byte structure is included */ +#define MSKWAJ_HDR_HASUNKNOWN1 (0x02) + +/** KWAJ optional header flag: unknown multi-sized structure is included */ +#define MSKWAJ_HDR_HASUNKNOWN2 (0x04) + +/** KWAJ optional header flag: file name (no extension) is included */ +#define MSKWAJ_HDR_HASFILENAME (0x08) + +/** KWAJ optional header flag: file extension is included */ +#define MSKWAJ_HDR_HASFILEEXT (0x10) + +/** KWAJ optional header flag: extra text is included */ +#define MSKWAJ_HDR_HASEXTRATEXT (0x20) + +/** + * A structure which represents an KWAJ compressed file. + * + * All fields are READ ONLY. + */ +struct mskwajd_header { + /** The compression type; should be one of #MSKWAJ_COMP_NONE, + * #MSKWAJ_COMP_XOR, #MSKWAJ_COMP_SZDD or #MSKWAJ_COMP_LZH + */ + unsigned short comp_type; + + /** The offset in the file where the compressed data stream begins */ + off_t data_offset; + + /** Flags indicating which optional headers were included. */ + int headers; + + /** The amount of uncompressed data in the file, or 0 if not present. */ + off_t length; + + /** output filename, or NULL if not present */ + char *filename; + + /** extra uncompressed data (usually text) in the header. + * This data can contain nulls so use extra_length to get the size. + */ + char *extra; + + /** length of extra uncompressed data in the header */ + unsigned short extra_length; +}; + +/** + * A compressor for the KWAJ file format. + * + * All fields are READ ONLY. + * + * @see mspack_create_kwaj_compressor(), mspack_destroy_kwaj_compressor() + */ +struct mskwaj_compressor { + /** + * Reads an input file and creates a compressed output file in the + * KWAJ compressed file format. The KWAJ compression format is quick + * but gives poor compression. It is possible for the compressed output + * file to be larger than the input file. + * + * @param self a self-referential pointer to the mskwaj_compressor + * instance being called + * @param input the name of the file to compressed. This is passed + * passed directly to mspack_system::open() + * @param output the name of the file to write compressed data to. + * This is passed directly to mspack_system::open(). + * @param length the length of the uncompressed file, or -1 to indicate + * that this should be determined automatically by using + * mspack_system::seek() on the input file. + * @return an error code, or MSPACK_ERR_OK if successful + * @see set_param() + */ + int (*compress)(struct mskwaj_compressor *self, + const char *input, + const char *output, + off_t length); + + /** + * Sets an KWAJ compression engine parameter. + * + * The following parameters are defined: + * + * - #MSKWAJC_PARAM_COMP_TYPE: the compression method to use. Must + * be one of #MSKWAJC_COMP_NONE, #MSKWAJC_COMP_XOR, #MSKWAJ_COMP_SZDD + * or #MSKWAJ_COMP_LZH. The default is #MSKWAJ_COMP_LZH. + * + * - #MSKWAJC_PARAM_INCLUDE_LENGTH: a boolean; should the compressed + * output file should include the uncompressed length of the input + * file in the header? This adds 4 bytes to the size of the output + * file. A value of zero says "no", non-zero says "yes". The default + * is "no". + * + * @param self a self-referential pointer to the mskwaj_compressor + * instance being called + * @param param the parameter to set + * @param value the value to set the parameter to + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there + * is a problem with either parameter or value. + * @see generate() + */ + int (*set_param)(struct mskwaj_compressor *self, + int param, + unsigned int value); + + + /** + * Sets the original filename of the file before compression, + * which will be stored in the header of the output file. + * + * The filename should be a null-terminated string, it must be an + * MS-DOS "8.3" type filename (up to 8 bytes for the filename, then + * optionally a "." and up to 3 bytes for a filename extension). + * + * If NULL is passed as the filename, no filename is included in the + * header. This is the default. + * + * @param self a self-referential pointer to the mskwaj_compressor + * instance being called + * @param filename the original filename to use + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if the + * filename is too long + */ + int (*set_filename)(struct mskwaj_compressor *self, + const char *filename); + + /** + * Sets arbitrary data that will be stored in the header of the + * output file, uncompressed. It can be up to roughly 64 kilobytes, + * as the overall size of the header must not exceed 65535 bytes. + * The data can contain null bytes if desired. + * + * If NULL is passed as the data pointer, or zero is passed as the + * length, no extra data is included in the header. This is the + * default. + * + * @param self a self-referential pointer to the mskwaj_compressor + * instance being called + * @param data a pointer to the data to be stored in the header + * @param bytes the length of the data in bytes + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS extra data + * is too long + */ + int (*set_extra_data)(struct mskwaj_compressor *self, + void *data, + size_t bytes); + + /** + * Returns the error code set by the most recently called method. + * + * @param self a self-referential pointer to the mskwaj_compressor + * instance being called + * @return the most recent error code + * @see compress() + */ + int (*last_error)(struct mschm_decompressor *self); +}; + +/** + * A decompressor for KWAJ compressed files. + * + * All fields are READ ONLY. + * + * @see mspack_create_kwaj_decompressor(), mspack_destroy_kwaj_decompressor() + */ +struct mskwaj_decompressor { + /** + * Opens a KWAJ file and reads the header. + * + * If the file opened is a valid KWAJ file, all headers will be read and + * a mskwajd_header structure will be returned. + * + * In the case of an error occuring, NULL is returned and the error code + * is available from last_error(). + * + * The filename pointer should be considered "in use" until close() is + * called on the KWAJ file. + * + * @param self a self-referential pointer to the mskwaj_decompressor + * instance being called + * @param filename the filename of the KWAJ compressed file. This is + * passed directly to mspack_system::open(). + * @return a pointer to a mskwajd_header structure, or NULL on failure + * @see close() + */ + struct mskwajd_header *(*open)(struct mskwaj_decompressor *self, + const char *filename); + + /** + * Closes a previously opened KWAJ file. + * + * This closes a KWAJ file and frees the mskwajd_header associated + * with it. The KWAJ header pointer is now invalid and cannot be + * used again. + * + * @param self a self-referential pointer to the mskwaj_decompressor + * instance being called + * @param kwaj the KWAJ file to close + * @see open() + */ + void (*close)(struct mskwaj_decompressor *self, + struct mskwajd_header *kwaj); + + /** + * Extracts the compressed data from a KWAJ file. + * + * This decompresses the compressed KWAJ data stream and writes it to + * an output file. + * + * @param self a self-referential pointer to the mskwaj_decompressor + * instance being called + * @param kwaj the KWAJ file to extract data from + * @param filename the filename to write the decompressed data to. This + * is passed directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*extract)(struct mskwaj_decompressor *self, + struct mskwajd_header *kwaj, + const char *filename); + + /** + * Decompresses an KWAJ file to an output file in one step. + * + * This opens an KWAJ file as input, reads the header, then decompresses + * the compressed data immediately to an output file, finally closing + * both the input and output file. It is more convenient to use than + * open() then extract() then close(), if you do not need to know the + * KWAJ output size or output filename. + * + * @param self a self-referential pointer to the mskwaj_decompressor + * instance being called + * @param input the filename of the input KWAJ file. This is passed + * directly to mspack_system::open(). + * @param output the filename to write the decompressed data to. This + * is passed directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*decompress)(struct mskwaj_decompressor *self, + const char *input, + const char *output); + + /** + * Returns the error code set by the most recently called method. + * + * This is useful for open() which does not return an + * error code directly. + * + * @param self a self-referential pointer to the mskwaj_decompressor + * instance being called + * @return the most recent error code + * @see open(), search() + */ + int (*last_error)(struct mskwaj_decompressor *self); +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/rbutil/rbutilqt/mspack/mszip.h b/rbutil/rbutilqt/mspack/mszip.h new file mode 100644 index 0000000000..13c9542ee6 --- /dev/null +++ b/rbutil/rbutilqt/mspack/mszip.h @@ -0,0 +1,121 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * The deflate method was created by Phil Katz. MSZIP is equivalent to the + * deflate method. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_MSZIP_H +#define MSPACK_MSZIP_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* MSZIP (deflate) compression / (inflate) decompression definitions */ + +#define MSZIP_FRAME_SIZE (32768) /* size of LZ history window */ +#define MSZIP_LITERAL_MAXSYMBOLS (288) /* literal/length huffman tree */ +#define MSZIP_LITERAL_TABLEBITS (9) +#define MSZIP_DISTANCE_MAXSYMBOLS (32) /* distance huffman tree */ +#define MSZIP_DISTANCE_TABLEBITS (6) + +/* if there are less direct lookup entries than symbols, the longer + * code pointers will be <= maxsymbols. This must not happen, or we + * will decode entries badly */ +#if (1 << MSZIP_LITERAL_TABLEBITS) < (MSZIP_LITERAL_MAXSYMBOLS * 2) +# define MSZIP_LITERAL_TABLESIZE (MSZIP_LITERAL_MAXSYMBOLS * 4) +#else +# define MSZIP_LITERAL_TABLESIZE ((1 << MSZIP_LITERAL_TABLEBITS) + \ + (MSZIP_LITERAL_MAXSYMBOLS * 2)) +#endif + +#if (1 << MSZIP_DISTANCE_TABLEBITS) < (MSZIP_DISTANCE_MAXSYMBOLS * 2) +# define MSZIP_DISTANCE_TABLESIZE (MSZIP_DISTANCE_MAXSYMBOLS * 4) +#else +# define MSZIP_DISTANCE_TABLESIZE ((1 << MSZIP_DISTANCE_TABLEBITS) + \ + (MSZIP_DISTANCE_MAXSYMBOLS * 2)) +#endif + +struct mszipd_stream { + struct mspack_system *sys; /* I/O routines */ + struct mspack_file *input; /* input file handle */ + struct mspack_file *output; /* output file handle */ + unsigned int window_posn; /* offset within window */ + + /* inflate() will call this whenever the window should be emptied. */ + int (*flush_window)(struct mszipd_stream *, unsigned int); + + int error, repair_mode, bytes_output; + + /* I/O buffering */ + unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end, input_end; + unsigned int bit_buffer, bits_left, inbuf_size; + + + /* huffman code lengths */ + unsigned char LITERAL_len[MSZIP_LITERAL_MAXSYMBOLS]; + unsigned char DISTANCE_len[MSZIP_DISTANCE_MAXSYMBOLS]; + + /* huffman decoding tables */ + unsigned short LITERAL_table [MSZIP_LITERAL_TABLESIZE]; + unsigned short DISTANCE_table[MSZIP_DISTANCE_TABLESIZE]; + + /* 32kb history window */ + unsigned char window[MSZIP_FRAME_SIZE]; +}; + +/* allocates MS-ZIP decompression stream for decoding the given stream. + * + * - uses system->alloc() to allocate memory + * + * - returns NULL if not enough memory + * + * - input_buffer_size is how many bytes to use as an input bitstream buffer + * + * - if repair_mode is non-zero, errors in decompression will be skipped + * and 'holes' left will be filled with zero bytes. This allows at least + * a partial recovery of erroneous data. + */ +extern struct mszipd_stream *mszipd_init(struct mspack_system *system, + struct mspack_file *input, + struct mspack_file *output, + int input_buffer_size, + int repair_mode); + +/* decompresses, or decompresses more of, an MS-ZIP stream. + * + * - out_bytes of data will be decompressed and the function will return + * with an MSPACK_ERR_OK return code. + * + * - decompressing will stop as soon as out_bytes is reached. if the true + * amount of bytes decoded spills over that amount, they will be kept for + * a later invocation of mszipd_decompress(). + * + * - the output bytes will be passed to the system->write() function given in + * mszipd_init(), using the output file handle given in mszipd_init(). More + * than one call may be made to system->write() + * + * - MS-ZIP will read input bytes as necessary using the system->read() + * function given in mszipd_init(), using the input file handle given in + * mszipd_init(). This will continue until system->read() returns 0 bytes, + * or an error. + */ +extern int mszipd_decompress(struct mszipd_stream *zip, off_t out_bytes); + +/* frees all stream associated with an MS-ZIP data stream + * + * - calls system->free() using the system pointer given in mszipd_init() + */ +void mszipd_free(struct mszipd_stream *zip); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/rbutil/rbutilqt/mspack/mszipc.c b/rbutil/rbutilqt/mspack/mszipc.c new file mode 100644 index 0000000000..2f1ecb2e87 --- /dev/null +++ b/rbutil/rbutilqt/mspack/mszipc.c @@ -0,0 +1,18 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * The deflate method was created by Phil Katz. MSZIP is equivalent to the + * deflate method. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* MS-ZIP compression implementation */ + +#include +#include + +/* todo */ diff --git a/rbutil/rbutilqt/mspack/mszipd.c b/rbutil/rbutilqt/mspack/mszipd.c new file mode 100644 index 0000000000..5b7ef4ff4d --- /dev/null +++ b/rbutil/rbutilqt/mspack/mszipd.c @@ -0,0 +1,475 @@ +/* This file is part of libmspack. + * (C) 2003-2010 Stuart Caie. + * + * The deflate method was created by Phil Katz. MSZIP is equivalent to the + * deflate method. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* MS-ZIP decompression implementation. */ + +#include +#include + +/* import bit-reading macros and code */ +#define BITS_TYPE struct mszipd_stream +#define BITS_VAR zip +#define BITS_ORDER_LSB +#define BITS_LSB_TABLE +#define READ_BYTES do { \ + READ_IF_NEEDED; \ + INJECT_BITS(*i_ptr++, 8); \ +} while (0) +#include + +/* import huffman macros and code */ +#define TABLEBITS(tbl) MSZIP_##tbl##_TABLEBITS +#define MAXSYMBOLS(tbl) MSZIP_##tbl##_MAXSYMBOLS +#define HUFF_TABLE(tbl,idx) zip->tbl##_table[idx] +#define HUFF_LEN(tbl,idx) zip->tbl##_len[idx] +#define HUFF_ERROR return INF_ERR_HUFFSYM +#include + +#define FLUSH_IF_NEEDED do { \ + if (zip->window_posn == MSZIP_FRAME_SIZE) { \ + if (zip->flush_window(zip, MSZIP_FRAME_SIZE)) { \ + return INF_ERR_FLUSH; \ + } \ + zip->window_posn = 0; \ + } \ +} while (0) + +/* match lengths for literal codes 257.. 285 */ +static const unsigned short lit_lengths[29] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, + 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 +}; + +/* match offsets for distance codes 0 .. 29 */ +static const unsigned short dist_offsets[30] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, + 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 +}; + +/* extra bits required for literal codes 257.. 285 */ +static const unsigned char lit_extrabits[29] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, + 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 +}; + +/* extra bits required for distance codes 0 .. 29 */ +static const unsigned char dist_extrabits[30] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 +}; + +/* the order of the bit length Huffman code lengths */ +static const unsigned char bitlen_order[19] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 +}; + +/* inflate() error codes */ +#define INF_ERR_BLOCKTYPE (-1) /* unknown block type */ +#define INF_ERR_COMPLEMENT (-2) /* block size complement mismatch */ +#define INF_ERR_FLUSH (-3) /* error from flush_window() callback */ +#define INF_ERR_BITBUF (-4) /* too many bits in bit buffer */ +#define INF_ERR_SYMLENS (-5) /* too many symbols in blocktype 2 header */ +#define INF_ERR_BITLENTBL (-6) /* failed to build bitlens huffman table */ +#define INF_ERR_LITERALTBL (-7) /* failed to build literals huffman table */ +#define INF_ERR_DISTANCETBL (-8) /* failed to build distance huffman table */ +#define INF_ERR_BITOVERRUN (-9) /* bitlen RLE code goes over table size */ +#define INF_ERR_BADBITLEN (-10) /* invalid bit-length code */ +#define INF_ERR_LITCODE (-11) /* out-of-range literal code */ +#define INF_ERR_DISTCODE (-12) /* out-of-range distance code */ +#define INF_ERR_DISTANCE (-13) /* somehow, distance is beyond 32k */ +#define INF_ERR_HUFFSYM (-14) /* out of bits decoding huffman symbol */ + +static int zip_read_lens(struct mszipd_stream *zip) { + /* for the bit buffer and huffman decoding */ + register unsigned int bit_buffer; + register int bits_left; + unsigned char *i_ptr, *i_end; + + /* bitlen Huffman codes -- immediate lookup, 7 bit max code length */ + unsigned short bl_table[(1 << 7)]; + unsigned char bl_len[19]; + + unsigned char lens[MSZIP_LITERAL_MAXSYMBOLS + MSZIP_DISTANCE_MAXSYMBOLS]; + unsigned int lit_codes, dist_codes, code, last_code=0, bitlen_codes, i, run; + + RESTORE_BITS; + + /* read the number of codes */ + READ_BITS(lit_codes, 5); lit_codes += 257; + READ_BITS(dist_codes, 5); dist_codes += 1; + READ_BITS(bitlen_codes, 4); bitlen_codes += 4; + if (lit_codes > MSZIP_LITERAL_MAXSYMBOLS) return INF_ERR_SYMLENS; + if (dist_codes > MSZIP_DISTANCE_MAXSYMBOLS) return INF_ERR_SYMLENS; + + /* read in the bit lengths in their unusual order */ + for (i = 0; i < bitlen_codes; i++) READ_BITS(bl_len[bitlen_order[i]], 3); + while (i < 19) bl_len[bitlen_order[i++]] = 0; + + /* create decoding table with an immediate lookup */ + if (make_decode_table(19, 7, &bl_len[0], &bl_table[0])) { + return INF_ERR_BITLENTBL; + } + + /* read literal / distance code lengths */ + for (i = 0; i < (lit_codes + dist_codes); i++) { + /* single-level huffman lookup */ + ENSURE_BITS(7); + code = bl_table[PEEK_BITS(7)]; + REMOVE_BITS(bl_len[code]); + + if (code < 16) lens[i] = last_code = code; + else { + switch (code) { + case 16: READ_BITS(run, 2); run += 3; code = last_code; break; + case 17: READ_BITS(run, 3); run += 3; code = 0; break; + case 18: READ_BITS(run, 7); run += 11; code = 0; break; + default: D(("bad code!: %u", code)) return INF_ERR_BADBITLEN; + } + if ((i + run) > (lit_codes + dist_codes)) return INF_ERR_BITOVERRUN; + while (run--) lens[i++] = code; + i--; + } + } + + /* copy LITERAL code lengths and clear any remaining */ + i = lit_codes; + zip->sys->copy(&lens[0], &zip->LITERAL_len[0], i); + while (i < MSZIP_LITERAL_MAXSYMBOLS) zip->LITERAL_len[i++] = 0; + + i = dist_codes; + zip->sys->copy(&lens[lit_codes], &zip->DISTANCE_len[0], i); + while (i < MSZIP_DISTANCE_MAXSYMBOLS) zip->DISTANCE_len[i++] = 0; + + STORE_BITS; + return 0; +} + +/* a clean implementation of RFC 1951 / inflate */ +static int inflate(struct mszipd_stream *zip) { + unsigned int last_block, block_type, distance, length, this_run, i; + + /* for the bit buffer and huffman decoding */ + register unsigned int bit_buffer; + register int bits_left; + register unsigned short sym; + unsigned char *i_ptr, *i_end; + + RESTORE_BITS; + + do { + /* read in last block bit */ + READ_BITS(last_block, 1); + + /* read in block type */ + READ_BITS(block_type, 2); + + if (block_type == 0) { + /* uncompressed block */ + unsigned char lens_buf[4]; + + /* go to byte boundary */ + i = bits_left & 7; REMOVE_BITS(i); + + /* read 4 bytes of data, emptying the bit-buffer if necessary */ + for (i = 0; (bits_left >= 8); i++) { + if (i == 4) return INF_ERR_BITBUF; + lens_buf[i] = PEEK_BITS(8); + REMOVE_BITS(8); + } + if (bits_left != 0) return INF_ERR_BITBUF; + while (i < 4) { + READ_IF_NEEDED; + lens_buf[i++] = *i_ptr++; + } + + /* get the length and its complement */ + length = lens_buf[0] | (lens_buf[1] << 8); + i = lens_buf[2] | (lens_buf[3] << 8); + if (length != (~i & 0xFFFF)) return INF_ERR_COMPLEMENT; + + /* read and copy the uncompressed data into the window */ + while (length > 0) { + READ_IF_NEEDED; + + this_run = length; + if (this_run > (unsigned int)(i_end - i_ptr)) this_run = i_end - i_ptr; + if (this_run > (MSZIP_FRAME_SIZE - zip->window_posn)) + this_run = MSZIP_FRAME_SIZE - zip->window_posn; + + zip->sys->copy(i_ptr, &zip->window[zip->window_posn], this_run); + zip->window_posn += this_run; + i_ptr += this_run; + length -= this_run; + FLUSH_IF_NEEDED; + } + } + else if ((block_type == 1) || (block_type == 2)) { + /* Huffman-compressed LZ77 block */ + unsigned int match_posn, code; + + if (block_type == 1) { + /* block with fixed Huffman codes */ + i = 0; + while (i < 144) zip->LITERAL_len[i++] = 8; + while (i < 256) zip->LITERAL_len[i++] = 9; + while (i < 280) zip->LITERAL_len[i++] = 7; + while (i < 288) zip->LITERAL_len[i++] = 8; + for (i = 0; i < 32; i++) zip->DISTANCE_len[i] = 5; + } + else { + /* block with dynamic Huffman codes */ + STORE_BITS; + if ((i = zip_read_lens(zip))) return i; + RESTORE_BITS; + } + + /* now huffman lengths are read for either kind of block, + * create huffman decoding tables */ + if (make_decode_table(MSZIP_LITERAL_MAXSYMBOLS, MSZIP_LITERAL_TABLEBITS, + &zip->LITERAL_len[0], &zip->LITERAL_table[0])) + { + return INF_ERR_LITERALTBL; + } + + if (make_decode_table(MSZIP_DISTANCE_MAXSYMBOLS,MSZIP_DISTANCE_TABLEBITS, + &zip->DISTANCE_len[0], &zip->DISTANCE_table[0])) + { + return INF_ERR_DISTANCETBL; + } + + /* decode forever until end of block code */ + for (;;) { + READ_HUFFSYM(LITERAL, code); + if (code < 256) { + zip->window[zip->window_posn++] = (unsigned char) code; + FLUSH_IF_NEEDED; + } + else if (code == 256) { + /* END OF BLOCK CODE: loop break point */ + break; + } + else { + code -= 257; /* codes 257-285 are matches */ + if (code >= 29) return INF_ERR_LITCODE; /* codes 286-287 are illegal */ + READ_BITS_T(length, lit_extrabits[code]); + length += lit_lengths[code]; + + READ_HUFFSYM(DISTANCE, code); + if (code > 30) return INF_ERR_DISTCODE; + READ_BITS_T(distance, dist_extrabits[code]); + distance += dist_offsets[code]; + + /* match position is window position minus distance. If distance + * is more than window position numerically, it must 'wrap + * around' the frame size. */ + match_posn = ((distance > zip->window_posn) ? MSZIP_FRAME_SIZE : 0) + + zip->window_posn - distance; + + /* copy match */ + if (length < 12) { + /* short match, use slower loop but no loop setup code */ + while (length--) { + zip->window[zip->window_posn++] = zip->window[match_posn++]; + match_posn &= MSZIP_FRAME_SIZE - 1; + FLUSH_IF_NEEDED; + } + } + else { + /* longer match, use faster loop but with setup expense */ + unsigned char *runsrc, *rundest; + do { + this_run = length; + if ((match_posn + this_run) > MSZIP_FRAME_SIZE) + this_run = MSZIP_FRAME_SIZE - match_posn; + if ((zip->window_posn + this_run) > MSZIP_FRAME_SIZE) + this_run = MSZIP_FRAME_SIZE - zip->window_posn; + + rundest = &zip->window[zip->window_posn]; zip->window_posn += this_run; + runsrc = &zip->window[match_posn]; match_posn += this_run; + length -= this_run; + while (this_run--) *rundest++ = *runsrc++; + if (match_posn == MSZIP_FRAME_SIZE) match_posn = 0; + FLUSH_IF_NEEDED; + } while (length > 0); + } + + } /* else (code >= 257) */ + + } /* for(;;) -- break point at 'code == 256' */ + } + else { + /* block_type == 3 -- bad block type */ + return INF_ERR_BLOCKTYPE; + } + } while (!last_block); + + /* flush the remaining data */ + if (zip->window_posn) { + if (zip->flush_window(zip, zip->window_posn)) return INF_ERR_FLUSH; + } + STORE_BITS; + + /* return success */ + return 0; +} + +/* inflate() calls this whenever the window should be flushed. As + * MSZIP only expands to the size of the window, the implementation used + * simply keeps track of the amount of data flushed, and if more than 32k + * is flushed, an error is raised. + */ +static int mszipd_flush_window(struct mszipd_stream *zip, + unsigned int data_flushed) +{ + zip->bytes_output += data_flushed; + if (zip->bytes_output > MSZIP_FRAME_SIZE) { + D(("overflow: %u bytes flushed, total is now %u", + data_flushed, zip->bytes_output)) + return 1; + } + return 0; +} + +struct mszipd_stream *mszipd_init(struct mspack_system *system, + struct mspack_file *input, + struct mspack_file *output, + int input_buffer_size, + int repair_mode) +{ + struct mszipd_stream *zip; + + if (!system) return NULL; + + input_buffer_size = (input_buffer_size + 1) & -2; + if (!input_buffer_size) return NULL; + + /* allocate decompression state */ + if (!(zip = (struct mszipd_stream *) system->alloc(system, sizeof(struct mszipd_stream)))) { + return NULL; + } + + /* allocate input buffer */ + zip->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size); + if (!zip->inbuf) { + system->free(zip); + return NULL; + } + + /* initialise decompression state */ + zip->sys = system; + zip->input = input; + zip->output = output; + zip->inbuf_size = input_buffer_size; + zip->input_end = 0; + zip->error = MSPACK_ERR_OK; + zip->repair_mode = repair_mode; + zip->flush_window = &mszipd_flush_window; + + zip->i_ptr = zip->i_end = &zip->inbuf[0]; + zip->o_ptr = zip->o_end = NULL; + zip->bit_buffer = 0; zip->bits_left = 0; + return zip; +} + +int mszipd_decompress(struct mszipd_stream *zip, off_t out_bytes) { + /* for the bit buffer */ + register unsigned int bit_buffer; + register int bits_left; + unsigned char *i_ptr, *i_end; + + int i, state, error; + + /* easy answers */ + if (!zip || (out_bytes < 0)) return MSPACK_ERR_ARGS; + if (zip->error) return zip->error; + + /* flush out any stored-up bytes before we begin */ + i = zip->o_end - zip->o_ptr; + if ((off_t) i > out_bytes) i = (int) out_bytes; + if (i) { + if (zip->sys->write(zip->output, zip->o_ptr, i) != i) { + return zip->error = MSPACK_ERR_WRITE; + } + zip->o_ptr += i; + out_bytes -= i; + } + if (out_bytes == 0) return MSPACK_ERR_OK; + + + while (out_bytes > 0) { + /* unpack another block */ + RESTORE_BITS; + + /* skip to next read 'CK' header */ + i = bits_left & 7; REMOVE_BITS(i); /* align to bytestream */ + state = 0; + do { + READ_BITS(i, 8); + if (i == 'C') state = 1; + else if ((state == 1) && (i == 'K')) state = 2; + else state = 0; + } while (state != 2); + + /* inflate a block, repair and realign if necessary */ + zip->window_posn = 0; + zip->bytes_output = 0; + STORE_BITS; + if ((error = inflate(zip))) { + D(("inflate error %d", error)) + if (zip->repair_mode) { + /* recover partially-inflated buffers */ + if (zip->bytes_output == 0 && zip->window_posn > 0) { + zip->flush_window(zip, zip->window_posn); + } + zip->sys->message(NULL, "MSZIP error, %u bytes of data lost.", + MSZIP_FRAME_SIZE - zip->bytes_output); + for (i = zip->bytes_output; i < MSZIP_FRAME_SIZE; i++) { + zip->window[i] = '\0'; + } + zip->bytes_output = MSZIP_FRAME_SIZE; + } + else { + return zip->error = (error > 0) ? error : MSPACK_ERR_DECRUNCH; + } + } + zip->o_ptr = &zip->window[0]; + zip->o_end = &zip->o_ptr[zip->bytes_output]; + + /* write a frame */ + i = (out_bytes < (off_t)zip->bytes_output) ? + (int)out_bytes : zip->bytes_output; + if (zip->sys->write(zip->output, zip->o_ptr, i) != i) { + return zip->error = MSPACK_ERR_WRITE; + } + + /* mspack errors (i.e. read errors) are fatal and can't be recovered */ + if ((error > 0) && zip->repair_mode) return error; + + zip->o_ptr += i; + out_bytes -= i; + } + + if (out_bytes) { + D(("bytes left to output")) + return zip->error = MSPACK_ERR_DECRUNCH; + } + return MSPACK_ERR_OK; +} + +void mszipd_free(struct mszipd_stream *zip) { + struct mspack_system *sys; + if (zip) { + sys = zip->sys; + sys->free(zip->inbuf); + sys->free(zip); + } +} diff --git a/rbutil/rbutilqt/mspack/qtm.h b/rbutil/rbutilqt/mspack/qtm.h new file mode 100644 index 0000000000..ab0bb4c32c --- /dev/null +++ b/rbutil/rbutilqt/mspack/qtm.h @@ -0,0 +1,128 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * The Quantum method was created by David Stafford, adapted by Microsoft + * Corporation. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_QTM_H +#define MSPACK_QTM_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Quantum compression / decompression definitions */ + +#define QTM_FRAME_SIZE (32768) + +struct qtmd_modelsym { + unsigned short sym, cumfreq; +}; + +struct qtmd_model { + int shiftsleft, entries; + struct qtmd_modelsym *syms; +}; + +struct qtmd_stream { + struct mspack_system *sys; /* I/O routines */ + struct mspack_file *input; /* input file handle */ + struct mspack_file *output; /* output file handle */ + + unsigned char *window; /* decoding window */ + unsigned int window_size; /* window size */ + unsigned int window_posn; /* decompression offset within window */ + unsigned int frame_todo; /* bytes remaining for current frame */ + + unsigned short H, L, C; /* high/low/current: arith coding state */ + unsigned char header_read; /* have we started decoding a new frame? */ + + int error; + + /* I/O buffers */ + unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end; + unsigned int bit_buffer, inbuf_size; + unsigned char bits_left, input_end; + + /* four literal models, each representing 64 symbols + * model0 for literals from 0 to 63 (selector = 0) + * model1 for literals from 64 to 127 (selector = 1) + * model2 for literals from 128 to 191 (selector = 2) + * model3 for literals from 129 to 255 (selector = 3) */ + struct qtmd_model model0, model1, model2, model3; + + /* three match models. + * model4 for match with fixed length of 3 bytes + * model5 for match with fixed length of 4 bytes + * model6 for variable length match, encoded with model6len model */ + struct qtmd_model model4, model5, model6, model6len; + + /* selector model. 0-6 to say literal (0,1,2,3) or match (4,5,6) */ + struct qtmd_model model7; + + /* symbol arrays for all models */ + struct qtmd_modelsym m0sym[64 + 1]; + struct qtmd_modelsym m1sym[64 + 1]; + struct qtmd_modelsym m2sym[64 + 1]; + struct qtmd_modelsym m3sym[64 + 1]; + struct qtmd_modelsym m4sym[24 + 1]; + struct qtmd_modelsym m5sym[36 + 1]; + struct qtmd_modelsym m6sym[42 + 1], m6lsym[27 + 1]; + struct qtmd_modelsym m7sym[7 + 1]; +}; + +/* allocates Quantum decompression state for decoding the given stream. + * + * - returns NULL if window_bits is outwith the range 10 to 21 (inclusive). + * + * - uses system->alloc() to allocate memory + * + * - returns NULL if not enough memory + * + * - window_bits is the size of the Quantum window, from 1Kb (10) to 2Mb (21). + * + * - input_buffer_size is the number of bytes to use to store bitstream data. + */ +extern struct qtmd_stream *qtmd_init(struct mspack_system *system, + struct mspack_file *input, + struct mspack_file *output, + int window_bits, + int input_buffer_size); + +/* decompresses, or decompresses more of, a Quantum stream. + * + * - out_bytes of data will be decompressed and the function will return + * with an MSPACK_ERR_OK return code. + * + * - decompressing will stop as soon as out_bytes is reached. if the true + * amount of bytes decoded spills over that amount, they will be kept for + * a later invocation of qtmd_decompress(). + * + * - the output bytes will be passed to the system->write() function given in + * qtmd_init(), using the output file handle given in qtmd_init(). More + * than one call may be made to system->write() + * + * - Quantum will read input bytes as necessary using the system->read() + * function given in qtmd_init(), using the input file handle given in + * qtmd_init(). This will continue until system->read() returns 0 bytes, + * or an error. + */ +extern int qtmd_decompress(struct qtmd_stream *qtm, off_t out_bytes); + +/* frees all state associated with a Quantum data stream + * + * - calls system->free() using the system pointer given in qtmd_init() + */ +void qtmd_free(struct qtmd_stream *qtm); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/rbutil/rbutilqt/mspack/qtmd.c b/rbutil/rbutilqt/mspack/qtmd.c new file mode 100644 index 0000000000..12b27f5608 --- /dev/null +++ b/rbutil/rbutilqt/mspack/qtmd.c @@ -0,0 +1,489 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * The Quantum method was created by David Stafford, adapted by Microsoft + * Corporation. + * + * This decompressor is based on an implementation by Matthew Russotto, used + * with permission. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* Quantum decompression implementation */ + +/* This decompressor was researched and implemented by Matthew Russotto. It + * has since been tidied up by Stuart Caie. More information can be found at + * http://www.speakeasy.org/~russotto/quantumcomp.html + */ + +#include +#include + +/* import bit-reading macros and code */ +#define BITS_TYPE struct qtmd_stream +#define BITS_VAR qtm +#define BITS_ORDER_MSB +#define READ_BYTES do { \ + unsigned char b0, b1; \ + READ_IF_NEEDED; b0 = *i_ptr++; \ + READ_IF_NEEDED; b1 = *i_ptr++; \ + INJECT_BITS((b0 << 8) | b1, 16); \ +} while (0) +#include + +/* Quantum static data tables: + * + * Quantum uses 'position slots' to represent match offsets. For every + * match, a small 'position slot' number and a small offset from that slot + * are encoded instead of one large offset. + * + * position_base[] is an index to the position slot bases + * + * extra_bits[] states how many bits of offset-from-base data is needed. + * + * length_base[] and length_extra[] are equivalent in function, but are + * used for encoding selector 6 (variable length match) match lengths, + * instead of match offsets. + * + * They are generated with the following code: + * unsigned int i, offset; + * for (i = 0, offset = 0; i < 42; i++) { + * position_base[i] = offset; + * extra_bits[i] = ((i < 2) ? 0 : (i - 2)) >> 1; + * offset += 1 << extra_bits[i]; + * } + * for (i = 0, offset = 0; i < 26; i++) { + * length_base[i] = offset; + * length_extra[i] = (i < 2 ? 0 : i - 2) >> 2; + * offset += 1 << length_extra[i]; + * } + * length_base[26] = 254; length_extra[26] = 0; + */ +static const unsigned int position_base[42] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, + 65536, 98304, 131072, 196608, 262144, 393216, 524288, 786432, 1048576, 1572864 +}; +static const unsigned char extra_bits[42] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19 +}; +static const unsigned char length_base[27] = { + 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 18, 22, 26, + 30, 38, 46, 54, 62, 78, 94, 110, 126, 158, 190, 222, 254 +}; +static const unsigned char length_extra[27] = { + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 +}; + + +/* Arithmetic decoder: + * + * GET_SYMBOL(model, var) fetches the next symbol from the stated model + * and puts it in var. + * + * If necessary, qtmd_update_model() is called. + */ +#define GET_SYMBOL(model, var) do { \ + range = ((H - L) & 0xFFFF) + 1; \ + symf = ((((C - L + 1) * model.syms[0].cumfreq)-1) / range) & 0xFFFF; \ + \ + for (i = 1; i < model.entries; i++) { \ + if (model.syms[i].cumfreq <= symf) break; \ + } \ + (var) = model.syms[i-1].sym; \ + \ + range = (H - L) + 1; \ + symf = model.syms[0].cumfreq; \ + H = L + ((model.syms[i-1].cumfreq * range) / symf) - 1; \ + L = L + ((model.syms[i].cumfreq * range) / symf); \ + \ + do { model.syms[--i].cumfreq += 8; } while (i > 0); \ + if (model.syms[0].cumfreq > 3800) qtmd_update_model(&model); \ + \ + while (1) { \ + if ((L & 0x8000) != (H & 0x8000)) { \ + if ((L & 0x4000) && !(H & 0x4000)) { \ + /* underflow case */ \ + C ^= 0x4000; L &= 0x3FFF; H |= 0x4000; \ + } \ + else break; \ + } \ + L <<= 1; H = (H << 1) | 1; \ + ENSURE_BITS(1); \ + C = (C << 1) | PEEK_BITS(1); \ + REMOVE_BITS(1); \ + } \ +} while (0) + +static void qtmd_update_model(struct qtmd_model *model) { + struct qtmd_modelsym tmp; + int i, j; + + if (--model->shiftsleft) { + for (i = model->entries - 1; i >= 0; i--) { + /* -1, not -2; the 0 entry saves this */ + model->syms[i].cumfreq >>= 1; + if (model->syms[i].cumfreq <= model->syms[i+1].cumfreq) { + model->syms[i].cumfreq = model->syms[i+1].cumfreq + 1; + } + } + } + else { + model->shiftsleft = 50; + for (i = 0; i < model->entries; i++) { + /* no -1, want to include the 0 entry */ + /* this converts cumfreqs into frequencies, then shifts right */ + model->syms[i].cumfreq -= model->syms[i+1].cumfreq; + model->syms[i].cumfreq++; /* avoid losing things entirely */ + model->syms[i].cumfreq >>= 1; + } + + /* now sort by frequencies, decreasing order -- this must be an + * inplace selection sort, or a sort with the same (in)stability + * characteristics */ + for (i = 0; i < model->entries - 1; i++) { + for (j = i + 1; j < model->entries; j++) { + if (model->syms[i].cumfreq < model->syms[j].cumfreq) { + tmp = model->syms[i]; + model->syms[i] = model->syms[j]; + model->syms[j] = tmp; + } + } + } + + /* then convert frequencies back to cumfreq */ + for (i = model->entries - 1; i >= 0; i--) { + model->syms[i].cumfreq += model->syms[i+1].cumfreq; + } + } +} + +/* Initialises a model to decode symbols from [start] to [start]+[len]-1 */ +static void qtmd_init_model(struct qtmd_model *model, + struct qtmd_modelsym *syms, int start, int len) +{ + int i; + + model->shiftsleft = 4; + model->entries = len; + model->syms = syms; + + for (i = 0; i <= len; i++) { + syms[i].sym = start + i; /* actual symbol */ + syms[i].cumfreq = len - i; /* current frequency of that symbol */ + } +} + + +/*-------- main Quantum code --------*/ + +struct qtmd_stream *qtmd_init(struct mspack_system *system, + struct mspack_file *input, + struct mspack_file *output, + int window_bits, int input_buffer_size) +{ + unsigned int window_size = 1 << window_bits; + struct qtmd_stream *qtm; + int i; + + if (!system) return NULL; + + /* Quantum supports window sizes of 2^10 (1Kb) through 2^21 (2Mb) */ + if (window_bits < 10 || window_bits > 21) return NULL; + + input_buffer_size = (input_buffer_size + 1) & -2; + if (input_buffer_size < 2) return NULL; + + /* allocate decompression state */ + if (!(qtm = (struct qtmd_stream *) system->alloc(system, sizeof(struct qtmd_stream)))) { + return NULL; + } + + /* allocate decompression window and input buffer */ + qtm->window = (unsigned char *) system->alloc(system, (size_t) window_size); + qtm->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size); + if (!qtm->window || !qtm->inbuf) { + system->free(qtm->window); + system->free(qtm->inbuf); + system->free(qtm); + return NULL; + } + + /* initialise decompression state */ + qtm->sys = system; + qtm->input = input; + qtm->output = output; + qtm->inbuf_size = input_buffer_size; + qtm->window_size = window_size; + qtm->window_posn = 0; + qtm->frame_todo = QTM_FRAME_SIZE; + qtm->header_read = 0; + qtm->error = MSPACK_ERR_OK; + + qtm->i_ptr = qtm->i_end = &qtm->inbuf[0]; + qtm->o_ptr = qtm->o_end = &qtm->window[0]; + qtm->input_end = 0; + qtm->bits_left = 0; + qtm->bit_buffer = 0; + + /* initialise arithmetic coding models + * - model 4 depends on window size, ranges from 20 to 24 + * - model 5 depends on window size, ranges from 20 to 36 + * - model 6pos depends on window size, ranges from 20 to 42 + */ + i = window_bits * 2; + qtmd_init_model(&qtm->model0, &qtm->m0sym[0], 0, 64); + qtmd_init_model(&qtm->model1, &qtm->m1sym[0], 64, 64); + qtmd_init_model(&qtm->model2, &qtm->m2sym[0], 128, 64); + qtmd_init_model(&qtm->model3, &qtm->m3sym[0], 192, 64); + qtmd_init_model(&qtm->model4, &qtm->m4sym[0], 0, (i > 24) ? 24 : i); + qtmd_init_model(&qtm->model5, &qtm->m5sym[0], 0, (i > 36) ? 36 : i); + qtmd_init_model(&qtm->model6, &qtm->m6sym[0], 0, i); + qtmd_init_model(&qtm->model6len, &qtm->m6lsym[0], 0, 27); + qtmd_init_model(&qtm->model7, &qtm->m7sym[0], 0, 7); + + /* all ok */ + return qtm; +} + +int qtmd_decompress(struct qtmd_stream *qtm, off_t out_bytes) { + unsigned int frame_todo, frame_end, window_posn, match_offset, range; + unsigned char *window, *i_ptr, *i_end, *runsrc, *rundest; + int i, j, selector, extra, sym, match_length; + unsigned short H, L, C, symf; + + register unsigned int bit_buffer; + register unsigned char bits_left; + + /* easy answers */ + if (!qtm || (out_bytes < 0)) return MSPACK_ERR_ARGS; + if (qtm->error) return qtm->error; + + /* flush out any stored-up bytes before we begin */ + i = qtm->o_end - qtm->o_ptr; + if ((off_t) i > out_bytes) i = (int) out_bytes; + if (i) { + if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { + return qtm->error = MSPACK_ERR_WRITE; + } + qtm->o_ptr += i; + out_bytes -= i; + } + if (out_bytes == 0) return MSPACK_ERR_OK; + + /* restore local state */ + RESTORE_BITS; + window = qtm->window; + window_posn = qtm->window_posn; + frame_todo = qtm->frame_todo; + H = qtm->H; + L = qtm->L; + C = qtm->C; + + /* while we do not have enough decoded bytes in reserve: */ + while ((qtm->o_end - qtm->o_ptr) < out_bytes) { + /* read header if necessary. Initialises H, L and C */ + if (!qtm->header_read) { + H = 0xFFFF; L = 0; READ_BITS(C, 16); + qtm->header_read = 1; + } + + /* decode more, up to the number of bytes needed, the frame boundary, + * or the window boundary, whichever comes first */ + frame_end = window_posn + (out_bytes - (qtm->o_end - qtm->o_ptr)); + if ((window_posn + frame_todo) < frame_end) { + frame_end = window_posn + frame_todo; + } + if (frame_end > qtm->window_size) { + frame_end = qtm->window_size; + } + + while (window_posn < frame_end) { + GET_SYMBOL(qtm->model7, selector); + if (selector < 4) { + /* literal byte */ + struct qtmd_model *mdl = (selector == 0) ? &qtm->model0 : + ((selector == 1) ? &qtm->model1 : + ((selector == 2) ? &qtm->model2 : + &qtm->model3)); + GET_SYMBOL((*mdl), sym); + window[window_posn++] = sym; + frame_todo--; + } + else { + /* match repeated string */ + switch (selector) { + case 4: /* selector 4 = fixed length match (3 bytes) */ + GET_SYMBOL(qtm->model4, sym); + READ_MANY_BITS(extra, extra_bits[sym]); + match_offset = position_base[sym] + extra + 1; + match_length = 3; + break; + + case 5: /* selector 5 = fixed length match (4 bytes) */ + GET_SYMBOL(qtm->model5, sym); + READ_MANY_BITS(extra, extra_bits[sym]); + match_offset = position_base[sym] + extra + 1; + match_length = 4; + break; + + case 6: /* selector 6 = variable length match */ + GET_SYMBOL(qtm->model6len, sym); + READ_MANY_BITS(extra, length_extra[sym]); + match_length = length_base[sym] + extra + 5; + + GET_SYMBOL(qtm->model6, sym); + READ_MANY_BITS(extra, extra_bits[sym]); + match_offset = position_base[sym] + extra + 1; + break; + + default: + /* should be impossible, model7 can only return 0-6 */ + D(("got %d from selector", selector)) + return qtm->error = MSPACK_ERR_DECRUNCH; + } + + rundest = &window[window_posn]; + frame_todo -= match_length; + + /* does match destination wrap the window? This situation is possible + * where the window size is less than the 32k frame size, but matches + * must not go beyond a frame boundary */ + if ((window_posn + match_length) > qtm->window_size) { + /* copy first part of match, before window end */ + i = qtm->window_size - window_posn; + j = window_posn - match_offset; + while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; + + /* flush currently stored data */ + i = (&window[qtm->window_size] - qtm->o_ptr); + + /* this should not happen, but if it does then this code + * can't handle the situation (can't flush up to the end of + * the window, but can't break out either because we haven't + * finished writing the match). bail out in this case */ + if (i > out_bytes) { + D(("during window-wrap match; %d bytes to flush but only need %d", + i, (int) out_bytes)) + return qtm->error = MSPACK_ERR_DECRUNCH; + } + if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { + return qtm->error = MSPACK_ERR_WRITE; + } + out_bytes -= i; + qtm->o_ptr = &window[0]; + qtm->o_end = &window[0]; + + /* copy second part of match, after window wrap */ + rundest = &window[0]; + i = match_length - (qtm->window_size - window_posn); + while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; + window_posn = window_posn + match_length - qtm->window_size; + + break; /* because "window_posn < frame_end" has now failed */ + } + else { + /* normal match - output won't wrap window or frame end */ + i = match_length; + + /* does match _offset_ wrap the window? */ + if (match_offset > window_posn) { + /* j = length from match offset to end of window */ + j = match_offset - window_posn; + if (j > (int) qtm->window_size) { + D(("match offset beyond window boundaries")) + return qtm->error = MSPACK_ERR_DECRUNCH; + } + runsrc = &window[qtm->window_size - j]; + if (j < i) { + /* if match goes over the window edge, do two copy runs */ + i -= j; while (j-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + while (i-- > 0) *rundest++ = *runsrc++; + } + else { + runsrc = rundest - match_offset; + while (i-- > 0) *rundest++ = *runsrc++; + } + window_posn += match_length; + } + } /* if (window_posn+match_length > frame_end) */ + } /* while (window_posn < frame_end) */ + + qtm->o_end = &window[window_posn]; + + /* if we subtracted too much from frame_todo, it will + * wrap around past zero and go above its max value */ + if (frame_todo > QTM_FRAME_SIZE) { + D(("overshot frame alignment")) + return qtm->error = MSPACK_ERR_DECRUNCH; + } + + /* another frame completed? */ + if (frame_todo == 0) { + /* re-align input */ + if (bits_left & 7) REMOVE_BITS(bits_left & 7); + + /* special Quantum hack -- cabd.c injects a trailer byte to allow the + * decompressor to realign itself. CAB Quantum blocks, unlike LZX + * blocks, can have anything from 0 to 4 trailing null bytes. */ + do { READ_BITS(i, 8); } while (i != 0xFF); + + qtm->header_read = 0; + + frame_todo = QTM_FRAME_SIZE; + } + + /* window wrap? */ + if (window_posn == qtm->window_size) { + /* flush all currently stored data */ + i = (qtm->o_end - qtm->o_ptr); + /* break out if we have more than enough to finish this request */ + if (i >= out_bytes) break; + if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { + return qtm->error = MSPACK_ERR_WRITE; + } + out_bytes -= i; + qtm->o_ptr = &window[0]; + qtm->o_end = &window[0]; + window_posn = 0; + } + + } /* while (more bytes needed) */ + + if (out_bytes) { + i = (int) out_bytes; + if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { + return qtm->error = MSPACK_ERR_WRITE; + } + qtm->o_ptr += i; + } + + /* store local state */ + + STORE_BITS; + qtm->window_posn = window_posn; + qtm->frame_todo = frame_todo; + qtm->H = H; + qtm->L = L; + qtm->C = C; + + return MSPACK_ERR_OK; +} + +void qtmd_free(struct qtmd_stream *qtm) { + struct mspack_system *sys; + if (qtm) { + sys = qtm->sys; + sys->free(qtm->window); + sys->free(qtm->inbuf); + sys->free(qtm); + } +} diff --git a/rbutil/rbutilqt/mspack/readbits.h b/rbutil/rbutilqt/mspack/readbits.h new file mode 100644 index 0000000000..457cbdd7d4 --- /dev/null +++ b/rbutil/rbutilqt/mspack/readbits.h @@ -0,0 +1,207 @@ +/* This file is part of libmspack. + * (C) 2003-2010 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_READBITS_H +#define MSPACK_READBITS_H 1 + +/* this header defines macros that read data streams by + * the individual bits + * + * INIT_BITS initialises bitstream state in state structure + * STORE_BITS stores bitstream state in state structure + * RESTORE_BITS restores bitstream state from state structure + * ENSURE_BITS(n) ensure there are at least N bits in the bit buffer + * READ_BITS(var,n) takes N bits from the buffer and puts them in var + * PEEK_BITS(n) extracts without removing N bits from the bit buffer + * REMOVE_BITS(n) removes N bits from the bit buffer + * + * READ_BITS simply calls ENSURE_BITS, PEEK_BITS and REMOVE_BITS, + * which means it's limited to reading the number of bits you can + * ensure at any one time. It also fails if asked to read zero bits. + * If you need to read zero bits, or more bits than can be ensured in + * one go, use READ_MANY_BITS instead. + * + * These macros have variable names baked into them, so to use them + * you have to define some macros: + * - BITS_TYPE: the type name of your state structure + * - BITS_VAR: the variable that points to your state structure + * - define BITS_ORDER_MSB if bits are read from the MSB, or + * define BITS_ORDER_LSB if bits are read from the LSB + * - READ_BYTES: some code that reads more data into the bit buffer, + * it should use READ_IF_NEEDED (calls read_input if the byte buffer + * is empty), then INJECT_BITS(data,n) to put data from the byte + * buffer into the bit buffer. + * + * You also need to define some variables and structure members: + * - unsigned char *i_ptr; // current position in the byte buffer + * - unsigned char *i_end; // end of the byte buffer + * - unsigned int bit_buffer; // the bit buffer itself + * - unsigned int bits_left; // number of bits remaining + * + * If you use read_input() and READ_IF_NEEDED, they also expect these + * structure members: + * - struct mspack_system *sys; // to access sys->read() + * - unsigned int error; // to record/return read errors + * - unsigned char input_end; // to mark reaching the EOF + * - unsigned char *inbuf; // the input byte buffer + * - unsigned int inbuf_size; // the size of the input byte buffer + * + * Your READ_BYTES implementation should read data from *i_ptr and + * put them in the bit buffer. READ_IF_NEEDED will call read_input() + * if i_ptr reaches i_end, and will fill up inbuf and set i_ptr to + * the start of inbuf and i_end to the end of inbuf. + * + * If you're reading in MSB order, the routines work by using the area + * beyond the MSB and the LSB of the bit buffer as a free source of + * zeroes when shifting. This avoids having to mask any bits. So we + * have to know the bit width of the bit buffer variable. We use + * and CHAR_BIT to find the size of the bit buffer in bits. + * + * If you are reading in LSB order, bits need to be masked. Normally + * this is done by computing the mask: N bits are masked by the value + * (1< +#endif +#ifndef CHAR_BIT +# define CHAR_BIT (8) +#endif +#define BITBUF_WIDTH (sizeof(bit_buffer) * CHAR_BIT) + +#define INIT_BITS do { \ + BITS_VAR->i_ptr = &BITS_VAR->inbuf[0]; \ + BITS_VAR->i_end = &BITS_VAR->inbuf[0]; \ + BITS_VAR->bit_buffer = 0; \ + BITS_VAR->bits_left = 0; \ + BITS_VAR->input_end = 0; \ +} while (0) + +#define STORE_BITS do { \ + BITS_VAR->i_ptr = i_ptr; \ + BITS_VAR->i_end = i_end; \ + BITS_VAR->bit_buffer = bit_buffer; \ + BITS_VAR->bits_left = bits_left; \ +} while (0) + +#define RESTORE_BITS do { \ + i_ptr = BITS_VAR->i_ptr; \ + i_end = BITS_VAR->i_end; \ + bit_buffer = BITS_VAR->bit_buffer; \ + bits_left = BITS_VAR->bits_left; \ +} while (0) + +#define ENSURE_BITS(nbits) do { \ + while (bits_left < (nbits)) READ_BYTES; \ +} while (0) + +#define READ_BITS(val, nbits) do { \ + ENSURE_BITS(nbits); \ + (val) = PEEK_BITS(nbits); \ + REMOVE_BITS(nbits); \ +} while (0) + +#define READ_MANY_BITS(val, bits) do { \ + unsigned char needed = (bits), bitrun; \ + (val) = 0; \ + while (needed > 0) { \ + if (bits_left <= (BITBUF_WIDTH - 16)) READ_BYTES; \ + bitrun = (bits_left < needed) ? bits_left : needed; \ + (val) = ((val) << bitrun) | PEEK_BITS(bitrun); \ + REMOVE_BITS(bitrun); \ + needed -= bitrun; \ + } \ +} while (0) + +#ifdef BITS_ORDER_MSB +# define PEEK_BITS(nbits) (bit_buffer >> (BITBUF_WIDTH - (nbits))) +# define REMOVE_BITS(nbits) ((bit_buffer <<= (nbits)), (bits_left -= (nbits))) +# define INJECT_BITS(bitdata,nbits) ((bit_buffer |= \ + (bitdata) << (BITBUF_WIDTH - (nbits) - bits_left)), (bits_left += (nbits))) +#else /* BITS_ORDER_LSB */ +# define PEEK_BITS(nbits) (bit_buffer & ((1 << (nbits))-1)) +# define REMOVE_BITS(nbits) ((bit_buffer >>= (nbits)), (bits_left -= (nbits))) +# define INJECT_BITS(bitdata,nbits) ((bit_buffer |= \ + (bitdata) << bits_left), (bits_left += (nbits))) +#endif + +#ifdef BITS_LSB_TABLE +/* lsb_bit_mask[n] = (1 << n) - 1 */ +static const unsigned short lsb_bit_mask[17] = { + 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; +# define PEEK_BITS_T(nbits) (bit_buffer & lsb_bit_mask[(nbits)]) +# define READ_BITS_T(val, nbits) do { \ + ENSURE_BITS(nbits); \ + (val) = PEEK_BITS_T(nbits); \ + REMOVE_BITS(nbits); \ +} while (0) +#endif + +#ifndef BITS_NO_READ_INPUT +# define READ_IF_NEEDED do { \ + if (i_ptr >= i_end) { \ + if (read_input(BITS_VAR)) \ + return BITS_VAR->error; \ + i_ptr = BITS_VAR->i_ptr; \ + i_end = BITS_VAR->i_end; \ + } \ +} while (0) + +static int read_input(BITS_TYPE *p) { + int read = p->sys->read(p->input, &p->inbuf[0], (int)p->inbuf_size); + if (read < 0) return p->error = MSPACK_ERR_READ; + + /* we might overrun the input stream by asking for bits we don't use, + * so fake 2 more bytes at the end of input */ + if (read == 0) { + if (p->input_end) { + D(("out of input bytes")) + return p->error = MSPACK_ERR_READ; + } + else { + read = 2; + p->inbuf[0] = p->inbuf[1] = 0; + p->input_end = 1; + } + } + + /* update i_ptr and i_end */ + p->i_ptr = &p->inbuf[0]; + p->i_end = &p->inbuf[read]; + return MSPACK_ERR_OK; +} +#endif +#endif diff --git a/rbutil/rbutilqt/mspack/readhuff.h b/rbutil/rbutilqt/mspack/readhuff.h new file mode 100644 index 0000000000..bb15c0a123 --- /dev/null +++ b/rbutil/rbutilqt/mspack/readhuff.h @@ -0,0 +1,173 @@ +/* This file is part of libmspack. + * (C) 2003-2010 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_READHUFF_H +#define MSPACK_READHUFF_H 1 + +/* This implements a fast Huffman tree decoding system. + */ + +#if !(defined(BITS_ORDER_MSB) || defined(BITS_ORDER_LSB)) +# error "readhuff.h is used in conjunction with readbits.h, include that first" +#endif +#if !(defined(TABLEBITS) && defined(MAXSYMBOLS)) +# error "define TABLEBITS(tbl) and MAXSYMBOLS(tbl) before using readhuff.h" +#endif +#if !(defined(HUFF_TABLE) && defined(HUFF_LEN)) +# error "define HUFF_TABLE(tbl) and HUFF_LEN(tbl) before using readhuff.h" +#endif +#ifndef HUFF_ERROR +# error "define HUFF_ERROR before using readhuff.h" +#endif +#ifndef HUFF_MAXBITS +# define HUFF_MAXBITS 16 +#endif + +/* Decodes the next huffman symbol from the input bitstream into var. + * Do not use this macro on a table unless build_decode_table() succeeded. + */ +#define READ_HUFFSYM(tbl, var) do { \ + ENSURE_BITS(HUFF_MAXBITS); \ + sym = HUFF_TABLE(tbl, PEEK_BITS(TABLEBITS(tbl))); \ + if (sym >= MAXSYMBOLS(tbl)) HUFF_TRAVERSE(tbl); \ + (var) = sym; \ + i = HUFF_LEN(tbl, sym); \ + REMOVE_BITS(i); \ +} while (0) + +#ifdef BITS_ORDER_LSB +# define HUFF_TRAVERSE(tbl) do { \ + i = TABLEBITS(tbl) - 1; \ + do { \ + if (i++ > HUFF_MAXBITS) HUFF_ERROR; \ + sym = HUFF_TABLE(tbl, \ + (sym << 1) | ((bit_buffer >> i) & 1)); \ + } while (sym >= MAXSYMBOLS(tbl)); \ +} while (0) +#else +#define HUFF_TRAVERSE(tbl) do { \ + i = 1 << (BITBUF_WIDTH - TABLEBITS(tbl)); \ + do { \ + if ((i >>= 1) == 0) HUFF_ERROR; \ + sym = HUFF_TABLE(tbl, \ + (sym << 1) | ((bit_buffer & i) ? 1 : 0)); \ + } while (sym >= MAXSYMBOLS(tbl)); \ +} while (0) +#endif + +/* make_decode_table(nsyms, nbits, length[], table[]) + * + * This function was originally coded by David Tritscher. + * It builds a fast huffman decoding table from + * a canonical huffman code lengths table. + * + * nsyms = total number of symbols in this huffman tree. + * nbits = any symbols with a code length of nbits or less can be decoded + * in one lookup of the table. + * length = A table to get code lengths from [0 to nsyms-1] + * table = The table to fill up with decoded symbols and pointers. + * Should be ((1<> 1; /* don't do 0 length codes */ + + /* fill entries for codes short enough for a direct mapping */ + for (bit_num = 1; bit_num <= nbits; bit_num++) { + for (sym = 0; sym < nsyms; sym++) { + if (length[sym] != bit_num) continue; +#ifdef BITS_ORDER_MSB + leaf = pos; +#else + /* reverse the significant bits */ + fill = length[sym]; reverse = pos >> (nbits - fill); leaf = 0; + do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); +#endif + + if((pos += bit_mask) > table_mask) return 1; /* table overrun */ + + /* fill all possible lookups of this symbol with the symbol itself */ +#ifdef BITS_ORDER_MSB + for (fill = bit_mask; fill-- > 0;) table[leaf++] = sym; +#else + fill = bit_mask; next_symbol = 1 << bit_num; + do { table[leaf] = sym; leaf += next_symbol; } while (--fill); +#endif + } + bit_mask >>= 1; + } + + /* exit with success if table is now complete */ + if (pos == table_mask) return 0; + + /* mark all remaining table entries as unused */ + for (sym = pos; sym < table_mask; sym++) { +#ifdef BITS_ORDER_MSB + table[sym] = 0xFFFF; +#else + reverse = sym; leaf = 0; fill = nbits; + do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill); + table[leaf] = 0xFFFF; +#endif + } + + /* next_symbol = base of allocation for long codes */ + next_symbol = ((table_mask >> 1) < nsyms) ? nsyms : (table_mask >> 1); + + /* give ourselves room for codes to grow by up to 16 more bits. + * codes now start at bit nbits+16 and end at (nbits+16-codelength) */ + pos <<= 16; + table_mask <<= 16; + bit_mask = 1 << 15; + + for (bit_num = nbits+1; bit_num <= HUFF_MAXBITS; bit_num++) { + for (sym = 0; sym < nsyms; sym++) { + if (length[sym] != bit_num) continue; + +#ifdef BITS_ORDER_MSB + leaf = pos >> 16; +#else + /* leaf = the first nbits of the code, reversed */ + reverse = pos >> 16; leaf = 0; fill = nbits; + do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); +#endif + for (fill = 0; fill < (bit_num - nbits); fill++) { + /* if this path hasn't been taken yet, 'allocate' two entries */ + if (table[leaf] == 0xFFFF) { + table[(next_symbol << 1) ] = 0xFFFF; + table[(next_symbol << 1) + 1 ] = 0xFFFF; + table[leaf] = next_symbol++; + } + + /* follow the path and select either left or right for next bit */ + leaf = table[leaf] << 1; + if ((pos >> (15-fill)) & 1) leaf++; + } + table[leaf] = sym; + + if ((pos += bit_mask) > table_mask) return 1; /* table overflow */ + } + bit_mask >>= 1; + } + + /* full table? */ + return (pos == table_mask) ? 0 : 1; +} +#endif diff --git a/rbutil/rbutilqt/mspack/sha.h b/rbutil/rbutilqt/mspack/sha.h new file mode 100644 index 0000000000..360521519b --- /dev/null +++ b/rbutil/rbutilqt/mspack/sha.h @@ -0,0 +1,15 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_LZSS_H +#define MSPACK_LZSS_H 1 + +/* SHA-1 message digest definitions */ + +#endif diff --git a/rbutil/rbutilqt/mspack/system.c b/rbutil/rbutilqt/mspack/system.c new file mode 100644 index 0000000000..f3f0019f5e --- /dev/null +++ b/rbutil/rbutilqt/mspack/system.c @@ -0,0 +1,237 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifndef LARGEFILE_SUPPORT +const char *largefile_msg = "library not compiled to support large files."; +#endif + + +int mspack_version(int entity) { + switch (entity) { + /* CHM decoder version 1 -> 2 changes: + * - added mschmd_sec_mscompressed::spaninfo + * - added mschmd_header::first_pmgl + * - added mschmd_header::last_pmgl + * - added mschmd_header::chunk_cache; + */ + case MSPACK_VER_MSCHMD: + return 2; + case MSPACK_VER_LIBRARY: + case MSPACK_VER_SYSTEM: + case MSPACK_VER_MSCABD: + case MSPACK_VER_MSSZDDD: + case MSPACK_VER_MSKWAJD: + return 1; + case MSPACK_VER_MSCABC: + case MSPACK_VER_MSCHMC: + case MSPACK_VER_MSLITD: + case MSPACK_VER_MSLITC: + case MSPACK_VER_MSHLPD: + case MSPACK_VER_MSHLPC: + case MSPACK_VER_MSSZDDC: + case MSPACK_VER_MSKWAJC: + return 0; + } + return -1; +} + +int mspack_sys_selftest_internal(int offt_size) { + return (sizeof(off_t) == offt_size) ? MSPACK_ERR_OK : MSPACK_ERR_SEEK; +} + +/* validates a system structure */ +int mspack_valid_system(struct mspack_system *sys) { + return (sys != NULL) && (sys->open != NULL) && (sys->close != NULL) && + (sys->read != NULL) && (sys->write != NULL) && (sys->seek != NULL) && + (sys->tell != NULL) && (sys->message != NULL) && (sys->alloc != NULL) && + (sys->free != NULL) && (sys->copy != NULL) && (sys->null_ptr == NULL); +} + +/* returns the length of a file opened for reading */ +int mspack_sys_filelen(struct mspack_system *system, + struct mspack_file *file, off_t *length) +{ + off_t current; + + if (!system || !file || !length) return MSPACK_ERR_OPEN; + + /* get current offset */ + current = system->tell(file); + + /* seek to end of file */ + if (system->seek(file, (off_t) 0, MSPACK_SYS_SEEK_END)) { + return MSPACK_ERR_SEEK; + } + + /* get offset of end of file */ + *length = system->tell(file); + + /* seek back to original offset */ + if (system->seek(file, current, MSPACK_SYS_SEEK_START)) { + return MSPACK_ERR_SEEK; + } + + return MSPACK_ERR_OK; +} + + + +/* definition of mspack_default_system -- if the library is compiled with + * MSPACK_NO_DEFAULT_SYSTEM, no default system will be provided. Otherwise, + * an appropriate default system (e.g. the standard C library, or some native + * API calls) + */ + +#ifdef MSPACK_NO_DEFAULT_SYSTEM +struct mspack_system *mspack_default_system = NULL; +#else + +/* implementation of mspack_default_system for standard C library */ + +#include +#include +#include +#include + +struct mspack_file_p { + FILE *fh; + const char *name; +}; + +static struct mspack_file *msp_open(struct mspack_system *self, + const char *filename, int mode) +{ + struct mspack_file_p *fh; + const char *fmode; + + switch (mode) { + case MSPACK_SYS_OPEN_READ: fmode = "rb"; break; + case MSPACK_SYS_OPEN_WRITE: fmode = "wb"; break; + case MSPACK_SYS_OPEN_UPDATE: fmode = "r+b"; break; + case MSPACK_SYS_OPEN_APPEND: fmode = "ab"; break; + default: return NULL; + } + + if ((fh = (struct mspack_file_p *) malloc(sizeof(struct mspack_file_p)))) { + fh->name = filename; + if ((fh->fh = fopen(filename, fmode))) return (struct mspack_file *) fh; + free(fh); + } + return NULL; +} + +static void msp_close(struct mspack_file *file) { + struct mspack_file_p *self = (struct mspack_file_p *) file; + if (self) { + fclose(self->fh); + free(self); + } +} + +static int msp_read(struct mspack_file *file, void *buffer, int bytes) { + struct mspack_file_p *self = (struct mspack_file_p *) file; + if (self && buffer && bytes >= 0) { + size_t count = fread(buffer, 1, (size_t) bytes, self->fh); + if (!ferror(self->fh)) return (int) count; + } + return -1; +} + +static int msp_write(struct mspack_file *file, void *buffer, int bytes) { + struct mspack_file_p *self = (struct mspack_file_p *) file; + if (self && buffer && bytes >= 0) { + size_t count = fwrite(buffer, 1, (size_t) bytes, self->fh); + if (!ferror(self->fh)) return (int) count; + } + return -1; +} + +static int msp_seek(struct mspack_file *file, off_t offset, int mode) { + struct mspack_file_p *self = (struct mspack_file_p *) file; + if (self) { + switch (mode) { + case MSPACK_SYS_SEEK_START: mode = SEEK_SET; break; + case MSPACK_SYS_SEEK_CUR: mode = SEEK_CUR; break; + case MSPACK_SYS_SEEK_END: mode = SEEK_END; break; + default: return -1; + } +#ifdef HAVE_FSEEKO + return fseeko(self->fh, offset, mode); +#else + return fseek(self->fh, offset, mode); +#endif + } + return -1; +} + +static off_t msp_tell(struct mspack_file *file) { + struct mspack_file_p *self = (struct mspack_file_p *) file; +#ifdef HAVE_FSEEKO + return (self) ? (off_t) ftello(self->fh) : 0; +#else + return (self) ? (off_t) ftell(self->fh) : 0; +#endif +} + +static void msp_msg(struct mspack_file *file, const char *format, ...) { + va_list ap; + if (file) fprintf(stderr, "%s: ", ((struct mspack_file_p *) file)->name); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fputc((int) '\n', stderr); + fflush(stderr); +} + +static void *msp_alloc(struct mspack_system *self, size_t bytes) { +#ifdef DEBUG + /* make uninitialised data obvious */ + char *buf = malloc(bytes + 8); + if (buf) memset(buf, 0xDC, bytes); + *((size_t *)buf) = bytes; + return &buf[8]; +#else + return malloc(bytes); +#endif +} + +static void msp_free(void *buffer) { +#ifdef DEBUG + char *buf = buffer; + size_t bytes; + if (buf) { + buf -= 8; + bytes = *((size_t *)buf); + /* make freed data obvious */ + memset(buf, 0xED, bytes); + free(buf); + } +#else + free(buffer); +#endif +} + +static void msp_copy(void *src, void *dest, size_t bytes) { + memcpy(dest, src, bytes); +} + +static struct mspack_system msp_system = { + &msp_open, &msp_close, &msp_read, &msp_write, &msp_seek, + &msp_tell, &msp_msg, &msp_alloc, &msp_free, &msp_copy, NULL +}; + +struct mspack_system *mspack_default_system = &msp_system; + +#endif diff --git a/rbutil/rbutilqt/mspack/system.h b/rbutil/rbutilqt/mspack/system.h new file mode 100644 index 0000000000..4a400850aa --- /dev/null +++ b/rbutil/rbutilqt/mspack/system.h @@ -0,0 +1,124 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_SYSTEM_H +#define MSPACK_SYSTEM_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* ensure config.h is read before mspack.h */ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +/* fix for problem with GCC 4 and glibc (thanks to Ville Skytta) + * http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=150429 + */ +#ifdef read +# undef read +#endif + +#ifdef DEBUG +# include +/* Old GCCs don't have __func__, but __FUNCTION__: + * http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html + */ +# if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif +# endif +# define D(x) do { printf("%s:%d (%s) ",__FILE__, __LINE__, __func__); \ + printf x ; fputc('\n', stdout); fflush(stdout);} while (0); +#else +# define D(x) +#endif + +/* CAB supports searching through files over 4GB in size, and the CHM file + * format actively uses 64-bit offsets. These can only be fully supported + * if the system the code runs on supports large files. If not, the library + * will work as normal using only 32-bit arithmetic, but if an offset + * greater than 2GB is detected, an error message indicating the library + * can't support the file should be printed. + */ +#ifdef HAVE_LIMITS_H +# include +#endif + +#if ((defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS >= 64) || \ + (defined(FILESIZEBITS) && FILESIZEBITS >= 64) || \ + (defined(SIZEOF_OFF_T) && SIZEOF_OFF_T >= 8) || \ + defined(_LARGEFILE_SOURCE) || defined(_LARGEFILE64_SOURCE)) +# define LARGEFILE_SUPPORT +# define LD "lld" +# define LU "llu" +#else +extern const char *largefile_msg; +# define LD "ld" +# define LU "lu" +#endif + +/* endian-neutral reading of little-endian data */ +#define __egi32(a,n) ( ((((unsigned char *) a)[n+3]) << 24) | \ + ((((unsigned char *) a)[n+2]) << 16) | \ + ((((unsigned char *) a)[n+1]) << 8) | \ + ((((unsigned char *) a)[n+0]))) +#define EndGetI64(a) ((((unsigned long long int) __egi32(a,4)) << 32) | \ + ((unsigned int) __egi32(a,0))) +#define EndGetI32(a) __egi32(a,0) +#define EndGetI16(a) ((((a)[1])<<8)|((a)[0])) + +/* endian-neutral reading of big-endian data */ +#define EndGetM32(a) (((((unsigned char *) a)[0]) << 24) | \ + ((((unsigned char *) a)[1]) << 16) | \ + ((((unsigned char *) a)[2]) << 8) | \ + ((((unsigned char *) a)[3]))) +#define EndGetM16(a) ((((a)[0])<<8)|((a)[1])) + +extern struct mspack_system *mspack_default_system; + +/* returns the length of a file opened for reading */ +extern int mspack_sys_filelen(struct mspack_system *system, + struct mspack_file *file, off_t *length); + +/* validates a system structure */ +extern int mspack_valid_system(struct mspack_system *sys); + +#if HAVE_STRINGS_H +# include +#endif + +#if HAVE_STRING_H +# include +#endif + +#if HAVE_MEMCMP +# define mspack_memcmp memcmp +#else +/* inline memcmp() */ +static inline int mspack_memcmp(const void *s1, const void *s2, size_t n) { + unsigned char *c1 = (unsigned char *) s1; + unsigned char *c2 = (unsigned char *) s2; + if (n == 0) return 0; + while (--n && (*c1 == *c2)) c1++, c2++; + return *c1 - *c2; +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/rbutil/rbutilqt/mspack/szdd.h b/rbutil/rbutilqt/mspack/szdd.h new file mode 100644 index 0000000000..e07c6b7c8e --- /dev/null +++ b/rbutil/rbutilqt/mspack/szdd.h @@ -0,0 +1,39 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_SZDD_H +#define MSPACK_SZDD_H 1 + +#include + +/* input buffer size during decompression - not worth parameterising IMHO */ +#define SZDD_INPUT_SIZE (2048) + +/* SZDD compression definitions */ + +struct msszdd_compressor_p { + struct msszdd_compressor base; + struct mspack_system *system; + int error; +}; + +/* SZDD decompression definitions */ + +struct msszdd_decompressor_p { + struct msszdd_decompressor base; + struct mspack_system *system; + int error; +}; + +struct msszddd_header_p { + struct msszddd_header base; + struct mspack_file *fh; +}; + +#endif diff --git a/rbutil/rbutilqt/mspack/szddc.c b/rbutil/rbutilqt/mspack/szddc.c new file mode 100644 index 0000000000..cdd39a6305 --- /dev/null +++ b/rbutil/rbutilqt/mspack/szddc.c @@ -0,0 +1,24 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* SZDD compression implementation */ + +#include +#include + +struct msszdd_compressor * + mspack_create_szdd_compressor(struct mspack_system *sys) +{ + /* todo */ + return NULL; +} + +void mspack_destroy_szdd_compressor(struct msszdd_compressor *self) { + /* todo */ +} diff --git a/rbutil/rbutilqt/mspack/szddd.c b/rbutil/rbutilqt/mspack/szddd.c new file mode 100644 index 0000000000..99c5aa4658 --- /dev/null +++ b/rbutil/rbutilqt/mspack/szddd.c @@ -0,0 +1,247 @@ +/* This file is part of libmspack. + * (C) 2003-2010 Stuart Caie. + * + * SZDD is a format used in the MS-DOS commands COMPRESS.EXE and + * EXPAND.EXE. The compression method is attributed to Steven Zeck, + * however it's pretty much identical to LZSS. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* SZDD decompression implementation */ + +#include +#include + +/* prototypes */ +static struct msszddd_header *szddd_open( + struct msszdd_decompressor *base, const char *filename); +static void szddd_close( + struct msszdd_decompressor *base, struct msszddd_header *hdr); +static int szddd_read_headers( + struct mspack_system *sys, struct mspack_file *fh, + struct msszddd_header *hdr); +static int szddd_extract( + struct msszdd_decompressor *base, struct msszddd_header *hdr, + const char *filename); +static int szddd_decompress( + struct msszdd_decompressor *base, const char *input, const char *output); +static int szddd_error( + struct msszdd_decompressor *base); + +/*************************************** + * MSPACK_CREATE_SZDD_DECOMPRESSOR + *************************************** + * constructor + */ +struct msszdd_decompressor * + mspack_create_szdd_decompressor(struct mspack_system *sys) +{ + struct msszdd_decompressor_p *self = NULL; + + if (!sys) sys = mspack_default_system; + if (!mspack_valid_system(sys)) return NULL; + + if ((self = (struct msszdd_decompressor_p *) sys->alloc(sys, sizeof(struct msszdd_decompressor_p)))) { + self->base.open = &szddd_open; + self->base.close = &szddd_close; + self->base.extract = &szddd_extract; + self->base.decompress = &szddd_decompress; + self->base.last_error = &szddd_error; + self->system = sys; + self->error = MSPACK_ERR_OK; + } + return (struct msszdd_decompressor *) self; +} + +/*************************************** + * MSPACK_DESTROY_SZDD_DECOMPRESSOR + *************************************** + * destructor + */ +void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *base) +{ + struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base; + if (self) { + struct mspack_system *sys = self->system; + sys->free(self); + } +} + +/*************************************** + * SZDDD_OPEN + *************************************** + * opens an SZDD file without decompressing, reads header + */ +static struct msszddd_header *szddd_open(struct msszdd_decompressor *base, + const char *filename) +{ + struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base; + struct msszddd_header *hdr; + struct mspack_system *sys; + struct mspack_file *fh; + + if (!self) return NULL; + sys = self->system; + + fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ); + hdr = (struct msszddd_header *) sys->alloc(sys, sizeof(struct msszddd_header_p)); + if (fh && hdr) { + ((struct msszddd_header_p *) hdr)->fh = fh; + self->error = szddd_read_headers(sys, fh, hdr); + } + else { + if (!fh) self->error = MSPACK_ERR_OPEN; + if (!hdr) self->error = MSPACK_ERR_NOMEMORY; + } + + if (self->error) { + if (fh) sys->close(fh); + if (hdr) sys->free(hdr); + hdr = NULL; + } + + return hdr; +} + +/*************************************** + * SZDDD_CLOSE + *************************************** + * closes an SZDD file + */ +static void szddd_close(struct msszdd_decompressor *base, + struct msszddd_header *hdr) +{ + struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base; + struct msszddd_header_p *hdr_p = (struct msszddd_header_p *) hdr; + + if (!self || !self->system) return; + + /* close the file handle associated */ + self->system->close(hdr_p->fh); + + /* free the memory associated */ + self->system->free(hdr); + + self->error = MSPACK_ERR_OK; +} + +/*************************************** + * SZDDD_READ_HEADERS + *************************************** + * reads the headers of an SZDD format file + */ +static unsigned char szdd_signature_expand[8] = { + 0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33 +}; +static unsigned char szdd_signature_qbasic[8] = { + 0x53, 0x5A, 0x20, 0x88, 0xF0, 0x27, 0x33, 0xD1 +}; + +static int szddd_read_headers(struct mspack_system *sys, + struct mspack_file *fh, + struct msszddd_header *hdr) +{ + unsigned char buf[8]; + + /* read and check signature */ + if (sys->read(fh, buf, 8) != 8) return MSPACK_ERR_READ; + + if ((mspack_memcmp(buf, szdd_signature_expand, 8) == 0)) { + /* common SZDD */ + hdr->format = MSSZDD_FMT_NORMAL; + + /* read the rest of the header */ + if (sys->read(fh, buf, 6) != 6) return MSPACK_ERR_READ; + if (buf[0] != 0x41) return MSPACK_ERR_DATAFORMAT; + hdr->missing_char = buf[1]; + hdr->length = EndGetI32(&buf[2]); + } + else if ((mspack_memcmp(buf, szdd_signature_qbasic, 8) == 0)) { + /* special QBasic SZDD */ + hdr->format = MSSZDD_FMT_QBASIC; + if (sys->read(fh, buf, 4) != 4) return MSPACK_ERR_READ; + hdr->missing_char = '\0'; + hdr->length = EndGetI32(buf); + } + else { + return MSPACK_ERR_SIGNATURE; + } + return MSPACK_ERR_OK; +} + +/*************************************** + * SZDDD_EXTRACT + *************************************** + * decompresses an SZDD file + */ +static int szddd_extract(struct msszdd_decompressor *base, + struct msszddd_header *hdr, const char *filename) +{ + struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base; + struct mspack_file *fh, *outfh; + struct mspack_system *sys; + off_t data_offset; + + if (!self) return MSPACK_ERR_ARGS; + if (!hdr) return self->error = MSPACK_ERR_ARGS; + sys = self->system; + + fh = ((struct msszddd_header_p *) hdr)->fh; + + /* seek to the compressed data */ + data_offset = (hdr->format == MSSZDD_FMT_NORMAL) ? 14 : 12; + if (sys->seek(fh, data_offset, MSPACK_SYS_SEEK_START)) { + return self->error = MSPACK_ERR_SEEK; + } + + /* open file for output */ + if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { + return self->error = MSPACK_ERR_OPEN; + } + + /* decompress the data */ + self->error = lzss_decompress(sys, fh, outfh, SZDD_INPUT_SIZE, + hdr->format == MSSZDD_FMT_NORMAL + ? LZSS_MODE_EXPAND + : LZSS_MODE_QBASIC); + + /* close output file */ + sys->close(outfh); + + return self->error; +} + +/*************************************** + * SZDDD_DECOMPRESS + *************************************** + * unpacks directly from input to output + */ +static int szddd_decompress(struct msszdd_decompressor *base, + const char *input, const char *output) +{ + struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base; + struct msszddd_header *hdr; + int error; + + if (!self) return MSPACK_ERR_ARGS; + + if (!(hdr = szddd_open(base, input))) return self->error; + error = szddd_extract(base, hdr, output); + szddd_close(base, hdr); + return self->error = error; +} + +/*************************************** + * SZDDD_ERROR + *************************************** + * returns the last error that occurred + */ +static int szddd_error(struct msszdd_decompressor *base) +{ + struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base; + return (self) ? self->error : MSPACK_ERR_ARGS; +} -- cgit v1.2.3