Add libmspack to rbutil

Change-Id: I520c14131ec1e12013f106c13cba00aac058ad83
Reviewed-on: http://gerrit.rockbox.org/391
Reviewed-by: Dominik Riebeling <Dominik.Riebeling@gmail.com>
This commit is contained in:
Amaury Pouly 2013-03-11 18:46:03 +01:00 committed by Dominik Riebeling
parent 27111d83be
commit 739a7ae0e9
37 changed files with 10082 additions and 0 deletions

View file

@ -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.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View file

@ -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

View file

@ -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 <mszip.h>
#include <qtm.h>
#include <lzx.h>
/* 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

View file

@ -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 <system.h>
#include <cab.h>
struct mscab_compressor *
mspack_create_cab_compressor(struct mspack_system *sys)
{
/* todo */
return NULL;
}
void mspack_destroy_cab_compressor(struct mscab_compressor *self) {
/* todo */
}

File diff suppressed because it is too large Load diff

View file

@ -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 <lzx.h>
/* 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

View file

@ -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 <system.h>
#include <chm.h>
struct mschm_compressor *
mspack_create_chm_compressor(struct mspack_system *sys)
{
/* todo */
return NULL;
}
void mspack_destroy_chm_compressor(struct mschm_compressor *self) {
/* todo */
}

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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 <lzss.h>
/* 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

View file

@ -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 <system.h>
#include <hlp.h>
struct mshlp_compressor *
mspack_create_hlp_compressor(struct mspack_system *sys)
{
/* todo */
return NULL;
}
void mspack_destroy_hlp_compressor(struct mshlp_compressor *self) {
/* todo */
}

View file

@ -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 <system.h>
#include <hlp.h>
struct mshlp_decompressor *
mspack_create_hlp_decompressor(struct mspack_system *sys)
{
/* todo */
return NULL;
}
void mspack_destroy_hlp_decompressor(struct mshlp_decompressor *self) {
/* todo */
}

View file

@ -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 <lzss.h>
/* 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

View file

@ -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 <system.h>
#include <kwaj.h>
struct mskwaj_compressor *
mspack_create_kwaj_compressor(struct mspack_system *sys)
{
/* todo */
return NULL;
}
void mspack_destroy_kwaj_compressor(struct mskwaj_compressor *self) {
/* todo */
}

View file

@ -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 <system.h>
#include <kwaj.h>
/* 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 <readbits.h>
/* 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 <readhuff.h>
/* 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;
}

View file

@ -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 <lzx.h>
#include <des.h>
#include <sha.h>
/* 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

View file

@ -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 <system.h>
#include <lit.h>
struct mslit_compressor *
mspack_create_lit_compressor(struct mspack_system *sys)
{
/* todo */
return NULL;
}
void mspack_destroy_lit_compressor(struct mslit_compressor *self) {
/* todo */
}

View file

@ -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 <system.h>
#include <lit.h>
struct mslit_decompressor *
mspack_create_lit_decompressor(struct mspack_system *sys)
{
/* todo */
return NULL;
}
void mspack_destroy_lit_decompressor(struct mslit_decompressor *self) {
/* todo */
}

View file

@ -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

View file

@ -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 <system.h>
#include <lzss.h>
#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;
}

View file

@ -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

View file

@ -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 <system.h>
#include <lzx.h>
/* todo */

View file

@ -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 <system.h>
#include <lzx.h>
/* 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 <readbits.h>
/* 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 <readhuff.h>
/* 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);
}
}

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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 <system.h>
#include <mszip.h>
/* todo */

View file

@ -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 <system.h>
#include <mszip.h>
/* 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 <readbits.h>
/* 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 <readhuff.h>
#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);
}
}

View file

@ -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

View file

@ -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 <system.h>
#include <qtm.h>
/* 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 <readbits.h>
/* 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);
}
}

View file

@ -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
* <limits.h> 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<<N)-1). However, you can define BITS_LSB_TABLE to use a lookup
* table instead of computing this. This adds two new macros,
* PEEK_BITS_T and READ_BITS_T which work the same way as PEEK_BITS
* and READ_BITS, except they use this lookup table. This is useful if
* you need to look up a number of bits that are only known at
* runtime, so the bit mask can't be turned into a constant by the
* compiler.
* The bit buffer datatype should be at least 32 bits wide: it must be
* possible to ENSURE_BITS(17), so it must be possible to add 16 new bits
* to the bit buffer when the bit buffer already has 1 to 15 bits left.
*/
#ifndef BITS_VAR
# error "define BITS_VAR as the state structure poiner variable name"
#endif
#ifndef BITS_TYPE
# error "define BITS_TYPE as the state structure type"
#endif
#if defined(BITS_ORDER_MSB) && defined(BITS_ORDER_LSB)
# error "you must define either BITS_ORDER_MSB or BITS_ORDER_LSB"
#else
# if !(defined(BITS_ORDER_MSB) || defined(BITS_ORDER_LSB))
# error "you must define BITS_ORDER_MSB or BITS_ORDER_LSB"
# endif
#endif
#if HAVE_LIMITS_H
# include <limits.h>
#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

View file

@ -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<<nbits) + (nsyms*2)) in length.
*
* Returns 0 for OK or 1 for error
*/
static int make_decode_table(unsigned int nsyms, unsigned int nbits,
unsigned char *length, unsigned short *table)
{
register unsigned short sym, next_symbol;
register unsigned int leaf, fill;
#ifdef BITS_ORDER_LSB
register unsigned int reverse;
#endif
register unsigned char bit_num;
unsigned int pos = 0; /* the current position in the decode table */
unsigned int table_mask = 1 << nbits;
unsigned int bit_mask = table_mask >> 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

View file

@ -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

View file

@ -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 <config.h>
#endif
#include <system.h>
#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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
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

View file

@ -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 <config.h>
#endif
#include <mspack.h>
/* 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 <stdio.h>
/* 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__ "<unknown>"
# 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 <limits.h>
#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 <strings.h>
#endif
#if HAVE_STRING_H
# include <string.h>
#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

View file

@ -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 <lzss.h>
/* 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

View file

@ -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 <system.h>
#include <szdd.h>
struct msszdd_compressor *
mspack_create_szdd_compressor(struct mspack_system *sys)
{
/* todo */
return NULL;
}
void mspack_destroy_szdd_compressor(struct msszdd_compressor *self) {
/* todo */
}

View file

@ -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 <system.h>
#include <szdd.h>
/* 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;
}