Release Maconv v1.0

This commit is contained in:
Guillaume Gonnet 2019-01-03 12:05:24 +01:00
commit ea24dbaeff
98 changed files with 22695 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build/

86
CMakeLists.txt Normal file
View File

@ -0,0 +1,86 @@
#
# Build Maconv.
#
# Copyright (C) 2019, Guillaume Gonnet
# License GPL3
cmake_minimum_required(VERSION 3.2)
project(maconv)
# Compile vendor libraries.
add_subdirectory("vendors/libhfs")
# Source files.
set(MACONV_SRC
"src/fs/file.h"
"src/fs/file.cc"
"src/fs/file_reader.h"
"src/fs/file_reader.cc"
"src/fs/file_writer.h"
"src/fs/file_writer.cc"
"src/conv/converters.h"
# "src/conv/appledouble.cc"
"src/conv/applesingle.cc"
"src/conv/macbinary.cc"
"src/conv/binhex.cc"
"src/conv/rsrc.cc"
"src/disk/disk.h"
"src/disk/extract.cc"
"src/disk/pack.cc"
"src/stuffit/stuffit.h"
"src/stuffit/stuffit.cc"
"src/stuffit/stuffit_v1.cc"
"src/stuffit/stuffit_v5.cc"
"src/stuffit/utils/bwt.h"
"src/stuffit/utils/bwt.cc"
"src/stuffit/utils/crc.h"
"src/stuffit/utils/crc.cc"
"src/stuffit/utils/huffman.h"
"src/stuffit/utils/huffman.cc"
"src/stuffit/methods.h"
"src/stuffit/methods.cc"
"src/stuffit/methods/rle90.h"
"src/stuffit/methods/rle90.cc"
"src/stuffit/methods/compress.h"
"src/stuffit/methods/compress.cc"
"src/stuffit/methods/algorithm13.h"
"src/stuffit/methods/algorithm13.cc"
"src/stuffit/methods/arsenic.h"
"src/stuffit/methods/arsenic.cc"
"src/formats/file_signature.h"
"src/formats/file_signature.cc"
"src/formats/formats.h"
"src/formats/formats.cc"
"src/formats/unpack.cc"
"src/formats/pack.cc"
"src/utils/buffer_stream.h"
"src/utils/buffer_stream.cc"
"src/utils/bit_reader.h"
"src/utils/bit_reader.cc"
"src/commands.h"
"src/commands.cc"
"src/main.cc"
)
# Include "src" and "vendors" folder for resolving #include.
include_directories("src" "vendors")
# Create the executable.
add_executable(maconv ${MACONV_SRC})
# Link "libhfs" library.
target_link_libraries(maconv hfs)
# Install rules for Maconv.
install(TARGETS maconv RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
install(FILES "src/maconv.1" DESTINATION "${CMAKE_INSTALL_PREFIX}/man/man1")

621
LICENSE Normal file
View File

@ -0,0 +1,621 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, 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
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If 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 convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU 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
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS

42
README.md Normal file
View File

@ -0,0 +1,42 @@
# Maconv
**Maconv** is a Linux software that can convert all kinds of old Macintosh formats,
including MacBinary, Stuffit archives and HFS disk files.
## Usage
Maconv has three sub-commands:
- `maconv c [options] input-file [output-file]`
- `maconv e [options] input-file [output-folder]`
- `maconv d [options] input-folder [output-file]`
The `c` sub-commamd converts a file from a format to another. The `e`
sub-commamd extracts a Stuffit archive (versions 1 and 5) or a HFS disk image.
The `d` sub-commamd creates an HFS disk image from a folder (like a file
archiver).
You can get more information about these commands with `maconv -h` or `man
maconv`.
## Supported formats
You can get more information on these format in `docs` folder.
Maconv supports the following container formats: [MacBinary][macbin],
[AppleSingle][as].
Also, Maconv can extract old [Stuffit archives][stuffit] (encoded with
compression method 0, 1, 2, 13 or 15).
## License
This project is under the GPLv3 license.
See `LICENSE` file at the root of this project for more information.
[macbin]: docs/formats/MacBinary.md
[as]: docs/formats/AppleSingle.md
[stuffit]: docs/stuffit/Stuffit.md

20
docs/README.md Normal file
View File

@ -0,0 +1,20 @@
# Maconv documentations
Welcome to the documentation associated with **Maconv**.
## Information about supported formats
- [MacBinary](formats/MacBinary.md)
- [BinHex](formats/BinHex.md)
- [AppleSingle](formats/AppleSingle.md)
- [AppleDouble](formats/AppleDouble.md)
## Stuffit specifications
You can find Stuffit specifications [on this page](stuffit/Stuffit.md).
In particular, the documentation presents the following versions of Stuffit:
- [Stuffit (v1)](stuffit/Stuffit_v1.md)
- [Stuffit (v5)](stuffit/Stuffit_v5.md)

0
docs/file-signature.md Normal file
View File

View File

@ -0,0 +1,13 @@
# AppleSingle format
TODO.
All integers are big-endians.
---------------------------
**Sources**
https://en.wikipedia.org/wiki/AppleSingle_and_AppleDouble_formats
http://kaiser-edv.de/documents/AppleSingle_AppleDouble.pdf

View File

@ -0,0 +1,60 @@
# AppleSingle format
AppleSingle is a file format developed to store Mac OS "dual-forked" files on
the Unix filesystem. AppleSingle is similar in concept to the more popular
MacBinary format, in that the resource and data forks are combined together with
a header containing the Finder information. In fact, the format is so similar,
it seemed there were no reason why Apple did not simply use MacBinary instead,
which by that point, was widely known and used.
All integers are big-endians.
Data are arranged as follows:
> `data = <file header> + <entry 1> + ... + <entry N> + <data 1> + ... + <data N>`
## File header
| **Offset** | **Length** | **Contents** |
|:-----------|:-----------|:-------------|
| 00 | Word | Magic number (always `0x00051600`) |
| 04 | Word | Version number (always `0x00020000`) |
| 08 | 16 Bytes | Always zero |
| 24 | Half | Number of entries |
## Entry
| **Offset** | **Length** | **Contents** |
|:-----------|:-----------|:-------------|
| 00 | Word | Entry ID (see below) |
| 04 | Word | Data offset |
| 08 | Word | Data length (can be zero) |
Available entry IDs are described in the following table.
| **Entry name** | **ID** | **Description** |
|:--------------------|:-------|:----------------|
| Data Fork | 1 | Magic number (always `0x00051600`) |
| Resource Fork | 2 | Version number (always `0x00020000`) |
| Real Name | 3 | Files name as created on home file system |
| Comment | 4 | Standard Macintosh comment |
| Icon, B&W | 5 | Standard Macintosh black and white icon |
| Icon, Color | 6 | Macintosh color icon |
| File Dates Info | 8 | File creation date, modification date, and so on |
| Finder Info | 9 | Standard Macintosh Finder information |
| Macintosh File Info | 10 | Macintosh file information, attributes, and so on |
| ProDOS File Info | 11 | ProDOS file information, attributes, and so on |
| MS-DOS File Info | 12 | MS-DOS file information, attributes, and so on |
| Short Name | 13 | AFP short name |
| AFP File Info | 14 | AFP file information, attributes, and so on |
| Directory ID | 15 | AFP directory ID |
---------------------------
**Sources**
https://en.wikipedia.org/wiki/AppleSingle_and_AppleDouble_formats
http://kaiser-edv.de/documents/AppleSingle_AppleDouble.pdf

9
docs/formats/BinHex.md Normal file
View File

@ -0,0 +1,9 @@
# BinHex format
---------------------------
**Sources**
https://files.stairways.com/other/binhex-40-specs-info.txt

76
docs/formats/MacBinary.md Normal file
View File

@ -0,0 +1,76 @@
# MacBinary format
MacBinary is a file format that combines the two forks of a classic Mac OS file
into a single file, along with HFS's extended metadata. It was used to share Mac
OS files over FTP, Web or e-mail because it could be stored on computers that
run operating systems with no HFS support, such as Unix or Windows.
This file describes a summary of MacBinnary I, II and III.
All integers are big-endians.
Data are arranged as follows:
> `data = <header 128B> + <data fork> + <padding 1> + <res fork> + <padding 2>`
## Header `<header 128B>`
| **Offset** | **Length** | **Contents** |
|:-----------|:-----------|:-------------|
| 000 | Byte | Always zero |
| 001 | Byte | Length of filename (in the range 1-31) |
| 002 | 63 Bytes | Filename (remaning bytes are zero) |
| 065 | Word | File type (4 characters) |
| 069 | Word | File creator (4 characters) |
| 073 | Byte | Finder flags: Bit 7 - isAlias. Bit 6 - isInvisible. Bit 5 - hasBundle. Bit 4 - nameLocked. Bit 3 - isStationery. Bit 2 - hasCustomIcon. Bit 1 - reserved. Bit 0 - hasBeenInited. |
| 074 | Byte | Always zero |
| 075 | Half | File's vertical position within its window |
| 077 | Half | File's horizontal position within its window |
| 079 | Half | File's window or folder ID. |
| 081 | Byte | "Protected" flag (in low order bit) |
| 082 | Byte | Always zero |
| 083 | Word | Data Fork length in B (zero if no Data Fork) |
| 087 | Word | Resource Fork length in B (zero if no R.F.) |
| 091 | Word | File's creation date |
| 095 | Word | File's "last modified" date |
| 099 | Half | Length of Get Info comment to be sent after the resource fork (if implemented, see below). |
| 101 **²** | Byte | Finder Flags, bits 0-7. (Bits 8-15 are already in byte 73) Bit 7 - hasNoInits Bit 6 - isShared Bit 5 - requiresSwitchLaunch Bit 4 - ColorReserved Bits 1-3 - color Bit 0 - isOnDesk |
| 102 **³** | Word | Signature for indentification purposes (always `mBIN`) |
| 106 **³** | Byte | Script of file name (from the fdScript field of an fxInfo record) |
| 107 **³** | Byte | Extended Finder flags (from the fdXFlags field of an fxInfo record) |
| 108-115 | | Unused (must be zeroed by creators, must be ignored by readers) |
| 116 **²** | Word | Length of total files when packed files are unpacked. As of the writing of this document, this field has never been used. |
| 120 **²** | Half | Length of a secondary header. If this is non-zero, skip this many bytes (rounded up to the next multiple of 128). This is for future expansion only, when sending files with MacBinary, this word should be zero. |
| 122 **²** | Byte | Version number of MacBinary (`129` for MacBinary II, `130` for MacBinary III) |
| 123 **²** | Byte | Minimum MacBinary version needed to read this file (set this value at 129 for backwards compatibility with MacBinary II) |
| 124 **²** | Half | CRC-16 of previous 124 bytes |
| 126 | Half | Padding (always zero) |
**²** These fields have been added in MacBinary II.
**³** These fields have been added in MacBinary III.
Cyclic redundancy check (CRC) used in header is CRC-16-CCITT, i.e. uses
polynomial number `0x1021` and starts with `0`.
## Data and ressource forks.
Data fork directly follow the header (at byte 128). The length of these data
(which can be zero) must correspond with the length given in the header (at byte
83).
Data are completed with some padding bytes (normally `0x00` but some
implementations use `0x7F`) until the total length of file is a multiple of 128.
If the total length is already a multiple of 128 after adding the data fork, no
padding is added.
The ressource fork follows the same rules (i.e. padding bytes are added after
the fork to keep the total length a multiple of 128).
---------------------------
**Sources**
https://en.wikipedia.org/wiki/MacBinary
https://github.com/mietek/theunarchiver/wiki/MacBinarySpecs
https://files.stairways.com/other/macbinaryii-standard-info.txt

37
docs/libHFS.md Normal file
View File

@ -0,0 +1,37 @@
# Documentation of libHFS
You can find the original API of this library in file `vendors/libhfs/libhfs.txt`.
## File paths
File/directory names are up to 31 characters long, volume names up to 27
characters long. In both cases, they must not contain `:` (directory separator).
An abolute path must begin with the name of the volume containing the desired
file (as if it was a folder).
Relative paths start with the `:` character (similar to `./` in Unix paths). A
path that doesn't contain `:` character is also considered as a relative path.
The directory sperator is the `:` character. When alone, this character refers
to the current directory (as `.` on Unix). When there is a sequence of **N** `:`
(which follow each other), the path refers to the **N-1**-th parent (as `../`
**N-1** times on Unix).
## Directories
On a volume, each directory has a unqiue ID.
ID `0` is reserved for a meta-directory containing all mounted volumes.
ID `2` is the root directory (of each volume).
Note: the root directory can't be deleted.
## Dates and times
Dates and times are in classic Mac OS format. Classic Mac OS times are 32 bits
unsigned integers that count the number of seconds since January 1, 1904 00:00
UTC.

View File

@ -0,0 +1,72 @@
GCC RUNTIME LIBRARY EXCEPTION
Version 3.1, 31 March 2009
Copyright (C) 2009 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
This GCC Runtime Library Exception ("Exception") is an additional
permission under section 7 of the GNU General Public License, version
3 ("GPLv3"). It applies to a given file (the "Runtime Library") that
bears a notice placed by the copyright holder of the file stating that
the file is governed by GPLv3 along with this Exception.
When you use GCC to compile a program, GCC may combine portions of
certain GCC header files and runtime libraries with the compiled
program. The purpose of this Exception is to allow compilation of
non-GPL (including proprietary) programs to use, in this way, the
header files and runtime libraries covered by this Exception.
0. Definitions.
A file is an "Independent Module" if it either requires the Runtime
Library for execution after a Compilation Process, or makes use of an
interface provided by the Runtime Library, but is not otherwise based
on the Runtime Library.
"GCC" means a version of the GNU Compiler Collection, with or without
modifications, governed by version 3 (or a specified later version) of
the GNU General Public License (GPL) with the option of using any
subsequent versions published by the FSF.
"GPL-compatible Software" is software whose conditions of propagation,
modification and use would permit combination with GCC in accord with
the license of GCC.
"Target Code" refers to output from any compiler for a real or virtual
target processor architecture, in executable form or suitable for
input to an assembler, loader, linker and/or execution
phase. Notwithstanding that, Target Code does not include data in any
format that is used as a compiler intermediate representation, or used
for producing a compiler intermediate representation.
The "Compilation Process" transforms code entirely represented in
non-intermediate languages designed for human-written code, and/or in
Java Virtual Machine byte code, into Target Code. Thus, for example,
use of source code generators and preprocessors need not be considered
part of the Compilation Process, since the Compilation Process can be
understood as starting with the output of the generators or
preprocessors.
A Compilation Process is "Eligible" if it is done using GCC, alone or
with other GPL-compatible software, or if it is done without using any
work based on GCC. For example, using non-GPL-compatible Software to
optimize any GCC intermediate representations would not qualify as an
Eligible Compilation Process.
1. Grant of Additional Permission.
You have permission to propagate a work of Target Code formed by
combining the Runtime Library with Independent Modules, even if such
propagation would otherwise violate the terms of GPLv3, provided that
all Target Code was generated by Eligible Compilation Processes. You
may then convey such a combination under terms of your choice,
consistent with the licensing of the Independent Modules.
2. No Weakening of GCC Copyleft.
The availability of this Exception does not imply any general
presumption that third-party software is unaffected by the copyleft
requirements of the license of GCC.

674
docs/licenses/Retro68.txt Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, 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
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If 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 convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU 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
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state 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 program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@ -0,0 +1,528 @@
This program, "The Unarchiver", its accompanying libraries, "XADMaster"
and "UniversalDetector", and the various smaller utility programs, such
as "unar" and "lsar", are distributed under 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.
"UniversalDetector" is also available under other licenses, such as the
Mozilla Public License. Please refer to the files in its subdirectory
for further information.
The GNU Lesser General Public License might be too restrictive for some
users of this code. Parts of the code are derived from earlier
LGPL-licensed code and will as such always be bound by the LGPL, but
some parts of the code are developed from scratch by the author of The
Unarchiver, Dag Ågren, and can thus be made available under a more
permissive license. For simplicity, everything is currently licensed
under the LGPL, but if you are interested in using any code from this
project under another license, please contact the author for further
information.
- Dag Ågren, <paracelsus@gmail.com>
-----------------------------------------------------------------------
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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!

3
docs/stuffit/Stuffit.md Normal file
View File

@ -0,0 +1,3 @@
# Stuffit specifications
TODO.

View File

@ -0,0 +1,50 @@
# Stuffit format
All intergers are big-endians.
## Archive header
| **Offset** | **Length** | **Contents** |
|:-----------|:-----------|:-------------|
| 00 | Word | Magic number 1 (see below) |
| 04 | Half | Number of entries in root directory |
| 06 | Word | Total size of archive |
| 10 | Word | Magic number 2 (always `0x724c6175`) |
| 14 | Byte | Version |
| 15 | Byte | Unknown |
| 16 | Word | Header size (if version not `1`) |
| 20 | Half | CRC-16 of header |
Magic number 1 must be one of the following values: `SIT!`, `ST46`, `ST50`,
`ST60`, `ST65`, `STin`, `STi2`, `STi3`, `STi4`.
## File / folder header
| **Offset** | **Length** | **Contents** |
|:-----------|:-----------|:-------------|
| 000 | Byte | Resource fork compression method |
| 001 | Byte | Data fork compression method |
| 002 | Byte | File name length (in range 1-31) |
| 003 | 63 bytes | File name (remaning bytes are zero) |
| 066 | Word | Mac OS file type |
| 070 | Word | Mac OS file creator |
| 074 | Half | Mac OS Finder flags |
| 076 | Word | Creation date (Mac OS format) |
| 080 | Word | Modification date (Mac OS format) |
| 084 | Word | Resource fork uncompressed length |
| 088 | Word | Data fork uncompressed length |
| 092 | Word | Resource fork compressed length |
| 096 | Word | Data fork compressed length |
| 100 | Half | Resource fork CRC-16 |
| 102 | Half | Data fork CRC-16 |
| 104 | 6 bytes | Unknown |
| 110 | Half | Header CRC-16 |
---------------------------
**Sources**
https://github.com/mietek/theunarchiver/wiki/StuffItFormat

View File

@ -0,0 +1,86 @@
# Stuffit 5 format
All intergers are big-endians.
## Archive header
| **Offset** | **Length** | **Contents** |
|:-----------|:-----------|:-------------|
| 000 | 80 bytes | Magic string (always `StuffIt (c)1997-???? Aladdin Systems, Inc., http://www.aladdinsys.com/StuffIt/` followed by `0x0D` `0x0A`, where characters marked `?` can vary) |
| 080 | Half | Unknown |
| 082 | Byte | Version (always `5`) |
| 083 | Byte | Flags (`0x80` = encrypted) |
| 084 | Word | Total size of archive |
| 088 | Word | Unknown |
| 092 | Half | Number of entries in root directory |
| 094 | Word | Offset of first entry in root directory |
| 098 | Half | Header CRC-16? |
| 100 | ? | Unknown data until first entry |
## Entry header
### Base header for file and folder
Both file and folder headers start with the following header.
| **Offset** | **Length** | **Contents** |
|:-----------|:-----------|:-------------|
| 00 | Word | Magic number (always `0xA5A5A5A5`) |
| 04 | Byte | Version |
| 05 | Byte | Unknown (but certainly `0x00`) |
| 06 | Half | Header size |
| 08 | Byte | Unknown |
| 09 | Byte | Entry flags/type |
| 10 | Word | Creation date (Mac OS format) |
| 14 | Word | Modification date (Mac OS format) |
| 18 | Word | Offset of previous entry |
| 22 | Word | Offset of next entry |
| 26 | Word | Offset of directory entry |
| 30 | Half | Name size (called **N** after) |
| 32 | Half | Header CRC-16 |
### File specific header
A file (when `flags` doesn't contain `0x40`) continues with the following header.
| **Offset** | **Length** | **Contents** |
|:-----------|:-----------|:-------------|
| 34 | Word | Data fork uncompressed length |
| 38 | Word | Data fork compressed length |
| 42 | Half | Data fork CRC-16 (Set to zero for method 15) |
| 44 | Half | Unknown |
| 46 | Byte | Data fork compression method (only `0`, `13` or `15`) |
| 47 | Byte | Password data lenght (called **M** after) |
| 48 | **M** | Password information |
| 48+**M** | **N** | Filename |
| 48+**M**+**N** | **K** | Comment size (called **K** after) |
| 48 | **N** | Filename |
| 48 | **N** | Filename |
### Folder specific header
A folder (when `flags` contains `0x40`) continues with the following header.
| **Offset** | **Length** | **Contents** |
|:-----------|:-----------|:-------------|
| 34 | Word | Offset of first entry in folder |
| 38 | Word | Size of complete directory |
| 42 | Word | Unknown |
| 46 | Half | Number of files in folder |
| 48 | **N** | Folder name |
| 48+**N** | ? | Unknown data until next entry |
Note: if the offset of first entry is `0xFFFFFFFF`, this folder must be skiped
after reading 48 bytes of header (the usefulness of these folders is unclear).
---------------------------
**Sources**
https://github.com/mietek/theunarchiver/wiki/StuffIt5Format
https://github.com/mietek/theunarchiver/blob/master/XADMaster/XADStuffIt5Parser.m#L29

115
src/commands.cc Normal file
View File

@ -0,0 +1,115 @@
/*
Run the selected command from the CLI.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "commands.h"
#include "formats/formats.h"
#include "disk/disk.h"
#include <path.hpp>
namespace maconv {
// Run convert "c" command.
void RunConvertCommand(std::string &input, std::string &output, std::string
&format)
{
// Read input path given in argument.
if (!Path(input).is_file())
StopOnError("input file doesn't exist");
// Unpack the input file.
auto u = UnPackLocalFile(input);
// If format is not present: guess output format.
bool no_out = output.empty() || Path(output).is_directory();
if (format.empty())
format = no_out ? "rsrc" : GuessFormatByExtension(output);
// Get the converter for the selected format.
auto conv = GetConverter(format);
if (conv.type == ConvData::NotFound)
StopOnError("format '%s' doesn't exist", format.c_str());
// Normalise the output directory.
if (no_out && !output.empty())
output = Path(output).directory().string();
else
output = Path(input).parent().string();
// If output is not present: use input name if single, real name if double.
bool is_double = (conv.type == ConvData::Double);
if (no_out)
output += is_double ? u.file.filename : GetFilenameFor(input, conv);
// Now save the output file(s).
PackLocalFile(u.file, output, conv);
}
// Run extract "e" command.
void RunExtractCommand(std::string &input, std::string &output, std::string
&res_format)
{
// Read input path given in argument.
if (!Path(input).is_file())
StopOnError("input file doesn't exist");
// Read ressource format.
prefered_conv = GetConverter(res_format);
if (prefered_conv.type == ConvData::NotFound)
StopOnError("format '%s' doesn't exist", res_format.c_str());
// Unpack and extract the input file.
auto u = UnPackLocalFile(input);
if (!ExtractArchiveOrDisk(u, output))
StopOnError("can't extract input file (unsupported format)");
}
// Run disk creation "d" command.
void RunDiskCommand(std::string &folder, std::string &output, std::string
&name)
{
// Read input path given in argument.
auto abs_in = Path(folder).absolute();
if (!abs_in.is_directory())
StopOnError("input folder doesn't exist");
// If no output name: use directory name.
if (output.empty())
output = abs_in.trim().string() + ".img";
// If no volume name: use directory name.
if (name.empty())
name = abs_in.trim().filename();
disk::PackDiskImage(abs_in.string(), output, name);
}
} // namespace maconv

52
src/commands.h Normal file
View File

@ -0,0 +1,52 @@
/*
Run the selected command from the CLI.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
namespace maconv {
// Is the verbose mode enabled?
extern bool verbose;
// Log debug message (if verbose mode is enabled).
extern "C" void LogDebug(const char *fmt, ...);
// Stop the program on an error.
extern "C" void StopOnError(const char *fmt, ...);
// Run convert "c" command.
void RunConvertCommand(std::string &input, std::string &output,
std::string &format);
// Run extract "e" command.
void RunExtractCommand(std::string &input, std::string &output,
std::string &res_format);
// Run disk creation "d" command.
void RunDiskCommand(std::string &folder, std::string &output,
std::string &name);
} // namespace maconv

120
src/conv/appledouble.cc Normal file
View File

@ -0,0 +1,120 @@
/*
Convert files from and to Apple Double format.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "conv/converters.h"
namespace maconv {
namespace conv {
// Get the name of the other file.
static bool GetOtherName(const std::string &input, std::string &other)
{
bool is_res = input.size() >= 4 && input.compare(input.size() - 4, 4, ".rsrc") == 0;
if (is_res)
other = input.substr(0, input.size() - 4);
else
other = input + ".rsrc";
return is_res;
}
static bool IsFileRessource(const std::string &input)
{
auto filename = Path(input).filename();
return (filename.substr(0, 2) == "._") || (filename.substr(0, 1) == "%") ||
(input.size() >= 4 && input.compare(input.size() - 4, 4, ".rsrc") == 0);
}
static bool GetRessourceFile(const std::string &input)
{
auto filename = Path(input).filename();
auto folder = input.substr(0, input.size() - filename.size());
// The file given is a ressource file.
bool is_res =
if (is_res) {
}
// Serach for a ressource file.
std::string other;
if (Path(folder + "._" + filename).is_file())
other = folder + "._" + filename;
else if (Path(folder + "%" + filename).is_file())
other = folder + "%" + filename;
else if (Path(input + ".rsrc").is_file())
other = input + ".rsrc";
else
return false;
}
// Read and decode a Apple Double file.
bool ReadAppleDouble(fs::FileReader &reader, UnPacked &u)
{
// If the file given it's a not a ressource file: read the res.
if (!IsFileRessource(reader.filename)) {
IS_COND(GetRessourceFile(u.n2));
int size = ReadLocalFile(u.n2, u.d2);
fs::FileReader res_reader {u.d2.get(), size, u.n2};
IS_COND(IsAppleDouble(res_reader));
ReadRessourceInfo(res_reader, u.file);
ReadDataFork(reader, u.file);
}
// The file given it's a ressource file.
else {
IS_COND(IsAppleDouble(reader));
ReadRessourceInfo(reader, u.file);
if (!GetDataFile(u.n2)) return true;
int size = ReadLocalFile(u.n2, u.d2);
fs::FileReader data_reader {u.d2.get(), size, u.n2};
ReadDataFork(data_reader, u.file);
}
return true;
}
// Write a Apple Double file.
void WriteAppleDouble(fs::File &file, const std::string &name)
{
// TODO.
}
} // namespace maconv
} // namespace conv

177
src/conv/applesingle.cc Normal file
View File

@ -0,0 +1,177 @@
/*
Convert files from and to AppleSingle format.
See docs/formats/AppleSingle.md for more information on this format.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "conv/converters.h"
#include <path.hpp>
namespace maconv {
namespace conv {
// Return true if a file is in AppleSingle format.
bool IsFileAppleSingle(fs::FileReader &reader)
{
IS_COND(reader.file_size >= 26);
IS_COND(reader.ReadWordBE() == 0x00051600);
IS_COND(reader.ReadWordBE() == 0x00020000);
return true;
}
// Extract a single AppleSingle entry.
void ExtractAppleSimpleEntry(fs::FileReader &reader, fs::File &file, uint32_t id,
uint32_t length)
{
switch (id) {
// Data fork.
case 1: {
file.data = reader.data + reader.Tell();
file.data_size = length;
} break;
// Ressource fork.
case 2: {
file.res = reader.data + reader.Tell();
file.res_size = length;
} break;
// Ressource fork.
case 3: {
file.filename = reader.ReadString(length);
} break;
// File creation and modification dates.
case 8: {
file.creation_date = reader.ReadJ2000Date();
file.modif_date = reader.ReadJ2000Date();
} break;
// Finder information.
case 9: {
file.type = reader.ReadWordBE();
file.creator = reader.ReadWordBE();
file.flags = reader.ReadHalfBE();
} break;
}
}
// Read and decode an AppleSingle file.
void ReadAppleSingle(fs::FileReader &reader, fs::File &file)
{
reader.Skip(26);
int num_entries = reader.ReadHalfBE();
// Extract each entries.
for (int i = 0; i < num_entries; i++) {
reader.Seek(26 + i * 12);
uint32_t id = reader.ReadWordBE();
uint32_t offset = reader.ReadWordBE();
uint32_t length = reader.ReadWordBE();
reader.Seek(offset);
ExtractAppleSimpleEntry(reader, file, id, length);
}
// If the filename is not provided: use the input filename.
if (file.filename.empty())
file.filename = Path(file.filename).filename();
// Add missing information.
GetLocalInfo(file.filename, file);
}
// Write an AppleSingle file.
void WriteAppleSingle(fs::File &file, fs::FileWriter &writer)
{
// Write file header.
writer.WriteWordBE(0x00051600);
writer.WriteWordBE(0x00020000);
writer.Fill(0x0, 16);
writer.WriteHalfBE(5); // Number of entries.
// Write filename entry.
uint32_t offset = 24 + 5 * 12;
writer.WriteWordBE(3); // ID.
writer.WriteWordBE(offset); // Offset.
writer.WriteWordBE(file.filename.size()); // Length.
// Write date entry.
offset += file.filename.size();
writer.WriteWordBE(3); // ID.
writer.WriteWordBE(offset); // Offset.
writer.WriteWordBE(12); // Length.
// Write Finder information entry.
offset += 12;
writer.WriteWordBE(9); // ID.
writer.WriteWordBE(offset); // Offset.
writer.WriteWordBE(32); // Length.
// Write data fork entry.
offset += 32;
writer.WriteWordBE(1); // ID.
writer.WriteWordBE(offset); // Offset.
writer.WriteWordBE(file.data_size); // Length.
// Write ressource fork entry.
offset += file.data_size;
writer.WriteWordBE(2); // ID.
writer.WriteWordBE(offset); // Offset.
writer.WriteWordBE(file.res_size); // Length.
// Write filename.
writer.WriteString(file.filename);
// Write creation and modification dates.
writer.WriteJ2000Date(file.creation_date);
writer.WriteJ2000Date(file.modif_date);
writer.WriteWordBE(0x80000000);
writer.WriteWordBE(file.modif_date);
// Write Finder information.
writer.WriteWordBE(file.type);
writer.WriteWordBE(file.creator);
writer.WriteHalfBE(file.flags);
writer.Fill(0x0, 22);
// Write data fork.
writer.Write(file.data, file.data_size);
// Wrire ressource fork.
writer.Write(file.res, file.res_size);
}
} // namespace maconv
} // namespace conv

56
src/conv/binhex.cc Normal file
View File

@ -0,0 +1,56 @@
/*
Convert files from and to BinHex format.
See docs/formats/BinHex.md for more information on this format.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "conv/converters.h"
namespace maconv {
namespace conv {
// Return true if a file is in BinHex format.
bool IsFileBinHex(fs::FileReader &reader)
{
// TODO.
return false;
}
// Read and decode a BinHex file.
void ReadBinHex(fs::FileReader &reader, fs::File &file)
{
// TODO.
}
// Write a BinHex file.
void WriteBinHex(fs::File &file, fs::FileWriter &writer)
{
// TODO.
}
} // namespace maconv
} // namespace conv

64
src/conv/converters.h Normal file
View File

@ -0,0 +1,64 @@
/*
All available converters.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs/file.h"
#include "fs/file_reader.h"
#include "fs/file_writer.h"
#include "formats/formats.h"
namespace maconv {
namespace conv {
// MacBinary format.
bool IsFileMacBinary(fs::FileReader &reader);
void ReadMacBinary(fs::FileReader &reader, fs::File &file);
void WriteMacBinary(fs::File &file, fs::FileWriter &writer);
// BinHex format.
bool IsFileBinHex(fs::FileReader &reader);
void ReadBinHex(fs::FileReader &reader, fs::File &file);
void WriteBinHex(fs::File &file, fs::FileWriter &writer);
// Apple Single format.
bool IsFileAppleSingle(fs::FileReader &reader);
void ReadAppleSingle(fs::FileReader &reader, fs::File &file);
void WriteAppleSingle(fs::File &file, fs::FileWriter &writer);
// Apple Double format.
bool ReadAppleDouble(fs::FileReader &reader, UnPacked &u);
void WriteAppleDouble(fs::File &file, const std::string &name);
// RSRC format (alias no format).
bool ReadRsrc(fs::FileReader &reader, UnPacked &u);
void WriteRsrc(fs::File &file, const std::string &name);
void WriteOnlyData(fs::File &file, const std::string &name);
// Extract a single AppleSingle entry.
void ExtractAppleSimpleEntry(fs::FileReader &reader, fs::File &file, uint32_t id,
uint32_t length);
} // namespace maconv
} // namespace conv

207
src/conv/macbinary.cc Normal file
View File

@ -0,0 +1,207 @@
/*
Convert files from and to MacBinary format.
See docs/formats/MacBinary.md for more information on this format.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "conv/converters.h"
#include "utils/buffer_stream.h"
#include "commands.h"
namespace maconv {
namespace conv {
// Precalculed CRC 16 table.
static uint16_t kCrc16Table[256] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
};
// Calculate CRC (Cyclic Redundancy Check) for some data.
static uint16_t CalculateCRC(uint8_t *data, uint32_t size)
{
uint16_t crc = 0;
for (uint8_t *end = data + size; data != end; data++)
crc = (crc << 8) ^ kCrc16Table[(*data ^ (crc >> 8)) & 0x00FF];
return crc;
}
// Return true if a file is in MacBinary format.
bool IsFileMacBinary(fs::FileReader &reader)
{
IS_COND(reader.file_size >= 128);
IS_COND(reader.ReadByte() == 0x0);
auto byte = reader.ReadByte();
IS_COND(byte > 0 && byte < 64);
IS_COND(reader.ReadWordBE() > 0x1F000000);
reader.Skip(68); // Byte #74
IS_COND(reader.ReadByte() == 0x0);
reader.Skip(7); // Byte #82
IS_COND(reader.ReadByte() == 0x0);
reader.Skip(39); // Byte #122
auto half = reader.ReadHalfBE();
IS_COND(half == 0 || half == 0x8181 || half == 0x8281);
return true;
}
// Read and decode a MacBinary file.
void ReadMacBinary(fs::FileReader &reader, fs::File &file)
{
reader.Skip(1);
// Read name of the file.
uint8_t filelength = reader.ReadByte();
file.filename = reader.ReadString(filelength);
reader.Skip(63 - filelength);
// Read file type, creator and Finder flags.
file.type = reader.ReadWordBE();
file.creator = reader.ReadWordBE();
file.flags = reader.ReadByte() << 8;
// Read window information.
reader.Skip(1);
reader.Skip(4); // TODO: read file positions within its window.
reader.Skip(4); // TODO: read folder ID and protected flags.
// Read size of forks.
file.data_size = reader.ReadWordBE();
file.res_size = reader.ReadWordBE();
// Read creation and modification dates.
file.creation_date = reader.ReadMacDate();
file.modif_date = reader.ReadMacDate();
// Read second part of Finder flags.
reader.Skip(2);
file.flags |= reader.ReadByte();
reader.Skip(22);
// Assume that CRC is correct.
// Set data and resource forks.
file.data = reader.data + 128;
file.res = reader.data + 128 + ((file.data_size + 127) & -128);
}
// Write a MacBinary file.
void WriteMacBinary(fs::File &file, fs::FileWriter &base)
{
utils::BufferStreamBuf header_buf {124};
std::ostream header_stream {&header_buf};
fs::FileWriter writer {header_stream};
// Write filename.
writer.WriteByte(0x0);
writer.WriteByte(file.filename.size());
writer.WriteString(file.filename);
writer.Fill(0x0, 63 - file.filename.size());
// Write file type, creator and Finder flags.
writer.WriteWordBE(file.type);
writer.WriteWordBE(file.creator);
writer.WriteByte(file.flags >> 8);
// Write window information.
writer.Fill(0x0, 1);
writer.Fill(0x0, 4); // TODO: write file positions within its window.
writer.Fill(0x0, 4); // TODO: write folder ID and protected flags.
// Write size of ressource and data forks.
writer.WriteWordBE(file.data_size);
writer.WriteWordBE(file.res_size);
// Write creation and modification dates.
writer.WriteMacDate(file.creation_date);
writer.WriteMacDate(file.modif_date);
// Write second part of Finder flags.
writer.Fill(0x0, 2);
writer.WriteByte(file.flags & 0xFF);
// Write MacBinray III magic strings/numbers.
writer.WriteString("mBIN");
writer.Fill(0x0, 16);
writer.WriteByte(130);
writer.WriteByte(129);
// Calculate CRC and write the header.
uint8_t *header_data = header_buf.buffer.get();
base.Write(header_data, 124);
base.WriteHalfBE(CalculateCRC(header_data, 124));
// Write data fork.
base.Fill(0x0, 2);
base.Write(file.data, file.data_size);
// Write ressource fork.
base.Fill(0x7F, ((file.data_size + 127) & -128) - file.data_size);
base.Write(file.res, file.res_size);
// Write remaning padding.
base.Fill(0x7F, ((file.res_size + 127) & -128) - file.res_size);
}
} // namespace maconv
} // namespace conv

131
src/conv/rsrc.cc Normal file
View File

@ -0,0 +1,131 @@
/*
Convert files from and to rsrc (raw) format.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "conv/converters.h"
#include <path.hpp>
namespace maconv {
namespace conv {
// Get the name of the other file.
static bool GetOtherName(const std::string &input, std::string &other)
{
bool is_res = input.size() >= 4 && input.compare(input.size() - 4, 4, ".rsrc") == 0;
if (is_res)
other = input.substr(0, input.size() - 4);
else
other = input + ".rsrc";
return is_res;
}
// Read "rsrc" from two files (data and ressource).
static void ReadRsrcDouble(UnPacked &u, const std::string &other, bool is_res)
{
uint32_t size = ReadLocalFile(other, u.d2);
if (is_res) {
u.file.data = u.d2.get();
u.file.data_size = size;
} else {
u.file.res = u.d2.get();
u.file.res_size = size;
}
}
// Read rsrc (raw) file(s).
bool ReadRsrc(fs::FileReader &reader, UnPacked &u)
{
u.file.Reset();
// Get the name of the other file to read.
std::string other;
bool is_res = GetOtherName(reader.filename, other);
// Read a raw file with one or two files.
bool is_double = Path(other).is_file();
if (is_double)
ReadRsrcDouble(u, other, is_res);
// Set input fork size.
if (is_res) {
u.file.filename = Path(other).filename();
u.file.res = u.d1.get();
u.file.res_size = reader.file_size;
} else {
u.file.filename = Path(reader.filename).filename();
u.file.data = u.d1.get();
u.file.data_size = reader.file_size;
}
// Find file information from the local file.
u.file.is_raw = true;
GetLocalInfo(is_double && is_res ? other : reader.filename, u.file);
return true;
}
// Write a single fork into a file.
static void WriteFork(fs::File &file, const std::string &name, uint8_t *data,
uint32_t length)
{
std::ofstream fstream {name, std::ios::binary};
fstream.write(reinterpret_cast<char *>(data), length);
fstream.close();
SetLocalInfo(file, name, file.res == data);
}
// Write raw (rsrc) file(s).
void WriteRsrc(fs::File &file, const std::string &name)
{
// Get the name of the other file to write.
std::string other;
bool is_res = GetOtherName(name, other);
// Write forks (write only if size is > 0).
if (file.data_size)
WriteFork(file, is_res ? other : name, file.data, file.data_size);
if (file.res_size)
WriteFork(file, is_res ? name : other, file.res, file.res_size);
}
// Write only the data fork (if exists).
void WriteOnlyData(fs::File &file, const std::string &name)
{
file.res_size = 0;
WriteRsrc(file, name);
}
} // namespace maconv
} // namespace conv

44
src/disk/disk.h Normal file
View File

@ -0,0 +1,44 @@
/*
Extract and pack disk images.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs/file.h"
#include "fs/file_reader.h"
#include "formats/formats.h"
namespace maconv {
namespace disk {
// Is a file a disk file?
bool IsFileDisk(const std::string &name);
// Extract a disk file.
void ExtractDisk(UnPacked &u, const std::string &out_folder);
// Pack files into a single disk image.
void PackDiskImage(const std::string &folder, const std::string &out,
const std::string &volname);
} // namespace disk
} // namespace maconv

157
src/disk/extract.cc Normal file
View File

@ -0,0 +1,157 @@
/*
Extract a disk image.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "disk/disk.h"
#include "commands.h"
#include <libhfs/hfs.h>
#include <libhfs/data.h>
#include <make_unique.hpp>
#include <path.hpp>
#include <cstdio>
namespace maconv {
namespace disk {
// Extract a single fork.
static void ExtractFork(hfsfile *hfile, const hfsdirent &ent, fs::File &file,
bool is_res)
{
// Allocate buffer.
int size = is_res ? ent.u.file.rsize : ent.u.file.dsize;
auto buffer = std::make_unique<uint8_t[]>(size);
// Read the data.
hfs_setfork(hfile, is_res ? 1 : 0);
if (hfs_read(hfile, buffer.get(), size) != size)
StopOnError("can't read %s (%d) from HFS disk", file.filename.c_str(), is_res);
// Fill file information.
if (is_res) {
file.res = buffer.get();
file.res_size = size;
} else {
file.data_size = size;
file.data = buffer.get();
}
// Save the buffer to the memory pool.
file.mem_pool.push_back(std::move(buffer));
}
// Extract a file from a disk.
static void ExtractFile(Path &localp, hfsvol *vol, const hfsdirent &ent)
{
Path::makedirs(localp);
fs::File file;
// Extract file information.
file.Reset();
file.type = d_getsl((const unsigned char *)ent.u.file.type);
file.creator = d_getsl((const unsigned char *)ent.u.file.creator);
file.flags = ent.fdflags;
file.filename = ent.name;
file.creation_date = ent.crdate;
file.modif_date = ent.mddate;
// Extract the two forks.
hfsfile *hfile = hfs_open(vol, ent.name);
ExtractFork(hfile, ent, file, false);
ExtractFork(hfile, ent, file, true);
hfs_close(hfile);
// Save the file.
auto out = Path::join(localp, GetFilenameFor(ent.name, prefered_conv));
PackLocalFile(file, out.string(), prefered_conv);
}
// Extract a directory from the disk.
static void ExtractDirectory(Path localp, hfsvol *vol, unsigned long id)
{
unsigned long current = hfs_getcwd(vol);
hfs_setcwd(vol, id);
hfsdir *dir = hfs_opendir(vol, ":");
hfsdirent ent;
while (hfs_readdir(dir, &ent) != -1) {
if (ent.fdflags & HFS_FNDR_ISINVISIBLE)
continue;
if (ent.flags & HFS_ISDIR)
ExtractDirectory(Path::join(localp, ent.name), vol, ent.cnid);
else
ExtractFile(localp, vol, ent);
}
hfs_closedir(dir);
hfs_setcwd(vol, current);
}
// Extract a disk file.
static void ExtractDiskFrom(const std::string &name, const std::string &out)
{
hfsvol *vol = hfs_mount(name.c_str(), 0, HFS_MODE_RDONLY);
ExtractDirectory(out, vol, HFS_CNID_ROOTDIR);
hfs_umount(vol);
}
// Extract a disk file.
void ExtractDisk(UnPacked &u, const std::string &out_folder)
{
// If it's not a "raw" file: extract the file directly.
if (u.file.is_raw)
ExtractDiskFrom(u.n1, out_folder);
// Else, save the data fork to a temporary file first and then extract.
char dirname[] = "/tmp/maconv.XXXXXX";
if (mkdtemp(dirname) == nullptr)
StopOnError("can't create temporary folder for HFS disk");
std::string name = std::string {dirname} + "/disk.dsk";
PackLocalFile(u.file, name, GetConverter("data"));
ExtractDiskFrom(name, out_folder);
std::remove(name.c_str());
rmdir(dirname);
}
// Is a file a disk file?
bool IsFileDisk(const std::string &name)
{
auto ext = Path(name).extension();
return ext == "dsk" || ext == "img";
}
} // namespace disk
} // namespace maconv

170
src/disk/pack.cc Normal file
View File

@ -0,0 +1,170 @@
/*
Pack files into a single disk image.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "disk/disk.h"
#include "commands.h"
#include <libhfs/hfs.h>
#include <libhfs/data.h>
#include <path.hpp>
#include <fstream>
#include <algorithm>
#include <unordered_set>
namespace maconv {
namespace disk {
// Type for seen files.
using SeenList = std::unordered_set<std::string>;
// Pack a directory into the disk.
static void PackDirectory(const Path &localp, hfsvol *vol);
// Write a single fork to an HFS file.
static void WriteFork(hfsfile *hfile, fs::File &file, bool is_res)
{
int size = is_res ? file.res_size : file.data_size;
uint8_t *data = is_res ? file.res : file.data;
hfs_setfork(hfile, is_res ? 1 : 0);
hfs_write(hfile, data, size);
}
// Pack a file into the disk.
static void PackFile(const Path &file, hfsvol *vol, SeenList &seen)
{
// Unpack the local file.
UnPacked u = UnPackLocalFile(file.string(), false);
// Insert file name(s) to seen list.
seen.insert(u.n1);
if (!u.n2.empty()) seen.insert(u.n2);
// Sanitize file name, type and creator.
auto filename = u.file.filename.substr(0, 31);
std::replace(filename.begin(), filename.end(), ':', '_');
char type[5] = {0}, creator[5] = {0};
d_putsl((unsigned char *)type, u.file.type);
d_putsl((unsigned char *)creator, u.file.creator);
// Create the file on the disk and write forks.
hfsfile *hfile = hfs_create(vol, filename.c_str(), type, creator);
if (hfile == nullptr)
StopOnError("can't create HFS file %s", filename.c_str());
WriteFork(hfile, u.file, false);
WriteFork(hfile, u.file, true);
// Set other attributes.
hfsdirent ent;
hfs_fstat(hfile, &ent);
ent.crdate = u.file.creation_date;
ent.mddate = u.file.modif_date;
ent.fdflags = u.file.flags;
ent.fdflags &= ~HFS_FNDR_ISINVISIBLE;
hfs_fsetattr(hfile, &ent);
hfs_close(hfile);
}
// Create a directory on the HFS disk and pack it.
static void CreateAndPackDirectory(const Path &localp, hfsvol *vol)
{
unsigned long current = hfs_getcwd(vol);
auto dirname = Path(localp).trim().filename().substr(0, 31);
std::replace(dirname.begin(), dirname.end(), ':', '_');
if (hfs_mkdir(vol, dirname.c_str()) != 0)
StopOnError("can't create HFS folder %s", dirname.c_str());
hfs_chdir(vol, dirname.c_str());
PackDirectory(localp, vol);
hfs_setcwd(vol, current);
}
// Pack a directory into the disk.
static void PackDirectory(const Path &localp, hfsvol *vol)
{
SeenList seen;
for (auto &file : Path::listdir(localp)) {
if (seen.count(file.string()) == 1) continue;
if (file.is_file())
PackFile(file, vol, seen);
else if (file.is_directory())
CreateAndPackDirectory(file, vol);
}
}
// Create a new file disk and mount it.
static hfsvol *CreateAndMountNewDisk(const std::string &filename,
const std::string &volname)
{
// Sanitize the volume name.
auto clean_name = volname.substr(0, 27);
std::replace(clean_name.begin(), clean_name.end(), ':', '_');
// Truncate the disk file.
int size = 20971520;
std::ofstream(filename, std::ios::binary | std::ios::trunc)
.seekp(size-1).put(0);
// Create and mount the file.
if (hfs_format(filename.c_str(), 0, 0, clean_name.c_str(), 0, NULL) != 0)
StopOnError("can't format disk file to HFS");
hfsvol *vol = hfs_mount(filename.c_str(), 0, HFS_MODE_RDWR);
if (vol == nullptr)
StopOnError("can't mount output disk image");
return vol;
}
// Pack files into a single disk image.
void PackDiskImage(const std::string &folder, const std::string &out,
const std::string &volname)
{
hfsvol *vol = CreateAndMountNewDisk(out, volname);
PackDirectory(folder, vol);
hfs_umount(vol);
}
} // namespace disk
} // namespace maconv

View File

@ -0,0 +1,140 @@
/*
Macintosh file signatures.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "formats/file_signature.h"
#include <memory>
namespace maconv {
namespace utils {
// Create a new entry in the "Unix-to-Mac" array.
#define S(type, creator, mac, helps...) \
SignToMac(SignToMac::type, creator, mac, (const char *[])helps, \
sizeof((const char *[])helps) / sizeof(const char *))
// File signature array.
SignToMac signs_to_mac[] = {
S(Extension, "TVOD", "Mp3 ", { ".mp3" }),
S(FileCmd, "TVOD", "Midi", { "Standard MIDI" }),
S(FileCmd, "TVOD", "WAVE", { "WAVE audio" }),
S(FileCmd, "CARO", "PDF ", { "PDF document" }),
S(FileCmd, "ZIP ", "ZIP ", { "Zip archive data" }),
S(Extension, "Gzip", "Gzip", { ".tar.gz", ".tgz" }),
S(FileCmd, "TARF", "TARF", { "tar archive" }),
S(FileCmd, "8BIM", "JPEG", { "JPEG image data" }),
S(FileCmd, "8BIM", "PNGf", { "PNG image data" }),
S(FileCmd, "8BIM", "BMPf", { "PC bitmap" }),
S(FileCmd, "8BIM", "TIFF", { "TIFF image data" }),
S(FileCmd, "8BIM", "GIFf", { "GIF image data" }),
S(Extension, "PPT3", "PPT3", { ".ppt", ".pptx" }),
S(Extension, "MSWD", "W8BN", { ".doc", ".docs" }),
S(Extension, "XCEL", "XLS ", { ".xls", ".xlsx" }),
// Else: UNKN, TEXT
};
// File signature array.
SignToUnix signs_to_unix[] = {
};
// Call Unix "file" command on a path.
static std::string CallFileCommand(const std::string &path)
{
using PosixPtr = std::unique_ptr<FILE, decltype(&pclose)>;
auto cmd = std::string {"file -d "} + path;
PosixPtr pipe {popen(cmd.c_str(), "r"), pclose};
if (!pipe)
return nullptr;
std::array<char, 128> buffer;
std::string result;
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
result += buffer.data();
return result;
}
// Try matching a file with an entry.
#define MAC_MATCH(type, func, entry, help) \
case SignToMac::type: if (func(entry, help)) return &entry; break
// Try matching a path with the given entry.
static bool UnixMatchExtension(const SignToMac &entry, const std::string &path)
{
auto pathlen = path.size();
auto cpath = path.c_str() + pathlen;
for (int i = 0; i < entry.help_len; i++) {
auto len = strlen(entry.helps[i]);
if (pathlen >= len && strcmp(cpath - len, entry.helps[i]) == 0)
return true;
}
return false;
}
// Try matching a "file" cmd output with the given entry.
static bool UnixMatchFileCmd(const SignToMac &entry, const std::string &output)
{
for (int i = 0; i < entry.help_len; i++) {
if (strstr(output.c_str(), entry.helps[i]) == nullptr)
return false;
}
return true;
}
// Get the MAC type (and creator) for a specific file.
const SignToMac *GetMacType(const std::string &path)
{
auto filetxt = CallFileCommand(path);
for (auto const &entry : signs_to_mac) {
switch (entry.type) {
MAC_MATCH(Extension, UnixMatchExtension, entry, path);
MAC_MATCH(FileCmd, UnixMatchFileCmd, entry, filetxt);
}
}
return nullptr;
}
} // namespace utils
} // namespace maconv

View File

@ -0,0 +1,69 @@
/*
Macintosh file signatures.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <path.hpp>
#include <string>
namespace maconv {
namespace utils {
// An entry in the "Unix-to-Mac" array.
struct SignToMac {
// Maching type: extension or file command.
enum Type { Extension, FileCmd } type;
// Help strings (and count)
int help_len;
const char **helps;
// MAC creator and type.
const char *creator;
const char *mac;
constexpr SignToMac(Type t, const char *c, const char *m, const char **hs, int hl)
: type{t}, creator{c}, mac{m}, help_len{hl}, helps{hs} {}
};
// An entry in the "Mac-to-Unix" array.
struct SignToUnix {
const char *mac;
const char *extension;
};
// File signature array.
extern SignToMac signs_to_mac[];
// File signature array.
extern SignToUnix signs_to_unix[];
// Get the MAC type (and creator) for a specific file.
const SignToMac *GetMacType(const std::string &path);
} // namespace utils
} // namespace maconv

124
src/formats/formats.cc Normal file
View File

@ -0,0 +1,124 @@
/*
All supported formats for file conversion.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "formats.h"
#include "conv/converters.h"
#include "disk/disk.h"
#include "stuffit/stuffit.h"
#include <path.hpp>
#include <cstring>
using namespace maconv::conv;
using namespace maconv::disk;
using namespace maconv::stuffit;
namespace maconv {
// Prefered conversion format.
ConvData prefered_conv = { ConvData::NotFound };
// All available converters.
ConvDataSingle formats_single[kNumFormatsSingle] = {
{ "macbin", ".bin", IsFileMacBinary, ReadMacBinary, WriteMacBinary },
{ "binhex", ".hqx", IsFileBinHex, ReadBinHex, WriteBinHex },
{ "applesingle", ".as", IsFileAppleSingle, ReadAppleSingle, WriteAppleSingle },
};
// Format with two files.
ConvDataDouble formats_double[kNumFormatsDouble] = {
// { "appledouble", ReadAppleDouble, WriteAppleDouble },
{ "rsrc", ReadRsrc, WriteRsrc },
{ "data", ReadRsrc, WriteOnlyData }
};
// Get a format converter from its name.
ConvData GetConverter(const std::string &name)
{
// Is the format a single format?
for (auto &format : formats_single) {
if (strcmp(format.name, name.c_str()) == 0)
return ConvData { ConvData::Single, static_cast<void *>(&format) };
}
// Or a double format?
for (auto &format : formats_double) {
if (strcmp(format.name, name.c_str()) == 0)
return ConvData { ConvData::Double, &format };
}
return ConvData { ConvData::NotFound };
}
// Guess a file format by its extension.
std::string GuessFormatByExtension(const std::string &filename)
{
auto ext = std::string {"."} + Path(filename).extension();
// It is a single format?
for (auto &format : formats_single) {
if (strcmp(format.ext, ext.c_str()) == 0)
return format.name;
}
// Other special formats.
if (ext == ".hex" || ext == ".hcx")
return "binhex";
return "rsrc";
}
// Get a filename for a converter.
std::string GetFilenameFor(const std::string &original, ConvData conv)
{
auto ext = (conv.type == ConvData::Double) ? "" : conv.s->ext;
return Path(original).filename() + ext;
}
// Extract an archive or a disk.
bool ExtractArchiveOrDisk(UnPacked &u, const std::string &output)
{
fs::FileReader reader {u.file};
if (IsFileDisk(u.file.filename))
ExtractDisk(u, output);
else if (IsFileStuffit1(reader))
ExtractStuffit1(reader, output);
else if (IsFileStuffit5(reader))
ExtractStuffit5(reader, output);
else
return false;
return true;
}
} // namespace maconv

111
src/formats/formats.h Normal file
View File

@ -0,0 +1,111 @@
/*
All supported formats for file conversion.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs/file.h"
#include "fs/file_reader.h"
#include "fs/file_writer.h"
#include <fstream>
namespace maconv {
// Data for unpacked files.
struct UnPacked {
fs::DataPtr d1, d2; // Data from opened files.
fs::File file; // The unpacked file.
std::string n1, n2; // Input file names.
};
// Convertion data with a single input file.
struct ConvDataSingle {
using TestF = bool (*)(fs::FileReader &);
using ReaderF = void (*)(fs::FileReader &, fs::File &);
using WriterF = void (*)(fs::File &, fs::FileWriter &);
const char *name; // Converter name.
const char *ext; // File extension.
TestF test;
ReaderF read;
WriterF write;
};
// Convertion data with two input files.
struct ConvDataDouble {
using ReaderF = bool (*)(fs::FileReader &reader, UnPacked &u);
using WriterF = void (*)(fs::File &, const std::string &);
const char *name; // Converter name.
ReaderF read;
WriterF write;
};
// Store data about a convertion format.
struct ConvData {
enum { NotFound, Single, Double } type;
union { void *v; ConvDataSingle *s; ConvDataDouble *d; };
};
// Prefered conversion format.
extern ConvData prefered_conv;
// Single converters (converters that need only one file).
constexpr int kNumFormatsSingle = 3;
extern ConvDataSingle formats_single[kNumFormatsSingle];
// Double converters (converters that need two files).
constexpr int kNumFormatsDouble = 2;
extern ConvDataDouble formats_double[kNumFormatsDouble];
// Get a format converter from its name.
ConvData GetConverter(const std::string &name);
// Guess a file format by its extension.
std::string GuessFormatByExtension(const std::string &filename);
// Get a filename for a converter.
std::string GetFilenameFor(const std::string &original, ConvData conv);
// Unpack a single file.
bool UnPackSingle(fs::File &file, fs::FileReader &reader);
// Unpack a local file (recursively).
UnPacked UnPackLocalFile(const std::string &input, bool recurs = true);
// Extract an archive or a disk.
bool ExtractArchiveOrDisk(UnPacked &u, const std::string &output);
// Pack a local file.
void PackLocalFile(fs::File &file, const std::string &filename, ConvData data);
} // namespace maconv

56
src/formats/pack.cc Normal file
View File

@ -0,0 +1,56 @@
/*
Pack a local file.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "formats/formats.h"
#include <path.hpp>
namespace maconv {
// Pack a single file.
static void PackSingleFile(fs::File &file, const std::string &filename,
ConvDataSingle *conv)
{
std::ofstream fstream {filename, std::ios::binary};
fs::FileWriter writer {fstream};
conv->write(file, writer);
}
// Pack a local file.
void PackLocalFile(fs::File &file, const std::string &filename, ConvData data)
{
// Create destination directory.
Path::makedirs(Path(filename).parent());
// Pack this file in one or two files.
if (data.type == ConvData::Single)
PackSingleFile(file, filename, data.s);
else
data.d->write(file, filename);
}
} // namespace maconv

97
src/formats/unpack.cc Normal file
View File

@ -0,0 +1,97 @@
/*
Unpack a local file.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "formats/formats.h"
#include "commands.h"
namespace maconv {
// Unpack two files (double format).
static void UnPackDouble(fs::FileReader &reader, UnPacked &u)
{
for (auto &format : formats_double) {
reader.Seek(0);
// "read" returns true if the file is actually read.
if (format.read(reader, u))
return;
}
}
// Unpack a single file.
bool UnPackSingle(fs::File &file, fs::FileReader &reader)
{
for (auto &format : formats_single) {
// Test if the file is in this format (if true, unpack it).
reader.Seek(0);
if (!format.test(reader))
continue;
reader.Seek(0);
file.Reset();
format.read(reader, file);
return true;
}
return false;
}
// Unpack the first local file.
static UnPacked UnPackFirstLocalFile(const std::string &input)
{
UnPacked u;
u.n1 = input;
// Read the input file given.
int size = ReadLocalFile(input, u.d1);
if (size == -1)
StopOnError("can't read input file %s", input.c_str());
// Unpack one or two files.
fs::FileReader reader {u.d1.get(), static_cast<uint32_t>(size), input};
if (!UnPackSingle(u.file, reader))
UnPackDouble(reader, u); // Cannot fail ("rsrc" accepts all files).
return u;
}
// Unpack a local file (recursively).
UnPacked UnPackLocalFile(const std::string &input, bool recurs)
{
auto u = UnPackFirstLocalFile(input);
while (recurs) {
fs::FileReader reader {u.file};
if (!UnPackSingle(u.file, reader))
break;
}
return u;
}
} // namespace maconv

92
src/fs/file.cc Normal file
View File

@ -0,0 +1,92 @@
/*
Store a Macintosh file.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "fs/file.h"
#include <fstream>
#include <make_unique.hpp>
#include <sys/types.h>
#include <utime.h>
namespace maconv {
// Reset all data from its file.
void fs::File::Reset()
{
data = nullptr;
res = nullptr;
data_size = 0;
res_size = 0;
creation_date = 0;
modif_date = 0;
creator = 0x63636363; // ????
type = 0x63636363; // ????
filename.clear();
is_raw = false;
}
// Read data from a local file.
int ReadLocalFile(const std::string &filename, fs::DataPtr &ptr)
{
std::ifstream file {filename, std::ios::binary};
if (!file)
return -1;
// Get file size and allocate this size.
file.seekg(0, std::ios_base::end);
int size = file.tellg();
ptr = std::make_unique<uint8_t[]>(size);
// Read the file entirely.
file.seekg(0, std::ios_base::beg);
file.read(reinterpret_cast<char *>(ptr.get()), size);
return size;
}
// Get file infotmation from a local file.
void GetLocalInfo(const std::string &filename, fs::File &file, bool is_res)
{
// TODO.
}
// Set file infotmation to a local file.
void SetLocalInfo(fs::File &file, const std::string &filename, bool is_res)
{
auto date = is_res ? file.creation_date : file.modif_date;
utimbuf buf { date, date };
utime(filename.c_str(), &buf);
}
} // namespace maconv

78
src/fs/file.h Normal file
View File

@ -0,0 +1,78 @@
/*
Store a Macintosh file.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#include <vector>
#include <time.h>
namespace maconv {
namespace fs {
// An unique pointer on memory data.
using DataPtr = std::unique_ptr<uint8_t[]>;
// Store data about a Macintosh file.
struct File {
// Reset all data from its file.
void Reset();
uint8_t *data; // Data fork.
uint32_t data_size; // Size of the data fork.
uint8_t *res; // Ressource fork.
uint32_t res_size; // Size of the ressource fork.
std::string filename; // Name of the file.
uint32_t type; // File type (4 chars).
uint32_t creator; // File creator (4 chars).
uint16_t flags; // Finder flags (see docs for more information).
time_t creation_date; // Creation date of the file (Unix time).
time_t modif_date; // Modification date of the file (Unix file).
bool is_raw; // Is this file from "raw" format.
std::vector<DataPtr> mem_pool; // Memory pool for storing data.
};
} // namespace fs
// Read data from a local file.
int ReadLocalFile(const std::string &filename, fs::DataPtr &ptr);
// Get file infotmation from a local file.
void GetLocalInfo(const std::string &filename, fs::File &file, bool is_res = false);
// Set file infotmation to a local file.
void SetLocalInfo(fs::File &file, const std::string &filename, bool is_res = false);
} // namespace maconv

124
src/fs/file_reader.cc Normal file
View File

@ -0,0 +1,124 @@
/*
File Reader: helper class for reading data from a file.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "fs/file_reader.h"
#include <libhfs/data.h>
namespace maconv {
namespace fs {
// Constructor (with an istream).
FileReader::FileReader(uint8_t *d, uint32_t fs, std::string fn)
: data{d}, file_size{fs}, filename{std::move(fn)},
stream_buf{d, fs}, stream{&stream_buf}
{
}
// Constructor (with a MAC file).
FileReader::FileReader(File &file)
: data{file.data}, file_size{file.data_size}, filename{file.filename},
stream_buf{file.data, file.data_size}, stream{&stream_buf}
{
}
// Read a single byte.
uint8_t FileReader::ReadByte()
{
return static_cast<uint8_t>(stream.get());
}
// Read a single half (16bits, big endian).
uint16_t FileReader::ReadHalfBE()
{
uint8_t b[2];
stream.read(reinterpret_cast<char *>(b), 2);
return static_cast<int32_t>(b[1] | (b[0] << 8));
}
// Read a single half (16bits, little endian).
uint16_t FileReader::ReadHalfLE()
{
uint16_t half;
stream.read(reinterpret_cast<char *>(&half), 2);
return half;
}
// Read a single word (32bits, big endian).
uint32_t FileReader::ReadWordBE()
{
uint8_t b[4];
stream.read(reinterpret_cast<char *>(b), 4);
return static_cast<int32_t>(b[3] | (b[2] << 8) | (b[1] << 16) | (b[0] << 24));
}
// Read a single word (32bits, little endian).
uint32_t FileReader::ReadWordLE()
{
uint32_t word;
stream.read(reinterpret_cast<char *>(&word), 4);
return word;
}
// Read a Macintosh date (epoch on January 1, 1904).
time_t FileReader::ReadMacDate()
{
return d_ltime(ReadWordBE());
}
// Read a J2000 date (epoch on January 1, 2000).
time_t FileReader::ReadJ2000Date()
{
return ReadMacDate() + 3029529600UL;
}
// Read a string (read until \0).
std::string FileReader::ReadString()
{
std::string result;
char c;
while (c = stream.get(), c != '\0')
result += c;
return result;
}
// Read a string of |size|.
std::string FileReader::ReadString(uint32_t size)
{
std::string result(size, '\0');
stream.read(&result[0], size);
return result;
}
} // namespace fs
} // namespace maconv

87
src/fs/file_reader.h Normal file
View File

@ -0,0 +1,87 @@
/*
File Reader: helper class for reading data from a file.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs/file.h"
#include "utils/buffer_stream.h"
#include <sstream>
#include <string>
#include <time.h>
namespace maconv {
namespace fs {
// Helper class for reading data from a file.
struct FileReader {
FileReader(uint8_t *data, uint32_t size, std::string filename = "");
FileReader(File &file);
// Set the position in the stream.
void Seek(uint32_t pos) { stream.seekg(pos); }
void Seek(uint32_t off, std::ios_base::seekdir dir) { stream.seekg(off, dir); }
// Get the absolute position.
uint32_t Tell() { return stream.tellg(); }
// Skip from reading a number of bytes.
void Skip(uint32_t length) { stream.ignore(length); }
// Read a single byte.
uint8_t ReadByte();
// Read a single short (16bits) (BE = big endian, LE = little endian).
uint16_t ReadHalfBE();
uint16_t ReadHalfLE();
// Read a single word (32bits) (BE = big endian, LE = little endian).
uint32_t ReadWordBE();
uint32_t ReadWordLE();
// Read a Macintosh date or a J2000 date (epoch on January 1, 2000).
time_t ReadMacDate();
time_t ReadJ2000Date();
// Read a string of |size| (if no size, read until \0).
std::string ReadString();
std::string ReadString(uint32_t size);
uint8_t *data; // Input data.
uint32_t file_size; // Total size of the file.
utils::RawDataStreamBuf stream_buf; // Stream buffer from raw data buffer.
std::istream stream; // Stream for reading data.
std::string filename; // Name of the file to read.
};
// Test a condtion and return false if the condition is false.
#define IS_COND(cond) if (!(cond)) return false
} // namespace fs
} // namespace maconv

121
src/fs/file_writer.cc Normal file
View File

@ -0,0 +1,121 @@
/*
File Reader: helper class for writing data to a file.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "fs/file_writer.h"
#include <libhfs/data.h>
namespace maconv {
namespace fs {
// Constructor.
FileWriter::FileWriter(std::ostream &s)
: stream{s}
{
}
// Write some bytes.
void FileWriter::Write(uint8_t *bytes, uint32_t length)
{
stream.write(reinterpret_cast<char *>(bytes), length);
}
// Fill |length| bytes of |byte|.
void FileWriter::Fill(uint8_t byte, uint32_t length)
{
for (uint32_t i = 0; i < length; i++)
stream.put(byte);
}
// Write a single byte.
void FileWriter::WriteByte(uint8_t byte)
{
stream.put(byte);
}
// Write a single short (16bits, big endian).
void FileWriter::WriteHalfBE(uint16_t half)
{
uint8_t b[2] = {
static_cast<uint8_t>(half >> 8),
static_cast<uint8_t>(half & 0xFF)
};
stream.write(reinterpret_cast<char *>(b), 2);
}
// Write a single short (16bits, little endian).
void FileWriter::WriteHalfLE(uint16_t half)
{
stream.write(reinterpret_cast<char *>(&half), 2);
}
// Write a single word (32bits, big endian).
void FileWriter::WriteWordBE(uint32_t w)
{
uint8_t b[4] = {
static_cast<uint8_t>(w >> 24),
static_cast<uint8_t>((w >> 16) & 0xFF),
static_cast<uint8_t>((w >> 8) & 0xFF),
static_cast<uint8_t>(w & 0xFF)
};
stream.write(reinterpret_cast<char *>(b), 4);
}
// Write a single word (32bits, little endian).
void FileWriter::WriteWordLE(uint32_t word)
{
stream.write(reinterpret_cast<char *>(&word), 4);
}
// Write a Macintosh date (epoch on January 1, 1904).
void FileWriter::WriteMacDate(time_t date)
{
WriteWordLE(d_mtime(date));
}
// Write a J2000 date (epoch on January 1, 2000).
void FileWriter::WriteJ2000Date(time_t date)
{
return WriteMacDate(date - 3029529600UL);
}
// Write a string.
void FileWriter::WriteString(const std::string &str)
{
stream << str;
}
} // namespace fs
} // namespace maconv

67
src/fs/file_writer.h Normal file
View File

@ -0,0 +1,67 @@
/*
File Reader: helper class for writing data to a file.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <ostream>
#include <string>
namespace maconv {
namespace fs {
// Helper class for writing data to a file.
struct FileWriter {
FileWriter(std::ostream &stream);
// Write some bytes.
void Write(uint8_t *bytes, uint32_t length);
// Fill |length| bytes of |byte|.
void Fill(uint8_t byte, uint32_t length);
// Write a single byte.
void WriteByte(uint8_t byte);
// Write a single short (16bits) (BE = big endian, LE = little endian).
void WriteHalfBE(uint16_t half);
void WriteHalfLE(uint16_t half);
// Write a single word (32bits) (BE = big endian, LE = little endian).
void WriteWordBE(uint32_t word);
void WriteWordLE(uint32_t word);
// Write a Macintosh date or a J2000 date (epoch on January 1, 2000).
void WriteMacDate(time_t date);
void WriteJ2000Date(time_t date);
// Write a string.
void WriteString(const std::string &str);
std::ostream &stream; // Output stream for writing data.
};
} // namespace fs
} // namespace maconv

134
src/maconv.1 Normal file
View File

@ -0,0 +1,134 @@
.TH MACONV 1 "2019-01-04" "v1.0" "maconv"
.SH NAME
maconv \- Macintosh format converter
.SH SYNOPSIS
.B "maconv c [options] input-file [output-file]"
.br
.B "maconv e [options] input-file [output-folder]"
.br
.B "maconv d [options] input-folder [output-file]"
.SH DESCRIPTION
\fBmaconv\fR(1) is a Linux software that can convert all kinds
of old Macintosh formats, including MacBinary, Stuffit archives and HFS disk
files.
.SH OPTIONS
Maconv has three sub-commands:
.BR "c" ", " "e" " and " "d" .
.br
Each sub-command can take the following flags:
.TP 4
.B "-h,--help"
Print an help message about \fBMaconv\fR and exit.
.TP 4
.B "-v,--verbose"
Enable verbose mode (i.e. log useful information when converting/extracting
files).
.RE
Some sub-commands take a format argument. This argument must be one of the
following formats:
.BR macbin ", " applesingle ", " rsrc " or " data " (saves only data fork)."
.RE
.B "FILE CONVERSION (maconv c)"
.RS 4
This sub-command converts a file from a format to another. Supported formats are
listed above. The command takes the following arguments:
.TP 4
.B "input-file"
The output format. If not provided this format is determined by the output
filename extension. If both this format and the output filename are not provided
the selected format is
.BR rsrc .
.TP 4
.B "output-file"
Name of the output file. This argument is optional. By default it's the name of
the input file with another extension for a format that generates only one file
or the "real" name of the file for other formats.
.TP 4
.BI "-f,--format" " format"
The output format. If not provided this format is determined by the output
filename extension. If both this format and the output filename are not provided
the selected format is
.BR rsrc .
.RE
.B "ARCHIVE EXTRACTION (maconv e)"
.RS 4
This sub-command extracts a Stuffit archive (version 1 or 5) or an HFS disk image.
The command takes the following arguments:
.TP 4
.B "input-file"
The input archive/disk image.
.TP 4
.B "output-folder"
The folder in which the archive/disk image will be extracted. This argument is
optional. By default the output folder is
.BR . " (current folder)."
.TP 4
.BI "-f,--format" " format"
Format with which extracted files will be saved. By default this format is
.BR rsrc .
.RE
.B "DISK CREATION (maconv d)"
.RS 4
This sub-command creates an HFS disk image from a folder (like a file archiver).
The command takes the following arguments:
.TP 4
.B "input-folder"
The input folder. Files in this folder will be added to the HFS disk image.
.TP 4
.B "output-file"
Name of the disk image to create. This argument is optional. By default the disk
image has the same name as the input folder plus
.IR .img .
.TP 4
.BI "-n,--name" " vol-name"
Name of the volume to create on the disk image. By default it's the base name of
the input folder.
.SH EXAMPLES
.TP 4
.B maconv c file-macbin.bin
Extract both data and ressource forks from
.I file-macbin.bin
(MacBinary format) and save them using
.BR rsrc " format."
.TP 4
.B maconv e -f macbin archive.sit
Extract a stuffit archive named
.IR archive.sit .
Extracted files are saved in
.B macbin
format.
.SH SEE ALSO
.BR unstuff "(1),"
.BR macunpack "(1)"
.SH AUTHOR
Guillaume Gonnet <Guillaume.Gonnet@grenoble-inp.org>

137
src/main.cc Normal file
View File

@ -0,0 +1,137 @@
/*
Maconv: Macintosh format converter.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "commands.h"
#include <CLI11.hpp>
#include <cstdarg>
using namespace maconv;
// Is the verbose mode enabled?
bool maconv::verbose;
// Log debug message (if verbose mode is enabled).
extern "C" void LogDebug(const char *fmt, ...)
{
if (!verbose) return;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
printf("\n");
}
// Stop the program on an error.
extern "C" void StopOnError(const char *fmt, ...)
{
va_list args;
fprintf(stderr, "\033[1m\033[31mERROR: ");
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\033[0m\n");
exit(1);
}
// Entry point of the application.
int main(int argc, char **argv)
{
// Create the base command.
CLI::App app {"Maconv: Macintosh format converter"};
app.require_subcommand();
app.add_flag("-v,--verbose", verbose, "Enable verbose mode");
// Convert "c" sub-command.
auto c_app = app.add_subcommand("c", "Convert a file from a format to another");
std::string c_input;
c_app->add_option("input", c_input, "Input file to convert")
->required()
->type_name("<filename>");
std::string c_output;
c_app->add_option("output", c_output, "Output converted file (autodetected by default)")
->type_name("<filename>");
std::string c_format;
c_app->add_option("-f,--format", c_format, "Target format (autodetected or rsrc by default)")
->type_name("<format>");
// Extract "e" sub-command.
auto e_app = app.add_subcommand("e", "Extract a Stuffit archive or a disk file");
std::string e_input;
e_app->add_option("input", e_input, "Input file to extract")
->required()
->type_name("<filename>");
std::string e_output;
e_app->add_option("output", e_output, "Output folder (current folder by default)")
->default_val(".")
->type_name("<folder name>");
std::string e_format;
e_app->add_option("-f,--format", e_format, "Res. format (rsrc by default)")
->default_val("rsrc")
->type_name("<format>");
// Disk creation "d" sub-command.
auto d_app = app.add_subcommand("d", "Create an HFS disk file");
std::string d_folder;
d_app->add_option("folder", d_folder, "Root of the disk")
->required()
->type_name("<folder name>");
std::string d_output;
d_app->add_option("output", d_output, "Output disk file")
->type_name("<filename>");
std::string d_name;
d_app->add_option("-n,--name", d_name, "Volume name (Input folder name by default)")
->type_name("<name>");
// Parse the CLI.
CLI11_PARSE(app, argc, argv);
// Select the right command to execute.
if (*c_app)
RunConvertCommand(c_input, c_output, c_format);
else if (*e_app)
RunExtractCommand(e_input, e_output, e_format);
else if (*d_app)
RunDiskCommand(d_folder, d_output, d_name);
}

101
src/stuffit/methods.cc Normal file
View File

@ -0,0 +1,101 @@
/*
All Stuffit compression methods.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stuffit/methods.h"
#include "stuffit/methods/rle90.h"
#include "stuffit/methods/compress.h"
#include "stuffit/methods/algorithm13.h"
#include "stuffit/methods/arsenic.h"
#include <make_unique.hpp>
#include <stdlib.h>
namespace maconv {
namespace stuffit {
// Get a compression method (from a method number).
CompMethodPtr GetCompressionMethod(uint8_t method)
{
switch (method) {
case 0: // No compression.
return std::make_unique<NoneMethod>();
case 1: // RLE 90 algorithm.
return std::make_unique<Rle90Method>();
case 2: // Compress algorithm (LZW).
return std::make_unique<CompressMethod>();
case 13: // LZSS and Huffman.
return std::make_unique<Algorithm13Method>();
case 15: // Arsenic algorithm.
return std::make_unique<ArsenicMethod>();
default:
return nullptr;
}
}
// Extract data from the compressed fork.
void CompressionMethod::Extract(const StuffitCompInfo &info, uint8_t *data,
std::vector<fs::DataPtr> &mem_pool)
{
uint32_t capacity = info.size ? info.size : info.comp_size;
this->data = data + info.offset;
this->end = this->data + info.comp_size;
Initialize();
std::unique_ptr<uint8_t[]> buffer;
buffer.reset((uint8_t *)malloc(capacity));
total_size = 0;
// Uncompress the data chunk by chunks.
for (uint32_t len = 0; true;) {
len = ReadBytes(buffer.get() + total_size, capacity - total_size);
if (len == -1)
break;
total_size += len;
if (len == 0 || total_size == capacity) {
capacity = (capacity * 3) / 2;
buffer.reset((uint8_t *)realloc((void *)buffer.release(), capacity));
}
}
// Store the uncompressed buffer in the mempool.
uncompressed = buffer.get();
mem_pool.emplace_back(std::move(buffer));
}
// Extract data from the compressed fork.
void NoneMethod::Extract(const StuffitCompInfo &info, uint8_t *data,
std::vector<fs::DataPtr> &mem_pool)
{
total_size = info.size ? info.size : info.comp_size;
uncompressed = data + info.offset;
}
} // namespace stuffit
} // namespace maconv

87
src/stuffit/methods.h Normal file
View File

@ -0,0 +1,87 @@
/*
All Stuffit compression methods.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "stuffit/stuffit.h"
#include <memory>
#include <string>
namespace maconv {
namespace stuffit {
// Exception when extracting a fork.
struct ExtractException : std::exception {
ExtractException(std::string what) : msg{what} {}
const char* what() const noexcept { return msg.c_str(); }
std::string msg;
};
// A compression method.
struct CompressionMethod {
virtual ~CompressionMethod() = default;
// Read the next bytes.
virtual int32_t ReadBytes(uint8_t *data, uint32_t length) { return -1; }
// Initialize the algorithm.
virtual void Initialize() {}
// Extract data from the compressed fork.
virtual void Extract(const StuffitCompInfo &info, uint8_t *data,
std::vector<fs::DataPtr> &mem_pool);
uint8_t *data; // Compressed data.
uint8_t *end; // End of compressed data.
uint8_t *uncompressed; // Pointer on t uncompressed data.
uint32_t total_size; // Length of uncompressed data.
};
// An unique pointer on a compression method.
using CompMethodPtr = std::unique_ptr<CompressionMethod>;
// Get a compression method (from a method number).
CompMethodPtr GetCompressionMethod(uint8_t method);
// "No compression" method.
struct NoneMethod : CompressionMethod {
// Extract data from the compressed fork.
void Extract(const StuffitCompInfo &info, uint8_t *data,
std::vector<fs::DataPtr> &mem_pool) override;
};
} // namespace stuffit
} // namespace maconv

View File

@ -0,0 +1,487 @@
/*
Stuffit algorithm 13: LZSS and Huffman.
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stuffit/methods/algorithm13.h"
#include <make_unique.hpp>
#include <string.h>
namespace maconv {
namespace stuffit {
// First code lengths (1).
static const int kFirstCodeLengths_1[321] = {
4, 5, 7, 8, 8, 9, 9, 9, 9, 7, 9, 9, 9, 8, 9, 9, 9, 9, 9,
9, 9, 9, 9, 10, 9, 9, 10, 10, 9, 10, 9, 9, 5, 9, 9, 9, 9, 10,
9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 9, 8, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9,
9, 7, 8, 9, 7, 9, 9, 7, 7, 9, 9, 9, 9, 10, 9, 10, 10, 10, 9,
9, 9, 5, 9, 8, 7, 5, 9, 8, 8, 7, 9, 9, 8, 8, 5, 5, 7, 10,
5, 8, 5, 8, 9, 9, 9, 9, 9, 10, 9, 9, 10, 9, 9, 10, 10, 10, 10,
10, 10, 10, 9, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10, 10, 10, 10, 10, 9, 9, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10,
10, 10, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 10, 10, 9, 10, 10, 10, 10, 10, 10,
10, 9, 10, 10, 10, 9, 10, 9, 5, 6, 5, 5, 8, 9, 9, 9, 9, 9, 9,
10, 10, 10, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 9, 10, 9, 9, 9, 10, 9, 10, 9, 10, 9, 10, 9,
10, 10, 10, 9, 10, 9, 10, 10, 9, 9, 9, 6, 9, 9, 10, 9, 5
};
// First code lengths (2).
static const int kFirstCodeLengths_2[321] = {
4, 7, 7, 8, 7, 8, 8, 8, 8, 7, 8, 7, 8, 7, 9, 8, 8, 8, 9,
9, 9, 9, 10, 10, 9, 10, 10, 10, 10, 10, 9, 9, 5, 9, 8, 9, 9, 11,
10, 9, 8, 9, 9, 9, 8, 9, 7, 8, 8, 8, 9, 9, 9, 9, 9, 10, 9,
9, 9, 10, 9, 9, 10, 9, 8, 8, 7, 7, 7, 8, 8, 9, 8, 8, 9, 9,
8, 8, 7, 8, 7, 10, 8, 7, 7, 9, 9, 9, 9, 10, 10, 11, 11, 11, 10,
9, 8, 6, 8, 7, 7, 5, 7, 7, 7, 6, 9, 8, 6, 7, 6, 6, 7, 9,
6, 6, 6, 7, 8, 8, 8, 8, 9, 10, 9, 10, 9, 9, 8, 9, 10, 10, 9,
10, 10, 9, 9, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 11, 10, 10, 10, 10,
10, 10, 10, 11, 10, 11, 10, 10, 9, 11, 10, 10, 10, 10, 10, 10, 9, 9, 10,
11, 10, 11, 10, 11, 10, 12, 10, 11, 10, 12, 11, 12, 10, 12, 10, 11, 10, 11,
11, 11, 9, 10, 11, 11, 11, 12, 12, 10, 10, 10, 11, 11, 10, 11, 10, 10, 9,
11, 10, 11, 10, 11, 11, 11, 10, 11, 11, 12, 11, 11, 10, 10, 10, 11, 10, 10,
11, 11, 12, 10, 10, 11, 11, 12, 11, 11, 10, 11, 9, 12, 10, 11, 11, 11, 10,
11, 10, 11, 10, 11, 9, 10, 9, 7, 3, 5, 6, 6, 7, 7, 8, 8, 8, 9,
9, 9, 11, 10, 10, 10, 12, 13, 11, 12, 12, 11, 13, 12, 12, 11, 12, 12, 13,
12, 14, 13, 14, 13, 15, 13, 14, 15, 15, 14, 13, 15, 15, 14, 15, 14, 15, 15,
14, 15, 13, 13, 14, 15, 15, 14, 14, 16, 16, 15, 15, 15, 12, 15, 10
};
// First code lengths (3).
static const int kFirstCodeLengths_3[321] = {
6, 6, 6, 6, 6, 9, 8, 8, 4, 9, 8, 9, 8, 9, 9, 9, 8, 9, 9,
10, 8, 10, 10, 10, 9, 10, 10, 10, 9, 10, 10, 9, 9, 9, 8, 10, 9, 10,
9, 10, 9, 10, 9, 10, 9, 9, 8, 9, 8, 9, 9, 9, 10, 10, 10, 10, 9,
9, 9, 10, 9, 10, 9, 9, 7, 8, 8, 9, 8, 9, 9, 9, 8, 9, 9, 10,
9, 9, 8, 9, 8, 9, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
9, 8, 8, 9, 8, 9, 7, 8, 8, 9, 8, 10, 10, 8, 9, 8, 8, 8, 10,
8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 9, 7, 9, 9, 10, 10,
10, 10, 10, 9, 10, 10, 10, 10, 10, 10, 9, 9, 10, 10, 10, 10, 10, 10, 10,
10, 9, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10,
10, 9, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10, 9,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 10, 10, 10,
10, 10, 10, 9, 10, 10, 10, 10, 10, 10, 9, 9, 9, 10, 10, 10, 10, 10, 10,
9, 9, 10, 9, 9, 8, 9, 8, 9, 4, 6, 6, 6, 7, 8, 8, 9, 9, 10,
10, 10, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 7, 10, 10, 10, 7, 10, 10, 7, 7, 7, 7, 7, 6, 7, 10, 7, 7, 10,
7, 7, 7, 6, 7, 6, 6, 7, 7, 6, 6, 9, 6, 9, 10, 6, 10
};
// First code lengths (4).
static const int kFirstCodeLengths_4[321] = {
2, 6, 6, 7, 7, 8, 7, 8, 7, 8, 8, 9, 8, 9, 9, 9, 8, 8, 9,
9, 9, 10, 10, 9, 8, 10, 9, 10, 9, 10, 9, 9, 6, 9, 8, 9, 9, 10,
9, 9, 9, 10, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 10, 10, 9, 7, 7, 8, 8, 8, 8, 9, 9, 7, 8, 9, 10,
8, 8, 7, 8, 8, 10, 8, 8, 8, 9, 8, 9, 9, 10, 9, 11, 10, 11, 9,
9, 8, 7, 9, 8, 8, 6, 8, 8, 8, 7, 10, 9, 7, 8, 7, 7, 8, 10,
7, 7, 7, 8, 9, 9, 9, 9, 10, 11, 9, 11, 10, 9, 7, 9, 10, 10, 10,
11, 11, 10, 10, 11, 10, 10, 10, 11, 11, 10, 9, 10, 10, 11, 10, 11, 10, 11,
10, 10, 10, 11, 10, 11, 10, 10, 9, 10, 10, 11, 10, 10, 10, 10, 9, 10, 10,
10, 10, 11, 10, 11, 10, 11, 10, 11, 11, 11, 10, 12, 10, 11, 10, 11, 10, 11,
11, 10, 8, 10, 10, 11, 10, 11, 11, 11, 10, 11, 10, 11, 10, 11, 11, 11, 9,
10, 11, 11, 10, 11, 11, 11, 10, 11, 11, 11, 10, 10, 10, 10, 10, 11, 10, 10,
11, 11, 10, 10, 9, 11, 10, 10, 11, 11, 10, 10, 10, 11, 10, 10, 10, 10, 10,
10, 9, 11, 10, 10, 8, 10, 8, 6, 5, 6, 6, 7, 7, 8, 8, 8, 9, 10,
11, 10, 10, 11, 11, 12, 12, 10, 11, 12, 12, 12, 12, 13, 13, 13, 13, 13, 12,
13, 13, 15, 14, 12, 14, 15, 16, 12, 12, 13, 15, 14, 16, 15, 17, 18, 15, 17,
16, 15, 15, 15, 15, 13, 13, 10, 14, 12, 13, 17, 17, 18, 10, 17, 4
};
// First code lengths (5).
static const int kFirstCodeLengths_5[321] = {
7, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 9, 7, 9, 9, 9, 9, 9,
9, 9, 9, 9, 10, 9, 10, 9, 10, 9, 10, 9, 9, 5, 9, 7, 9, 9, 9,
9, 9, 7, 7, 7, 9, 7, 7, 8, 7, 8, 8, 7, 7, 9, 9, 9, 9, 7,
7, 7, 9, 9, 9, 9, 9, 9, 7, 9, 7, 7, 7, 7, 9, 9, 7, 9, 9,
7, 7, 7, 7, 7, 9, 7, 8, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 7, 8, 7, 7, 7, 8, 8, 6, 7, 9, 7, 7, 8, 7, 5, 6, 9,
5, 7, 5, 6, 7, 7, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 9, 10,
10, 10, 9, 9, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 9, 10, 10, 10, 9, 10, 10, 10, 9, 9, 10, 9, 9, 9, 9,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10, 10, 10, 10, 10,
10, 10, 9, 10, 10, 10, 9, 10, 10, 10, 9, 9, 9, 10, 10, 10, 10, 10, 9,
10, 9, 10, 10, 9, 10, 10, 9, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10, 10, 10, 10,
10, 9, 10, 9, 10, 9, 10, 10, 9, 5, 6, 8, 8, 7, 7, 7, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 5, 10, 8, 9, 8, 9
};
// First code lengths (global array).
static const int *kFirstCodeLengths[5] = {
kFirstCodeLengths_1, kFirstCodeLengths_2, kFirstCodeLengths_3,
kFirstCodeLengths_4, kFirstCodeLengths_5
};
// Second code lengths (1).
static const int kSecondCodeLengths_1[321] = {
4, 5, 6, 6, 7, 7, 6, 7, 7, 7, 6, 8, 7, 8, 8, 8, 8, 9, 6,
9, 8, 9, 8, 9, 9, 9, 8, 10, 5, 9, 7, 9, 6, 9, 8, 10, 9, 10,
8, 8, 9, 9, 7, 9, 8, 9, 8, 9, 8, 8, 6, 9, 9, 8, 8, 9, 9,
10, 8, 9, 9, 10, 8, 10, 8, 8, 8, 8, 8, 9, 7, 10, 6, 9, 9, 11,
7, 8, 8, 9, 8, 10, 7, 8, 6, 9, 10, 9, 9, 10, 8, 11, 9, 11, 9,
10, 9, 8, 9, 8, 8, 8, 8, 10, 9, 9, 10, 10, 8, 9, 8, 8, 8, 11,
9, 8, 8, 9, 9, 10, 8, 11, 10, 10, 8, 10, 9, 10, 8, 9, 9, 11, 9,
11, 9, 10, 10, 11, 10, 12, 9, 12, 10, 11, 10, 11, 9, 10, 10, 11, 10, 11,
10, 11, 10, 11, 10, 10, 10, 9, 9, 9, 8, 7, 6, 8, 11, 11, 9, 12, 10,
12, 9, 11, 11, 11, 10, 12, 11, 11, 10, 12, 10, 11, 10, 10, 10, 11, 10, 11,
11, 11, 9, 12, 10, 12, 11, 12, 10, 11, 10, 12, 11, 12, 11, 12, 11, 12, 10,
12, 11, 12, 11, 11, 10, 12, 10, 11, 10, 12, 10, 12, 10, 12, 10, 11, 11, 11,
10, 11, 11, 11, 10, 12, 11, 12, 10, 10, 11, 11, 9, 12, 11, 12, 10, 11, 10,
12, 10, 11, 10, 12, 10, 11, 10, 7, 5, 4, 6, 6, 7, 7, 7, 8, 8, 7,
7, 6, 8, 6, 7, 7, 9, 8, 9, 9, 10, 11, 11, 11, 12, 11, 10, 11, 12,
11, 12, 11, 12, 12, 12, 12, 11, 12, 12, 11, 12, 11, 12, 11, 13, 11, 12, 10,
13, 10, 14, 14, 13, 14, 15, 14, 16, 15, 15, 18, 18, 18, 9, 18, 8
};
// Second code lengths (2).
static const int kSecondCodeLengths_2[321] = {
5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 7, 8, 7, 7, 7, 8, 8,
8, 8, 9, 8, 9, 8, 9, 9, 9, 7, 9, 8, 8, 6, 9, 8, 9, 8, 9,
8, 9, 8, 9, 8, 9, 8, 9, 8, 8, 8, 8, 8, 9, 8, 9, 8, 9, 9,
10, 8, 10, 8, 9, 9, 8, 8, 8, 7, 8, 8, 9, 8, 9, 7, 9, 8, 10,
8, 9, 8, 9, 8, 9, 8, 8, 8, 9, 9, 9, 9, 10, 9, 11, 9, 10, 9,
10, 8, 8, 8, 9, 8, 8, 8, 9, 9, 8, 9, 10, 8, 9, 8, 8, 8, 11,
8, 7, 8, 9, 9, 9, 9, 10, 9, 10, 9, 10, 9, 8, 8, 9, 9, 10, 9,
10, 9, 10, 8, 10, 9, 10, 9, 11, 10, 11, 9, 11, 10, 10, 10, 11, 9, 11,
9, 10, 9, 11, 9, 11, 10, 10, 9, 10, 9, 9, 8, 10, 9, 11, 9, 9, 9,
11, 10, 11, 9, 11, 9, 11, 9, 11, 10, 11, 10, 11, 10, 11, 9, 10, 10, 11,
10, 10, 8, 10, 9, 10, 10, 11, 9, 11, 9, 10, 10, 11, 9, 10, 10, 9, 9,
10, 9, 10, 9, 10, 9, 10, 9, 11, 9, 11, 10, 10, 9, 10, 9, 11, 9, 11,
9, 11, 9, 10, 9, 11, 9, 11, 9, 11, 9, 10, 8, 11, 9, 10, 9, 10, 9,
10, 8, 10, 8, 9, 8, 9, 8, 7, 4, 4, 5, 6, 6, 6, 7, 7, 7, 7,
8, 8, 8, 7, 8, 8, 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, 10, 10, 12,
11, 11, 12, 12, 11, 12, 12, 11, 12, 12, 12, 12, 12, 12, 11, 12, 11, 13, 12,
13, 12, 13, 14, 14, 14, 15, 13, 14, 13, 14, 18, 18, 17, 7, 16, 9
};
// Second code lengths (3).
static const int kSecondCodeLengths_3[321] = {
5, 6, 6, 6, 6, 7, 7, 7, 6, 8, 7, 8, 7, 9, 8, 8, 7, 7, 8,
9, 9, 9, 9, 10, 8, 9, 9, 10, 8, 10, 9, 8, 6, 10, 8, 10, 8, 10,
9, 9, 9, 9, 9, 10, 9, 9, 8, 9, 8, 9, 8, 9, 9, 10, 9, 10, 9,
9, 8, 10, 9, 11, 10, 8, 8, 8, 8, 9, 7, 9, 9, 10, 8, 9, 8, 11,
9, 10, 9, 10, 8, 9, 9, 9, 9, 8, 9, 9, 10, 10, 10, 12, 10, 11, 10,
10, 8, 9, 9, 9, 8, 9, 8, 8, 10, 9, 10, 11, 8, 10, 9, 9, 8, 12,
8, 9, 9, 9, 9, 8, 9, 10, 9, 12, 10, 10, 10, 8, 7, 11, 10, 9, 10,
11, 9, 11, 7, 11, 10, 12, 10, 12, 10, 11, 9, 11, 9, 12, 10, 12, 10, 12,
10, 9, 11, 12, 10, 12, 10, 11, 9, 10, 9, 10, 9, 11, 11, 12, 9, 10, 8,
12, 11, 12, 9, 12, 10, 12, 10, 13, 10, 12, 10, 12, 10, 12, 10, 9, 10, 12,
10, 9, 8, 11, 10, 12, 10, 12, 10, 12, 10, 11, 10, 12, 8, 12, 10, 11, 10,
10, 10, 12, 9, 11, 10, 12, 10, 12, 11, 12, 10, 9, 10, 12, 9, 10, 10, 12,
10, 11, 10, 11, 10, 12, 8, 12, 9, 12, 8, 12, 8, 11, 10, 11, 10, 11, 9,
10, 8, 10, 9, 9, 8, 9, 8, 7, 4, 3, 5, 5, 6, 5, 6, 6, 7, 7,
8, 8, 8, 7, 7, 7, 9, 8, 9, 9, 11, 9, 11, 9, 8, 9, 9, 11, 12,
11, 12, 12, 13, 13, 12, 13, 14, 13, 14, 13, 14, 13, 13, 13, 12, 13, 13, 12,
13, 13, 14, 14, 13, 13, 14, 14, 14, 14, 15, 18, 17, 18, 8, 16, 10
};
// Second code lengths (4).
static const int kSecondCodeLengths_4[321] = {
4, 5, 6, 6, 6, 6, 7, 7, 6, 7, 7, 9, 6, 8, 8, 7, 7, 8, 8,
8, 6, 9, 8, 8, 7, 9, 8, 9, 8, 9, 8, 9, 6, 9, 8, 9, 8, 10,
9, 9, 8, 10, 8, 10, 8, 9, 8, 9, 8, 8, 7, 9, 9, 9, 9, 9, 8,
10, 9, 10, 9, 10, 9, 8, 7, 8, 9, 9, 8, 9, 9, 9, 7, 10, 9, 10,
9, 9, 8, 9, 8, 9, 8, 8, 8, 9, 9, 10, 9, 9, 8, 11, 9, 11, 10,
10, 8, 8, 10, 8, 8, 9, 9, 9, 10, 9, 10, 11, 9, 9, 9, 9, 8, 9,
8, 8, 8, 10, 10, 9, 9, 8, 10, 11, 10, 11, 11, 9, 8, 9, 10, 11, 9,
10, 11, 11, 9, 12, 10, 10, 10, 12, 11, 11, 9, 11, 11, 12, 9, 11, 9, 10,
10, 10, 10, 12, 9, 11, 10, 11, 9, 11, 11, 11, 10, 11, 11, 12, 9, 10, 10,
12, 11, 11, 10, 11, 9, 11, 10, 11, 10, 11, 9, 11, 11, 9, 8, 11, 10, 11,
11, 10, 7, 12, 11, 11, 11, 11, 11, 12, 10, 12, 11, 13, 11, 10, 12, 11, 10,
11, 10, 11, 10, 11, 11, 11, 10, 12, 11, 11, 10, 11, 10, 10, 10, 11, 10, 12,
11, 12, 10, 11, 9, 11, 10, 11, 10, 11, 10, 12, 9, 11, 11, 11, 9, 11, 10,
10, 9, 11, 10, 10, 9, 10, 9, 7, 4, 5, 5, 5, 6, 6, 7, 6, 8, 7,
8, 9, 9, 7, 8, 8, 10, 9, 10, 10, 12, 10, 11, 11, 11, 11, 10, 11, 12,
11, 11, 11, 11, 11, 13, 12, 11, 12, 13, 12, 12, 12, 13, 11, 9, 12, 13, 7,
13, 11, 13, 11, 10, 11, 13, 15, 15, 12, 14, 15, 15, 15, 6, 15, 5
};
// Second code lengths (5).
static const int kSecondCodeLengths_5[321] = {
8, 10, 11, 11, 11, 12, 11, 11, 12, 6, 11, 12, 10, 5, 12, 12, 12, 12, 12,
12, 12, 13, 13, 14, 13, 13, 12, 13, 12, 13, 12, 15, 4, 10, 7, 9, 11, 11,
10, 9, 6, 7, 8, 9, 6, 7, 6, 7, 8, 7, 7, 8, 8, 8, 8, 8, 8,
9, 8, 7, 10, 9, 10, 10, 11, 7, 8, 6, 7, 8, 8, 9, 8, 7, 10, 10,
8, 7, 8, 8, 7, 10, 7, 6, 7, 9, 9, 8, 11, 11, 11, 10, 11, 11, 11,
8, 11, 6, 7, 6, 6, 6, 6, 8, 7, 6, 10, 9, 6, 7, 6, 6, 7, 10,
6, 5, 6, 7, 7, 7, 10, 8, 11, 9, 13, 7, 14, 16, 12, 14, 14, 15, 15,
16, 16, 14, 15, 15, 15, 15, 15, 15, 15, 15, 14, 15, 13, 14, 14, 16, 15, 17,
14, 17, 15, 17, 12, 14, 13, 16, 12, 17, 13, 17, 14, 13, 13, 14, 14, 12, 13,
15, 15, 14, 15, 17, 14, 17, 15, 14, 15, 16, 12, 16, 15, 14, 15, 16, 15, 16,
17, 17, 15, 15, 17, 17, 13, 14, 15, 15, 13, 12, 16, 16, 17, 14, 15, 16, 15,
15, 13, 13, 15, 13, 16, 17, 15, 17, 17, 17, 16, 17, 14, 17, 14, 16, 15, 17,
15, 15, 14, 17, 15, 17, 15, 16, 15, 15, 16, 16, 14, 17, 17, 15, 15, 16, 15,
17, 15, 14, 16, 16, 16, 16, 16, 12, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7,
8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 11, 10, 11, 11, 11, 11, 11,
12, 12, 12, 13, 13, 12, 13, 12, 14, 14, 12, 13, 13, 13, 13, 14, 12, 13, 13,
14, 14, 14, 13, 14, 14, 15, 15, 13, 15, 13, 17, 17, 17, 9, 17, 7
};
// Second code lengths (global array).
static const int *kSecondCodeLengths[5] = {
kSecondCodeLengths_1, kSecondCodeLengths_2, kSecondCodeLengths_3,
kSecondCodeLengths_4, kSecondCodeLengths_5
};
// Offset code lengths (1).
static const int kOffsetCodeLengths_1[11] = {
5, 6, 3, 3, 3, 3, 3, 3, 3, 4, 6
};
// Offset code lengths (2).
static const int kOffsetCodeLengths_2[13] = {
5, 6, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 6
};
// Offset code lengths (3).
static const int kOffsetCodeLengths_3[14] = {
6, 7, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 5, 7
};
// Offset code lengths (4).
static const int kOffsetCodeLengths_4[11] = {
3, 6, 5, 4, 2, 3, 3, 3, 4, 4, 6
};
// Offset code lengths (5).
static const int kOffsetCodeLengths_5[11] = {
6, 7, 7, 6, 4, 3, 2, 2, 3, 3, 6
};
// Offset code lengths (global array).
static const int *kOffsetCodeLengths[5] = {
kOffsetCodeLengths_1, kOffsetCodeLengths_2, kOffsetCodeLengths_3,
kOffsetCodeLengths_4, kOffsetCodeLengths_5
};
// Offset code size.
static const int kOffsetCodeSize[5] = { 11, 13, 14, 11, 11 };
// Meta codes.
static const int kMetaCodes[37] = {
0x5D8, 0x058, 0x040, 0x0C0, 0x000, 0x078, 0x02B, 0x014, 0x00C, 0x01C, 0x01B,
0x00B, 0x010, 0x020, 0x038, 0x018, 0x0D8, 0xBD8, 0x180, 0x680, 0x380, 0xF80,
0x780, 0x480, 0x080, 0x280, 0x3D8, 0xFD8, 0x7D8, 0x9D8, 0x1D8, 0x004, 0x001,
0x002, 0x007, 0x003, 0x008
};
// Meta code lengths.
static const int kMetaCodeLengths[37] = {
11, 8, 8, 8, 8, 7, 6, 5, 5, 5, 5, 6, 5, 6, 7, 7, 9, 12, 10,
11, 11, 12, 12, 11, 11, 11, 12, 12, 12, 12, 12, 5, 2, 2, 3, 4, 5
};
// LZSS decoder constants.
constexpr int kLzssMatch = -1;
constexpr int kLzssEnd = -2;
// Intialize LZSS.
void Algorithm13Method::InitializeLZSS()
{
int val = *(input.data++);
int code = (val >> 4);
if (code == 0) {
HuffmanDecoder metacode;
metacode.Initialize();
for (int i = 0; i < 37; i++)
metacode.AddValueLF(i, kMetaCodes[i], kMetaCodeLengths[i]);
metacode.MakeTable(true);
ParseHuffmanCode(firstcode, 321, metacode);
if (val & 0x08) secondcode = firstcode;
else ParseHuffmanCode(secondcode, 321, metacode);
ParseHuffmanCode(offsetcode, (val & 0x07) + 10, metacode);
}
else if (code < 6) {
firstcode.Initialize(kFirstCodeLengths[code-1], 321, 32, true);
secondcode.Initialize(kSecondCodeLengths[code-1], 321, 32, true);
offsetcode.Initialize(kOffsetCodeLengths[code-1], kOffsetCodeSize[code-1], 32, true);
}
else {
throw ExtractException("Algo13: invalid compressed data [code]");
}
currcode = &firstcode;
firstcode.MakeTable(true);
secondcode.MakeTable(true);
offsetcode.MakeTable(true);
}
// Parse code of size.
void Algorithm13Method::ParseHuffmanCode(HuffmanDecoder &decoder, int numcodes,
HuffmanDecoder &metacode)
{
int length = 0;
int lengths[numcodes];
for (int i = 0; i < numcodes; i++) {
int val = metacode.NextSymbol(input);
switch(val) {
case 31: length = -1; break;
case 32: length++; break;
case 33: length--; break;
case 34:
if (input.ReadBit()) lengths[i++] = length;
break;
case 35:
val = input.ReadWord(3) + 2;
while (val--) lengths[i++] = length;
break;
case 36:
val = input.ReadWord(6) + 10;
while (val--) lengths[i++] = length;
break;
default: length = val+1; break;
}
lengths[i] = length;
}
decoder.Initialize(lengths, numcodes, 32, true);
}
// Get the next litteral or offset.
int Algorithm13Method::NextLiteralOrOffset(int &offset, int &length)
{
if (input.HasEnded())
throw ExtractException("Algo13: all data read but algo has not finished");
int val = currcode->NextSymbol(input);
if (val < 0x100) {
currcode = &firstcode;
return val;
}
else {
currcode = &secondcode;
if (val < 0x13E) length = val - 0x100 + 3;
else if (val == 0x13E) length = input.ReadWord(10) + 65;
else if (val == 0x13F) length = input.ReadWord(15) + 65;
else return kLzssEnd;
int bit_length = offsetcode.NextSymbol(input);
if (bit_length == 0) offset = 1;
else if (bit_length == 1) offset = 2;
else offset = (1 << (bit_length-1)) + input.ReadWord(bit_length-1) + 1;
return kLzssMatch;
}
}
// Constants.
constexpr int kWindowSize = 65536;
constexpr int kWindowMask = kWindowSize - 1;
// Initialize the algorithm.
void Algorithm13Method::Initialize()
{
ended = false;
input.Load(data, end - data);
window_buffer = std::make_unique<uint8_t[]>(kWindowSize);
match_length = 0; match_offset = 0;
memset(window_buffer.get(), 0, kWindowSize);
InitializeLZSS();
}
// Read the next byte.
int32_t Algorithm13Method::ReadNextByte(uint32_t pos)
{
if (!match_length) {
int offset, length;
int val = NextLiteralOrOffset(offset, length);
if (val >= 0) {
window_buffer[pos & kWindowMask] = val;
return val;
} else if (val == kLzssEnd) {
ended = true;
return -1;
} else {
match_length = length;
match_offset = pos - offset;
}
}
match_length--;
uint8_t byte = window_buffer[match_offset++ & kWindowMask];
window_buffer[pos & kWindowMask] = byte;
return byte;
}
// Read the next bytes.
int32_t Algorithm13Method::ReadBytes(uint8_t *buffer, uint32_t length)
{
if (ended) return -1;
uint8_t *start = buffer;
uint8_t *end_capacity = buffer + length;
int32_t byte;
while (buffer != end_capacity && (byte = ReadNextByte(total_size + (buffer - start))) != -1)
*(buffer++) = byte;
return buffer - start;
}
} // namespace stuffit
} // namespace maconv

View File

@ -0,0 +1,71 @@
/*
Stuffit algorithm 13: LZSS and Huffman.
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "stuffit/methods.h"
#include "stuffit/utils/huffman.h"
#include <memory>
namespace maconv {
namespace stuffit {
// Algorithm 13 compression algorithm.
struct Algorithm13Method : CompressionMethod {
// Intialize LZSS.
void InitializeLZSS();
// Parse Huffman code given in data.
void ParseHuffmanCode(HuffmanDecoder &decoder, int numcodes,
HuffmanDecoder &metacode);
// Get the next litteral or offset.
int NextLiteralOrOffset(int &offset, int &length);
// Initialize the algorithm.
void Initialize() override;
// Read the next byte.
int32_t ReadNextByte(uint32_t pos);
// Read the next bytes.
int32_t ReadBytes(uint8_t *data, uint32_t length) override;
bool ended;
utils::BitReaderLE input;
std::unique_ptr<uint8_t[]> window_buffer;
int match_length, match_offset;
HuffmanDecoder firstcode, secondcode, offsetcode;
HuffmanDecoder *currcode;
};
} // namespace stuffit
} // namespace maconv

View File

@ -0,0 +1,336 @@
/*
Arsenic algorithm: BWT and arithmetic coding.
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stuffit/methods/arsenic.h"
#include "commands.h"
#include <make_unique.hpp>
#include <string.h>
namespace maconv {
namespace stuffit {
// The randomization table.
static const uint16_t kRandomizationTable[] = {
0xEE, 0x56, 0xF8, 0xC3, 0x9D, 0x9F, 0xAE, 0x2C,
0xAD, 0xCD, 0x24, 0x9D, 0xA6, 0x101, 0x18, 0xB9,
0xA1, 0x82, 0x75, 0xE9, 0x9F, 0x55, 0x66, 0x6A,
0x86, 0x71, 0xDC, 0x84, 0x56, 0x96, 0x56, 0xA1,
0x84, 0x78, 0xB7, 0x32, 0x6A, 0x3, 0xE3, 0x2,
0x11, 0x101, 0x8, 0x44, 0x83, 0x100, 0x43, 0xE3,
0x1C, 0xF0, 0x86, 0x6A, 0x6B, 0xF, 0x3, 0x2D,
0x86, 0x17, 0x7B, 0x10, 0xF6, 0x80, 0x78, 0x7A,
0xA1, 0xE1, 0xEF, 0x8C, 0xF6, 0x87, 0x4B, 0xA7,
0xE2, 0x77, 0xFA, 0xB8, 0x81, 0xEE, 0x77, 0xC0,
0x9D, 0x29, 0x20, 0x27, 0x71, 0x12, 0xE0, 0x6B,
0xD1, 0x7C, 0xA, 0x89, 0x7D, 0x87, 0xC4, 0x101,
0xC1, 0x31, 0xAF, 0x38, 0x3, 0x68, 0x1B, 0x76,
0x79, 0x3F, 0xDB, 0xC7, 0x1B, 0x36, 0x7B, 0xE2,
0x63, 0x81, 0xEE, 0xC, 0x63, 0x8B, 0x78, 0x38,
0x97, 0x9B, 0xD7, 0x8F, 0xDD, 0xF2, 0xA3, 0x77,
0x8C, 0xC3, 0x39, 0x20, 0xB3, 0x12, 0x11, 0xE,
0x17, 0x42, 0x80, 0x2C, 0xC4, 0x92, 0x59, 0xC8,
0xDB, 0x40, 0x76, 0x64, 0xB4, 0x55, 0x1A, 0x9E,
0xFE, 0x5F, 0x6, 0x3C, 0x41, 0xEF, 0xD4, 0xAA,
0x98, 0x29, 0xCD, 0x1F, 0x2, 0xA8, 0x87, 0xD2,
0xA0, 0x93, 0x98, 0xEF, 0xC, 0x43, 0xED, 0x9D,
0xC2, 0xEB, 0x81, 0xE9, 0x64, 0x23, 0x68, 0x1E,
0x25, 0x57, 0xDE, 0x9A, 0xCF, 0x7F, 0xE5, 0xBA,
0x41, 0xEA, 0xEA, 0x36, 0x1A, 0x28, 0x79, 0x20,
0x5E, 0x18, 0x4E, 0x7C, 0x8E, 0x58, 0x7A, 0xEF,
0x91, 0x2, 0x93, 0xBB, 0x56, 0xA1, 0x49, 0x1B,
0x79, 0x92, 0xF3, 0x58, 0x4F, 0x52, 0x9C, 0x2,
0x77, 0xAF, 0x2A, 0x8F, 0x49, 0xD0, 0x99, 0x4D,
0x98, 0x101, 0x60, 0x93, 0x100, 0x75, 0x31, 0xCE,
0x49, 0x20, 0x56, 0x57, 0xE2, 0xF5, 0x26, 0x2B,
0x8A, 0xBF, 0xDE, 0xD0, 0x83, 0x34, 0xF4, 0x17
};
// Initialize the model with some values.
void ArithmeticModel::Initialize(int first_symbol, int last_symbol, int increment,
int frequency_limit)
{
this->increment = increment;
this->freq_limit = frequency_limit;
this->num_symbols = last_symbol - first_symbol + 1;
ResetModel();
for (int i = 0; i < num_symbols; i++)
symbols[i].symbol = i + first_symbol;
}
// Reset the model.
void ArithmeticModel::ResetModel()
{
total_freq = increment * num_symbols;
for (int i = 0; i < num_symbols; i++)
symbols[i].freq = increment;
}
// Increase the model frequency at |symindex| by |increment|.
void ArithmeticModel::IncreaseFrequency(int symindex)
{
symbols[symindex].freq += increment;
total_freq += increment;
if (total_freq <= freq_limit)
return;
total_freq = 0;
for (int i = 0; i < num_symbols; i++) {
symbols[i].freq++;
symbols[i].freq >>= 1;
total_freq += symbols[i].freq;
}
}
// Decoder constants.
constexpr int kDecoderNumBits = 26;
constexpr int kDecoderOne = (1 << (kDecoderNumBits - 1));
constexpr int kDecoderHalf = (1 << (kDecoderNumBits - 2));
// Initialize the decoder with some values.
void ArithmeticDecoder::Initialize(uint8_t *data, uint32_t length)
{
input.Load(data, length);
range = kDecoderOne;
code = input.ReadLongWord(kDecoderNumBits);
}
// Get the next arithmetic code.
void ArithmeticDecoder::NextCode(int symlow, int symsize, int symtot)
{
int renormf = range / symtot;
int lowincr = renormf * symlow;
code -= lowincr;
range = (symlow + symsize == symtot) ? (range - lowincr) : (symsize * renormf);
for (; range <= kDecoderHalf; range <<= 1)
code = (code << 1) | input.ReadBit();
}
// Get the next arithmetic symbol.
int ArithmeticDecoder::NextSymbol(ArithmeticModel *model)
{
int freq = code / (range / model->total_freq);
int cumulative = 0, n = 0;
for (; n < model->num_symbols - 1; n++) {
if (cumulative + model->symbols[n].freq > freq) break;
cumulative += model->symbols[n].freq;
}
NextCode(cumulative, model->symbols[n].freq, model->total_freq);
model->IncreaseFrequency(n);
return model->symbols[n].symbol;
}
// Get the next word (that has |n| bits).
int ArithmeticDecoder::NextWord(ArithmeticModel *model, int n)
{
int word = 0;
for (int i = 0; i < n; i++) {
if (NextSymbol(model))
word |= (1 << i);
}
return word;
}
// Initialize the algorithm.
void ArsenicMethod::Initialize()
{
decoder.Initialize(data, end - data);
initial_model.Initialize(0, 1, 1, 256);
selector_model.Initialize(0, 10, 8, 1024);
mtf_model[0].Initialize(2, 3, 8, 1024);
mtf_model[1].Initialize(4, 7, 4, 1024);
mtf_model[2].Initialize(8, 15, 4, 1024);
mtf_model[3].Initialize(16, 31, 4, 1024);
mtf_model[4].Initialize(32, 63, 2, 1024);
mtf_model[5].Initialize(64, 127, 2, 1024);
mtf_model[6].Initialize(128, 255, 1, 1024);
if (decoder.NextWord(&initial_model, 8) != 'A')
throw ExtractException("Arsenic: invalid compressed data [A]");
if (decoder.NextWord(&initial_model, 8) != 's')
throw ExtractException("Arsenic: invalid compressed data [s]");
block_bits = decoder.NextWord(&initial_model, 4) + 9;
block_size = (1 << block_bits);
num_bytes = 0; byte_count = 0; repeat = 0;
crc = 0xFFFFFFFF; compcrc = 0;
block = std::make_unique<uint8_t[]>(block_size);
end_of_blocks = decoder.NextSymbol(&initial_model); // Check first end marker.
}
// Read the next block.
void ArsenicMethod::ReadNextBlock()
{
mtf.ResetDecoder();
randomized = decoder.NextSymbol(&initial_model);
transform_index = decoder.NextWord(&initial_model, block_bits);
num_bytes = 0;
while (true) {
int sel = decoder.NextSymbol(&selector_model);
if (sel == 0 || sel == 1) { // Zero counting.
int zero_state = 1, zero_count = 0;
while (sel < 2) {
if (sel == 0) zero_count += zero_state;
else if (sel == 1) zero_count += (2 * zero_state);
zero_state *= 2;
sel = decoder.NextSymbol(&selector_model);
}
if (num_bytes + zero_count > block_size)
throw ExtractException("Arsenic: invalid block [zero count]");
memset(&block[num_bytes], mtf.Decode(0), zero_count);
num_bytes += zero_count;
}
int symbol;
if (sel == 10) break;
else if (sel == 2) symbol = 1;
else symbol = decoder.NextSymbol(&mtf_model[sel - 3]);
if (num_bytes >= block_size)
throw ExtractException("Arsenic: invalid block [num of bytes]");
block[num_bytes++] = mtf.Decode(symbol);
}
if (transform_index >= num_bytes)
throw ExtractException("Arsenic: invalid block [transform index]");
selector_model.ResetModel();
for (int i = 0; i < 7;i++)
mtf_model[i].ResetModel();
if (decoder.NextSymbol(&initial_model)) { // End marker.
compcrc = decoder.NextWord(&initial_model, 32);
end_of_blocks = true;
}
transform = std::make_unique<uint32_t[]>(num_bytes);
CalculateInverseBWT(transform.get(), block.get(), num_bytes);
}
// Read the next byte.
int32_t ArsenicMethod::ReadNextByte()
{
int byte, out_byte;
if (repeat) {
out_byte = last; repeat--;
goto end;
}
retry:
if (byte_count >= num_bytes) {
if (end_of_blocks) return -1;
ReadNextBlock();
byte_count = 0; count = 0; last = 0;
rand_index = 0; rand_count = kRandomizationTable[0];
}
transform_index = transform[transform_index];
byte = block[transform_index];
if (randomized && rand_count == byte_count) {
byte ^= 1;
rand_index = (rand_index + 1) & 0xFF;
rand_count += kRandomizationTable[rand_index];
}
byte_count++;
if (count == 4) {
count = 0;
if (byte == 0) goto retry;
repeat = byte - 1;
out_byte = last;
}
else {
if (byte == last) count++;
else { count = 1; last = byte; }
out_byte = byte;
}
end:
crc = CalcCRC(crc, out_byte, CRCTable_edb88320);
return out_byte;
}
// Read the next bytes.
int32_t ArsenicMethod::ReadBytes(uint8_t *buffer, uint32_t length)
{
if (end_of_blocks) {
// if (compcrc != ~crc) // FIX ME
// throw ExtractException("Arsenic: invalid CRC after uncompressing");
return -1;
}
uint8_t *start = buffer;
uint8_t *end_capacity = buffer + length;
int32_t byte;
while (buffer != end_capacity && (byte = ReadNextByte()) != -1)
*(buffer++) = byte;
return buffer - start;
}
} // namespace stuffit
} // namespace maconv

View File

@ -0,0 +1,122 @@
/*
Arsenic algorithm: BWT and arithmetic coding.
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "stuffit/methods.h"
#include "stuffit/utils/bwt.h"
#include "stuffit/utils/crc.h"
#include "utils/bit_reader.h"
#include <memory>
namespace maconv {
namespace stuffit {
// Store an arithmetic symbol.
struct ArithmeticSymbol {
int symbol;
int freq;
};
// The arithmetic model.
struct ArithmeticModel {
// Initialize the model with some values.
void Initialize(int firstsymb, int lastsym, int incr, int freqlimit);
// Reset the model.
void ResetModel();
// Increase the model frequency at |symindex| by |increment|.
void IncreaseFrequency(int symindex);
int total_freq;
int increment;
int freq_limit;
int num_symbols;
ArithmeticSymbol symbols[128];
};
// The arithmetic decoder.
struct ArithmeticDecoder {
// Initialize the decoder with some values.
void Initialize(uint8_t *data, uint32_t length);
// Get the next arithmetic code.
void NextCode(int symlow, int symsize, int symtot);
// Get the next arithmetic symbol.
int NextSymbol(ArithmeticModel *model);
// Get the next word (that has |n| bits).
int NextWord(ArithmeticModel *model, int n);
utils::BitReaderBE input;
int range, code;
};
// Arsenic compression algorithm.
struct ArsenicMethod : CompressionMethod {
// Initialize the algorithm.
void Initialize() override;
// Read the next block.
void ReadNextBlock();
// Read the next byte.
int32_t ReadNextByte();
// Read the next bytes.
int32_t ReadBytes(uint8_t *data, uint32_t length) override;
ArithmeticModel initial_model, selector_model, mtf_model[7];
ArithmeticDecoder decoder;
MtfDecoder mtf;
std::unique_ptr<uint8_t[]> block;
int block_bits, block_size;
bool end_of_blocks;
int num_bytes, byte_count, transform_index;
std::unique_ptr<uint32_t[]> transform;
int randomized, rand_count, rand_index;
int repeat, count, last;
uint32_t crc, compcrc;
};
} // namespace stuffit
} // namespace maconv

View File

@ -0,0 +1,174 @@
/*
Compress Algorithm (LZW, block mode).
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stuffit/methods/compress.h"
#include <make_unique.hpp>
namespace maconv {
namespace stuffit {
// Initialize the decoder.
void CompressLzw::Initialize(int max_symbols, int reserved_symbols)
{
this->max_symbols = max_symbols;
this->reserved_symbols = reserved_symbols;
nodes = std::make_unique<CompressTreeNode[]>(max_symbols);
for (int i = 0; i < 256; i++) {
nodes[i].chr = i;
nodes[i].parent = -1;
}
ClearTable();
}
// Clear the decoder table.
void CompressLzw::ClearTable()
{
num_symbols = 256 + reserved_symbols;
prev_symbol = -1;
symbol_size = 9;
}
// The the first bytes corresponding to symbol in the LZW tree.
uint8_t CompressLzw::FindFirstByte(int symbol)
{
while (nodes[symbol].parent >= 0)
symbol = nodes[symbol].parent;
return nodes[symbol].chr;
}
// Get the next symbol.
void CompressLzw::NextSymbol(int symbol)
{
if (symbol > num_symbols || (prev_symbol < 0 && symbol == num_symbols))
throw ExtractException("Compress: invalid code");
int parent = prev_symbol;
prev_symbol = symbol;
if (parent < 0) return;
int postfix_byte = FindFirstByte(symbol == num_symbols ? parent : symbol);
if (num_symbols == max_symbols) // Too many symbols.
return;
nodes[num_symbols].parent = parent;
nodes[num_symbols].chr = postfix_byte;
num_symbols++;
if (num_symbols != max_symbols && ((num_symbols & (num_symbols-1)) == 0))
symbol_size++;
}
// Calculate the number of bytes needed to write the output.
int CompressLzw::CalcOutputLength()
{
int n = 0;
for (int symbol = prev_symbol; symbol >= 0; n++)
symbol = nodes[symbol].parent;
return n;
}
// Write the ouput data to a buffer.
void CompressLzw::OutputToBuffer(int len, uint8_t *buffer)
{
int symbol = prev_symbol;
buffer += len;
while (symbol >= 0) {
*(--buffer) = nodes[symbol].chr;
symbol = nodes[symbol].parent;
}
}
// Initialize the algorithm.
void CompressMethod::Initialize()
{
block_mode = (flags & 0x80) != 0;
lzw.Initialize(1 << (flags & 0x1F), block_mode ? 1 : 0);
input.Load(data, end - data);
output_len = -1;
symbol_counter = 0;
}
// Load the next block of data.
bool CompressMethod::LoadNextBlock()
{
int symbol;
while (true) {
if (input.HasEnded(lzw.symbol_size - 1)) return false;
symbol = input.ReadWord(lzw.symbol_size);
symbol_counter++;
if (symbol != 256 || !block_mode) break;
if (symbol_counter % 8)
input.IgnoreBits(lzw.symbol_size * (8 - symbol_counter % 8));
lzw.ClearTable();
symbol_counter = 0;
}
lzw.NextSymbol(symbol);
output_len = lzw.CalcOutputLength();
return true;
}
// Read the next bytes.
int32_t CompressMethod::ReadBytes(uint8_t *data, uint32_t length)
{
if (output_len == -1 && !LoadNextBlock())
return -1;
if (output_len > length)
return 0;
int32_t len = output_len;
lzw.OutputToBuffer(output_len, data);
output_len = -1;
return len;
}
} // namespace stuffit
} // namespace maconv

View File

@ -0,0 +1,102 @@
/*
Compress Algorithm (LZW, block mode).
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "stuffit/methods.h"
#include "utils/bit_reader.h"
#include <memory>
namespace maconv {
namespace stuffit {
struct CompressTreeNode {
uint8_t chr;
int parent;
};
// LZW (Lempel-Ziv-Welch) decoder.
struct CompressLzw {
// Initialize the decoder.
void Initialize(int max_symbols,int reserved_symbols);
// Clear the decoder table.
void ClearTable();
// The the first bytes corresponding to symbol in the LZW tree.
uint8_t FindFirstByte(int symbol);
// Read the next symbol.
void NextSymbol(int symbol);
// Calculate the number of bytes needed to write the output.
int CalcOutputLength();
// Write the ouput data to a buffer.
void OutputToBuffer(int len, uint8_t *buffer);
int num_symbols, max_symbols, reserved_symbols;
int prev_symbol;
int symbol_size;
std::unique_ptr<CompressTreeNode[]> nodes;
};
// Compress algorithm.
struct CompressMethod : CompressionMethod {
CompressMethod(int flags_ = 0x8E) : flags{flags_} {}
// Initialize the algorithm.
void Initialize() override;
// Load the next block of data.
bool LoadNextBlock();
// Read the next bytes.
int32_t ReadBytes(uint8_t *data, uint32_t length) override;
int flags;
bool block_mode;
int symbol_counter;
int output_len;
utils::BitReaderLE input;
CompressLzw lzw;
};
} // namespace stuffit
} // namespace maconv

View File

@ -0,0 +1,78 @@
/*
RLE 90 compression algorithm.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stuffit/methods/rle90.h"
namespace maconv {
namespace stuffit {
// Initialize the algorithm.
void Rle90Method::Initialize()
{
count = 0;
byte = 0;
}
// Read the next byte.
uint8_t Rle90Method::ReadNextByte()
{
// We have a byte to repeat.
if (count != 0)
return (count--, byte);
// Read the next byte from the stream.
uint8_t next = *(data++);
if (next != 0x90)
return (byte = next, byte);
// The previous byte need to be repeated (if not 0x0).
next = *(data++);
if (next == 0x0)
return (byte = next, byte);
count = next - 2;
return next;
}
// Read the next bytes.
int32_t Rle90Method::ReadBytes(uint8_t *buffer, uint32_t length)
{
if (data == end && count == 0)
return -1;
uint8_t *start = buffer;
uint8_t *end_capacity = buffer + length;
while ((data != end || count != 0) && buffer != end_capacity)
*(buffer++) = ReadNextByte();
return buffer - start;
}
} // namespace stuffit
} // namespace maconv

View File

@ -0,0 +1,48 @@
/*
RLE 90 compression algorithm.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "stuffit/methods.h"
namespace maconv {
namespace stuffit {
// RLE 90 compression algorithm.
struct Rle90Method : CompressionMethod {
// Initialize the algorithm.
void Initialize() override;
// Read the next byte.
uint8_t ReadNextByte();
// Read the next bytes.
int32_t ReadBytes(uint8_t *data, uint32_t length) override;
uint32_t count; // Number of bytes to repeat.
uint8_t byte; // Byte to repeat.
};
} // namespace stuffit
} // namespace maconv

137
src/stuffit/stuffit.cc Normal file
View File

@ -0,0 +1,137 @@
/*
Extract Stuffit archives.
See docs/stuffit/Stuffit.md for more information on this format.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stuffit/stuffit.h"
#include "stuffit/methods.h"
#include "formats/formats.h"
#include "commands.h"
#include <path.hpp>
#include <cstdarg>
namespace maconv {
namespace stuffit {
// Warn the user that a fork couldn't be extracted.
static void WarnForkError(StuffitEntry &ent, bool is_res, const char *msg, ...)
{
va_list args;
fprintf(stderr, "\033[1m\033[33mWARNING: ");
fprintf(stderr, is_res ? "ressource fork " : "data fork ");
fprintf(stderr, "of '%s' couldn't be extracted (", ent.name.c_str());
va_start(args, msg);
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, ")\033[0m\n");
}
// Extract a single fork.
static void ExtractFork(StuffitEntry &ent, bool is_res, fs::File &file,
uint8_t *data)
{
const StuffitCompInfo &info = is_res ? ent.res : ent.data;
// Select the compression handler and extract the fork.
auto ptr = GetCompressionMethod(info.method);
if (!ptr)
return (void)WarnForkError(ent, is_res, "compression method %u not supported", info.method);
// Try extracting the fork.
try {
ptr->Extract(info, data, file.mem_pool);
} catch (ExtractException &e) {
return (void)WarnForkError(ent, is_res, e.what());
}
// Fill file information.
if (is_res) {
file.res = ptr->uncompressed;
file.res_size = ptr->total_size;
} else {
file.data_size = ptr->total_size;
file.data = ptr->uncompressed;
}
}
// Extract a file.
static void ExtractFile(fs::FileReader &reader, StuffitEntry &ent,
const std::string &dest_folder)
{
// Copy extracted data to file object.
fs::File file;
file.Reset();
file.type = ent.type;
file.creator = ent.type;
file.flags = ent.type;
file.creation_date = ent.type;
file.modif_date = ent.type;
file.filename = ent.name;
// Log information to user.
std::string filename = dest_folder + "/" + GetFilenameFor(ent.name, prefered_conv);
LogDebug("Extracting %s ...", filename.c_str());
// Uncompress forks (if not empty).
if (ent.data.comp_size > 0)
ExtractFork(ent, false, file, reader.data);
if (ent.res.comp_size > 0)
ExtractFork(ent, true, file, reader.data);
// Save the file.
PackLocalFile(file, filename, prefered_conv);
}
// Extract a directory.
static void ExtractDirectory(StuffitEntry &ent, const std::string &dest_folder)
{
std::string dirname = dest_folder + "/" + ent.name;
Path::makedirs(dirname);
// TODO: set mofitication date.
}
// Extract a Stuffit entry.
void ExtractStuffitEntry(fs::FileReader &reader, StuffitEntry &ent,
const std::string &dest_folder)
{
if (ent.etype == StuffitEntryType::File)
ExtractFile(reader, ent, dest_folder);
else if (ent.etype == StuffitEntryType::Folder)
ExtractDirectory(ent, dest_folder);
}
} // namespace stuffit
} // namespace maconv

88
src/stuffit/stuffit.h Normal file
View File

@ -0,0 +1,88 @@
/*
Extract Stuffit archives.
See docs/stuffit/Stuffit.md for more information on this format.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs/file.h"
#include "fs/file_reader.h"
#include "fs/file_writer.h"
#include <string>
namespace maconv {
namespace stuffit {
// Stuffit entry type.
enum class StuffitEntryType : uint8_t {
Folder, File, EndFolder, Unknown
};
// Information about a compressed fork.
struct StuffitCompInfo {
uint8_t method; // The compression method.
uint32_t offset; // Compressed data offset in the archive.
uint32_t size; // Fork size (uncompressed).
uint32_t comp_size; // Fork size (compressed).
};
// An entry in a Stuffit archive.
struct StuffitEntry {
StuffitEntryType etype; // Entry type (folder, file, end folder).
std::string name; // Name of the file/directory.
uint32_t entity_off; // Entity offset.
uint32_t parent_off; // Parent offset.
uint16_t num_files; // Number of files (folder only).
time_t creation_date; // Creation date of the file (Unix time).
time_t modif_date; // Modification date of the file (Unix file).
uint32_t type; // File type (4 chars).
uint32_t creator; // File creator (4 chars).
uint32_t flags; // Finder flags.
StuffitCompInfo data; // Info about data fork.
StuffitCompInfo res; // Info about res fork.
};
// Stuffit (v1) functions.
bool IsFileStuffit1(fs::FileReader &reader);
void ExtractStuffit1(fs::FileReader &reader, const std::string &output);
// Stuffit (v5) functions.
bool IsFileStuffit5(fs::FileReader &reader);
void ExtractStuffit5(fs::FileReader &reader, const std::string &output);
// Extract a Stuffit entry.
void ExtractStuffitEntry(fs::FileReader &reader, StuffitEntry &ent,
const std::string &dest_folder);
} // namespace stuffit
} // namespace maconv

152
src/stuffit/stuffit_v1.cc Normal file
View File

@ -0,0 +1,152 @@
/*
Extract files from Stuffit (v1) archives.
See docs/stuffit/Stuffit_v1.md for more information on this format.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stuffit/stuffit.h"
#include <limits>
namespace maconv {
namespace stuffit {
// Constants.
constexpr uint8_t kTypeFolder = 32;
constexpr uint8_t kTypeEndFolder = 33;
// Return true if a file is in Stuffit (v1) format.
bool IsFileStuffit1(fs::FileReader &reader)
{
reader.Seek(0);
IS_COND(reader.file_size >= 22);
IS_COND(reader.ReadByte() == 'S');
uint8_t byte = reader.ReadByte();
IS_COND(byte == 'T' || byte == 'I');
reader.Skip(8);
IS_COND(reader.ReadWordBE() == 0x724c6175);
return true;
}
// Read Stuffit (v1) header.
static uint32_t ReadHeader(fs::FileReader &reader)
{
reader.Seek(0);
reader.Skip(6); // Skip magic number and number of files.
uint32_t total_size = reader.ReadWordBE();
reader.Skip(12); // Skip other bytes from header.
return total_size;
}
// Read Stuffit (v1) file header.
static void ReadFileHeader(fs::FileReader &reader, StuffitEntry &ent)
{
// Read compression methods.
ent.res.method = reader.ReadByte();
ent.data.method = reader.ReadByte();
// Guess file type.
if (ent.data.method == kTypeFolder || ent.res.method == kTypeFolder)
ent.etype = StuffitEntryType::Folder;
else if (ent.data.method == kTypeEndFolder || ent.res.method == kTypeEndFolder)
ent.etype = StuffitEntryType::EndFolder;
else
ent.etype = StuffitEntryType::File;
// If the entry type is "EndFolder": don't parse the rest.
if (ent.etype == StuffitEntryType::EndFolder)
return (void)reader.Skip(110);
// Read file name.
uint8_t filelength = reader.ReadByte();
ent.name = reader.ReadString(filelength);
reader.Skip(63 - filelength);
// Read file type, creator and Finder flags.
ent.type = reader.ReadWordBE();
ent.creator = reader.ReadWordBE();
ent.flags = reader.ReadHalfLE();
// Read creation and modification dates.
ent.creation_date = reader.ReadMacDate();
ent.modif_date = reader.ReadMacDate();
// If the entry type is "Folder": don't parse the rest.
if (ent.etype == StuffitEntryType::Folder)
return (void)reader.Skip(28);
// Read data and res sizes and skip CRC-16.
ent.res.size = reader.ReadWordBE();
ent.data.size = reader.ReadWordBE();
ent.res.comp_size = reader.ReadWordBE();
ent.data.comp_size = reader.ReadWordBE();
reader.Skip(12);
// Set ressource and data offsets.
ent.res.offset = reader.Tell();
ent.data.offset = ent.res.offset + ent.res.comp_size;
// Move to next entry.
reader.Seek(ent.data.offset + ent.data.comp_size);
}
// Extract a Stuffit (v1) directory.
static void ExtractDirectory(fs::FileReader &reader, const std::string &dest_dir,
uint32_t total_size)
{
StuffitEntry ent;
while (reader.Tell() < total_size) {
ReadFileHeader(reader, ent);
ExtractStuffitEntry(reader, ent, dest_dir);
if (ent.etype == StuffitEntryType::EndFolder)
break;
if (ent.etype == StuffitEntryType::Folder)
ExtractDirectory(reader, dest_dir + "/" + ent.name, total_size);
}
}
// Extract a Stuffit (v1) archive.
void ExtractStuffit1(fs::FileReader &reader, const std::string &output)
{
uint32_t total_size = ReadHeader(reader);
ExtractDirectory(reader, output, total_size);
}
} // namespace maconv
} // namespace stuffit

203
src/stuffit/stuffit_v5.cc Normal file
View File

@ -0,0 +1,203 @@
/*
Extract files from Stuffit (v5) archives.
See docs/stuffit/Stuffit_v5.md for more information on this format.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stuffit/stuffit.h"
#include "commands.h"
#include <unordered_map>
namespace maconv {
namespace stuffit {
// Stuffit (v5) entity flags.
constexpr uint8_t kFlagDirectory = 0x40;
constexpr uint8_t kFlagHasRessource = 0x1;
// Return true if a file is in Stuffit (v5) format.
bool IsFileStuffit5(fs::FileReader &reader)
{
reader.Seek(0);
IS_COND(reader.file_size >= 100);
auto first = reader.ReadString(16);
IS_COND(first == "StuffIt (c)1997-");
reader.Skip(4);
auto sec = reader.ReadString(60);
IS_COND(sec == " Aladdin Systems, Inc., http://www.aladdinsys.com/StuffIt/\x0d\x0a");
return true;
}
// Read Stuffit (v5) header.
static uint16_t ReadHeader(fs::FileReader &reader)
{
reader.Seek(0);
reader.Skip(83); // Skip magic string.
// Make sure that the archive is not encrypted.
if (reader.ReadByte() & 0x80)
StopOnError("Stuffit archive is encrypted");
// Read entry offset and number of files.
reader.Skip(8);
uint16_t num_files = reader.ReadHalfBE();
uint32_t offset = reader.ReadWordBE();
reader.Seek(offset);
return num_files;
}
// Read Stuffit (v5) file header.
static void ReadFileHeader(fs::FileReader &reader, StuffitEntry &ent)
{
reader.Skip(4); // Skip magic number.
// Read version and header size.
uint8_t version = reader.ReadByte();
reader.Skip(1);
uint16_t header_size = reader.ReadHalfBE();
reader.Skip(1);
// Read flags for knowing if entry is a file or a folder.
ent.etype = (reader.ReadByte() & kFlagDirectory) ? StuffitEntryType::Folder
: StuffitEntryType::File;
// Read creation and modification dates.
ent.creation_date = reader.ReadMacDate();
ent.modif_date = reader.ReadMacDate();
reader.Skip(8);
// Entity and parent offsets.
ent.entity_off = reader.Tell() - 26;
ent.parent_off = reader.ReadWordBE();
// Read name and data lengths.
uint16_t name_length = reader.ReadHalfBE();
reader.Skip(2);
ent.data.size = reader.ReadWordBE();
ent.data.comp_size = reader.ReadWordBE();
reader.Skip(4);
// The entry is a folder: read the number of files.
if (ent.etype == StuffitEntryType::Folder) {
ent.num_files = reader.ReadHalfBE();
// This folder is not a real one and must be skipped.
if (ent.data.size == 0XFFFFFFFF) {
ReadFileHeader(reader, ent);
return (void)ent.num_files++;
}
}
// The entry is a file: read compression method.
else {
ent.num_files = 0;
ent.data.method = reader.ReadByte();
reader.Skip(1);
}
// Read file/folder name.
ent.name = reader.ReadString(name_length);
// Skip comment (if exist).
if (reader.Tell() < ent.entity_off + header_size) {
uint16_t comment_length = reader.ReadHalfBE();
reader.Skip(comment_length + 2);
}
// Read second flags: if 0x1, there is a ressource fork.
bool has_res = reader.ReadHalfBE() & kFlagHasRessource;
reader.Skip(2);
// Read file type, creator and flags.
ent.type = reader.ReadWordBE();
ent.creator = reader.ReadWordBE();
ent.flags = reader.ReadHalfBE();
reader.Skip(version == 0x1 ? 22 : 18);
// Read ressource data (if exists).
if (has_res) {
ent.res.size = reader.ReadWordBE();
ent.res.comp_size = reader.ReadWordBE();
reader.Skip(4);
ent.res.method = reader.ReadByte();
reader.Skip(1);
} else {
ent.res.size = 0;
ent.res.comp_size = 0;
}
// If the entry is a folder, return here.
if (ent.etype == StuffitEntryType::Folder)
return;
// Set data and res offsets.
ent.data.offset = reader.Tell();
ent.res.offset = ent.data.offset + ent.data.comp_size;
// Seek to the next entry (if it's a file).
reader.Seek(ent.res.offset + ent.res.comp_size);
}
// Extract a directoty.
void ExtractStuffit5(fs::FileReader &reader, const std::string &output)
{
uint16_t num_files = ReadHeader(reader);
std::unordered_map<uint32_t, std::string> folders;
StuffitEntry ent;
std::string dest_folder;
for (uint16_t i = 0; i < num_files; i++) {
ReadFileHeader(reader, ent);
// Get the parent folder of the file.
auto folder = folders.find(ent.parent_off);
dest_folder = (folder != folders.end()) ? folder->second : output;
ExtractStuffitEntry(reader, ent, dest_folder);
num_files += ent.num_files;
// Add this directory to folders map.
if (ent.etype == StuffitEntryType::Folder)
folders[ent.entity_off] = dest_folder + "/" + ent.name;
}
}
} // namespace maconv
} // namespace stuffit

76
src/stuffit/utils/bwt.cc Normal file
View File

@ -0,0 +1,76 @@
/*
BurrowsWheeler Transform.
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stuffit/utils/bwt.h"
namespace maconv {
namespace stuffit {
// Calculate inverse of BWT.
void CalculateInverseBWT(uint32_t *transform, uint8_t *block, int block_len)
{
int counts[256] = {0};
int cumulative_counts[256];
for (int i = 0; i < block_len; i++)
counts[block[i]]++;
for (int i = 0, total = 0; i < 256; i++) {
cumulative_counts[i] = total;
total += counts[i];
counts[i] = 0;
}
for (int i = 0; i < block_len; i++) {
transform[cumulative_counts[block[i]] + counts[block[i]]] = i;
counts[block[i]]++;
}
}
// Reset the decoder.
void MtfDecoder::ResetDecoder()
{
for (int i = 0; i < 256; i++)
table[i] = i;
}
// Decode the next symbol.
int MtfDecoder::Decode(int symbol)
{
int res = table[symbol];
for (int i = symbol; i > 0; i--)
table[i] = table[i-1];
table[0] = res;
return res;
}
} // namespace stuffit
} // namespace maconv

51
src/stuffit/utils/bwt.h Normal file
View File

@ -0,0 +1,51 @@
/*
BurrowsWheeler Transform.
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <inttypes.h>
namespace maconv {
namespace stuffit {
// The MTF (Move-to-Front Transform) decoder.
struct MtfDecoder {
// Reset the decoder.
void ResetDecoder();
// Decode the next symbol.
int Decode(int symbol);
int table[256];
};
// Calculate inverse of BWT.
void CalculateInverseBWT(uint32_t *transform, uint8_t *block, int block_len);
} // namespace stuffit
} // namespace maconv

78
src/stuffit/utils/crc.cc Normal file
View File

@ -0,0 +1,78 @@
/*
Cyclic redundancy check (CRC).
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stuffit/utils/crc.h"
namespace maconv {
namespace stuffit {
// EDB88320 CRC table.
const uint32_t CRCTable_edb88320[256]= {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
// Calculate CRC for the next byte.
uint32_t CalcCRC(uint32_t prevcrc, uint8_t byte, const uint32_t *table)
{
return table[(prevcrc ^ byte) & 0xFF] ^ (prevcrc >> 8);
}
} // namespace stuffit
} // namespace maconv

41
src/stuffit/utils/crc.h Normal file
View File

@ -0,0 +1,41 @@
/*
Cyclic redundancy check (CRC).
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <inttypes.h>
namespace maconv {
namespace stuffit {
// CRC tables.
extern const uint32_t CRCTable_edb88320[256];
// Calculate CRC for the next byte.
uint32_t CalcCRC(uint32_t prevcrc, uint8_t byte, const uint32_t *table);
} // namespace stuffit
} // namespace maconv

View File

@ -0,0 +1,275 @@
/*
Huffman decoder.
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stuffit/utils/huffman.h"
#include "stuffit/methods.h"
#include <make_unique.hpp>
#include <limits>
namespace maconv {
namespace stuffit {
// Reverse a 32bits integer.
static uint32_t Reverse32(uint32_t val)
{
val = ((val >> 1) & 0x55555555) | ((val & 0x55555555) << 1);
val = ((val >> 2) & 0x33333333) | ((val & 0x33333333) << 2);
val = ((val >> 4) & 0x0F0F0F0F) | ((val & 0x0F0F0F0F) << 4);
val = ((val >> 8) & 0x00FF00FF) | ((val & 0x00FF00FF) << 8);
return (val >> 16) | (val << 16);
}
// Reverse a Nbits integer.
static uint32_t ReverseN(uint32_t val, int length)
{
return Reverse32(val) >> (32 - length);
}
// Copy an Huffman decoder.
HuffmanDecoder &HuffmanDecoder::operator=(const HuffmanDecoder &other)
{
tree = other.tree;
num_entries = other.num_entries;
min_length = other.min_length;
max_length = other.max_length;
return *this;
}
// Initialize the decoder.
void HuffmanDecoder::Initialize()
{
NewNode();
num_entries = 1;
min_length = std::numeric_limits<int>::max();
max_length = std::numeric_limits<int>::min();
}
// Initialize the decoder (with some lengths).
void HuffmanDecoder::Initialize(const int *lengths, int numsymbols, int maxcodelength,
bool zeros)
{
Initialize();
int code = 0, symbolsleft = numsymbols;
for (int length = 1; length <= maxcodelength; length++) {
for (int i = 0; i < numsymbols; i++) {
if (lengths[i] != length) continue;
AddValue(i, zeros ? code : ~code, length);
code++;
if (--symbolsleft == 0) return; // Early exit if all codes have been handled.
}
code <<= 1;
}
}
// Add a new value in the decoder.
void HuffmanDecoder::AddValue(int value, uint32_t code, int length)
{
AddValue(value, code, length, length);
}
// Add a new value in the decoder.
void HuffmanDecoder::AddValue(int value, uint32_t code, int length, int repeat_pos)
{
if (length > max_length) max_length = length;
if (length < min_length) min_length = length;
repeat_pos = length - 1 - repeat_pos;
int last_node = 0;
int codest = ((code >> (repeat_pos - 1)) & 3);
if (repeat_pos == 0 || (repeat_pos >= 0 && (codest == 0 || codest == 3)))
throw ExtractException("Huffman: invalid repeat position");
for (int bitpos = length - 1; bitpos >= 0; bitpos--) {
int bit = (code >> bitpos) & 1;
if (IsLeafNode(last_node))
throw ExtractException("Huffman: prefix already exists");
if (bitpos == repeat_pos) {
if (!IsOpenBranch(last_node, bit))
throw ExtractException("Huffman: invalid repeating code");
int repeat_node = NewNode();
int next_node = NewNode();
SetBranch(last_node, bit, repeat_node);
SetBranch(repeat_node, bit, repeat_node);
SetBranch(repeat_node, bit ^ 1, next_node);
last_node = next_node;
bitpos++; // Terminating bit already handled, skip it.
}
else {
if (IsOpenBranch(last_node, bit))
SetBranch(last_node, bit, NewNode());
last_node = Branch(last_node, bit);
}
}
if (!IsEmptyNode(last_node))
throw ExtractException("Huffman: prefix already exists");
SetLeafValue(last_node, value);
}
// Add a new value in the decoder (low bit first).
void HuffmanDecoder::AddValueLF(int value, uint32_t code, int length)
{
AddValue(value, ReverseN(code, length), length, length);
}
// Add a new value in the decoder (low bit first).
void HuffmanDecoder::AddValueLF(int value, uint32_t code, int length, int repeat_pos)
{
AddValue(value, ReverseN(code, length), length, repeat_pos);
}
// Get the next symbol from a bit reader.
int HuffmanDecoder::NextSymbol(utils::BitReader &input)
{
if (!table)
throw ExtractException("Huffman: search table not built");
int bits = input.ReadWord(table_size, false);
int length = table[bits].length;
int value = table[bits].value;
if (length < 0)
throw ExtractException("Huffman: invalid prefix code when getting next symbol [length]");
if (length <= table_size) {
input.SkipBits(length);
return value;
}
input.SkipBits(table_size);
int node = value;
for (int bit; !IsLeafNode(node); node = Branch(node, bit)) {
bit = input.ReadBit();
if (IsOpenBranch(node, bit))
throw ExtractException("Huffman: invalid prefix code when getting next symbol [code]");
}
return LeafValue(node);
}
// Make search table (Little Endian).
void HuffmanDecoder::MakeTableRecursLE(int node, HuffmanTableEntry *table,
int depth)
{
int curr_table_size = (1 << (table_size - depth));
int curr_stride = (1 << depth);
if (IsLeafNode(node)) {
for (int i = 0; i < curr_table_size; i++) {
table[i * curr_stride].length = depth;
table[i * curr_stride].value = LeafValue(node);
}
}
else if (IsInvalidNode(node)) {
for (int i = 0; i < curr_table_size; i++)
table[i * curr_stride].length = -1;
}
else {
if (depth == table_size) {
table[0].length = table_size + 1;
table[0].value = node;
} else {
MakeTableRecursLE(LeftBranch(node), table, depth + 1);
MakeTableRecursLE(RightBranch(node), table + curr_stride, depth + 1);
}
}
}
// Make search table (Big Endian).
void HuffmanDecoder::MakeTableRecursBE(int node, HuffmanTableEntry *table,
int depth)
{
int curr_table_size = (1 << (table_size - depth));
if (IsInvalidNode(node)) {
for (int i = 0; i < curr_table_size; i++)
table[i].length = -1;
}
else if (IsLeafNode(node)) {
for(int i = 0; i < curr_table_size; i++) {
table[i].length = depth;
table[i].value = LeafValue(node);
}
}
else {
if (depth == table_size) {
table[0].length = table_size + 1;
table[0].value = node;
} else {
MakeTableRecursBE(LeftBranch(node), table, depth + 1);
MakeTableRecursBE(RightBranch(node), table + curr_table_size/2, depth + 1);
}
}
}
// Make the search table.
void HuffmanDecoder::MakeTable(bool is_LE)
{
constexpr int kMaxTableSize = 10;
if (max_length < min_length) table_size = kMaxTableSize;
else if (max_length >= kMaxTableSize) table_size = kMaxTableSize;
else table_size = max_length;
table = std::make_unique<HuffmanTableEntry[]>(1 << table_size);
if (is_LE) MakeTableRecursLE(0, table.get(), 0);
else MakeTableRecursBE(0, table.get(), 0);
}
} // namespace stuffit
} // namespace maconv

122
src/stuffit/utils/huffman.h Normal file
View File

@ -0,0 +1,122 @@
/*
Huffman decoder.
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "utils/bit_reader.h"
#include <vector>
#include <memory>
namespace maconv {
namespace stuffit {
// A node in the Huffman tree.
struct HuffmanTreeNode {
int branches[2];
};
struct HuffmanTableEntry {
uint32_t length;
int32_t value;
};
// Huffman code decoder.
struct HuffmanDecoder {
// Copy an Huffman decoder.
HuffmanDecoder &operator=(const HuffmanDecoder &);
// Initialize the decoder.
void Initialize();
void Initialize(const int *lengths, int numsymbols, int maxcodelength, bool zeros);
// Add a new value in the decoder (high bit first).
void AddValue(int value, uint32_t code, int length);
void AddValue(int value, uint32_t code, int length, int repeat_pos);
// Add a new value in the decoder (low bit first).
void AddValueLF(int value, uint32_t code, int length);
void AddValueLF(int value, uint32_t code, int length, int repeat_pos);
// Get the next symbol from a bit reader.
int NextSymbol(utils::BitReader &input);
// Make the search table.
void MakeTable(bool is_LE);
protected:
// Get a pointer on a node.
HuffmanTreeNode *NodePtr(int node) { return &tree[node]; }
// Get/set node ID on branch |bit| of |node|.
int Branch(int node, int bit) { return NodePtr(node)->branches[bit]; }
void SetBranch(int node, int bit, int next) { NodePtr(node)->branches[bit] = next; }
// Get/set left branch of |node|.
int LeftBranch(int node) { return Branch(node, 0); }
void SetLeftBranch(int node, int next) { SetBranch(node, 0, next); }
// Get/set right branch of |node|.
int RightBranch(int node) { return Branch(node, 1); }
void SetRightBranch(int node, int next) { SetBranch(node, 1, next); }
// Get/set leaf value.
int LeafValue(int node) { return LeftBranch(node); }
void SetLeafValue(int node, int v) { SetLeftBranch(node, v); SetRightBranch(node, v); }
// Set/get whether a node is empty or not.
void SetEmptyNode(int node) { SetLeftBranch(node, -1); SetRightBranch(node, -2); }
bool IsEmptyNode(int node) { return LeftBranch(node) == -1 && RightBranch(node) == -2; }
// Get information on a node.
bool IsInvalidNode(int node) { return node < 0; }
bool IsOpenBranch(int node, int bit) { return IsInvalidNode(Branch(node, bit)); }
bool IsLeafNode(int node) { return LeftBranch(node) == RightBranch(node); }
// Create a new node.
int NewNode() { tree.push_back({0}); SetEmptyNode(tree.size()-1);
return tree.size()-1; }
// Make search table.
void MakeTableRecursLE(int node, HuffmanTableEntry *table, int depth);
void MakeTableRecursBE(int node, HuffmanTableEntry *table, int depth);
std::vector<HuffmanTreeNode> tree;
int num_entries, min_length, max_length;
int table_size;
std::unique_ptr<HuffmanTableEntry[]> table;
};
} // namespace stuffit
} // namespace maconv

128
src/utils/bit_reader.cc Normal file
View File

@ -0,0 +1,128 @@
/*
Read a buffer by group of bits (not necessarily multiple of 8).
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "utils/bit_reader.h"
#include <algorithm>
namespace maconv {
namespace utils {
// Load a buffer of |length| in the reader.
void BitReader::Load(uint8_t *data, int length)
{
this->data = data;
this->end = data + length;
}
// Ignore a number of bits (n can be > 32).
void BitReader::IgnoreBits(int n)
{
for (; n > 0; n -= 24)
ReadWord(std::min(n, 24));
}
// Refill the bit cache.
void BitReaderBE::FillBitCache()
{
int num_bytes = std::min((32 - num_bits) / 8, (int)(end - data));
num_bits += (8 * num_bytes);
for (int i = 0; i < num_bytes; i++)
bits = (bits << 8) | *(data++);
}
// Read a word (i.e. n <= 32 bits).
uint32_t BitReaderBE::ReadWord(int n, bool skip)
{
if (n > num_bits)
FillBitCache();
uint32_t ret = (bits >> (num_bits - n)) & ((1 << n) - 1);
if (skip) SkipBits(n);
return ret;
}
// Read a long word (n can be > 25 bits).
uint32_t BitReaderBE::ReadLongWord(int n, bool skip)
{
if (n <= 25) return ReadWord(n, skip);
int bits = ReadWord(25, skip) << (n - 25);
return bits | ReadWord(n - 25, skip);
}
// Skip some bits (that has been readed).
void BitReaderBE::SkipBits(int n)
{
num_bits -= n;
}
// Refill the bit cache.
void BitReaderLE::FillBitCache()
{
int num_bytes = std::min((32 - num_bits) / 8, (int)(end - data));
for (int i = 0; i < num_bytes; i++, num_bits += 8)
bits |= *(data++) << num_bits;
}
// Read a word (i.e. n <= 32 bits).
uint32_t BitReaderLE::ReadWord(int n, bool skip)
{
if (n > num_bits)
FillBitCache();
uint32_t ret = bits & ((1 << n) - 1);
if (skip) SkipBits(n);
return ret;
}
// Read a long word (n can be > 25 bits).
uint32_t BitReaderLE::ReadLongWord(int n, bool skip)
{
if (n <= 25) return ReadWord(n, skip);
int bits = ReadWord(25, skip);
return (ReadWord(n - 25, skip) << 25) | bits;
}
// Skip some bits (that has been readed).
void BitReaderLE::SkipBits(int n)
{
bits >>= n;
num_bits -= n;
}
} // namespace utils
} // namespace maconv

100
src/utils/bit_reader.h Normal file
View File

@ -0,0 +1,100 @@
/*
Read a buffer by group of bits (not necessarily multiple of 8).
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <inttypes.h>
namespace maconv {
namespace utils {
// Read a buffer by group of bits (not necessarily multiple of 8).
struct BitReader {
// Load a buffer of |length| in the reader.
void Load(uint8_t *data, int length);
// Is the reader at end?
bool HasEnded(int n = 0) { return data == end && num_bits <= n; }
// Ignore a number of bits (n can be > 32).
void IgnoreBits(int n);
// Read a single bit.
virtual uint8_t ReadBit() { return ReadWord(1); };
// Read a word (i.e. n <= 25 bits).
virtual uint32_t ReadWord(int n, bool skip = true) = 0;
// Read a long word (n can be > 25).
virtual uint32_t ReadLongWord(int n, bool skip = true) = 0;
// Skip some bits (that has been readed).
virtual void SkipBits(int n) = 0;
uint8_t *data; // Current pointer on data.
uint8_t *end; // Length of the buffer.
uint32_t bits = 0; // Bit cache.
int num_bits = 0; // Number of bits in the cache.
};
// BitReader that reads Big Endian integers.
struct BitReaderBE : BitReader {
// Read a word (i.e. <= 32 bits).
uint32_t ReadWord(int n, bool skip = true) override;
// Read a long word (n can be > 25 bits).
uint32_t ReadLongWord(int n, bool skip = true) override;
// Skip some bits (that has been readed).
void SkipBits(int n) override;
// Refill the bit cache.
void FillBitCache();
};
// BitReader that reads Little Endian integers.
struct BitReaderLE : BitReader {
// Read a word (i.e. <= 25 bits).
uint32_t ReadWord(int n, bool skip = true) override;
// Read a long word (n can be > 25 bits).
uint32_t ReadLongWord(int n, bool skip = true) override;
// Skip some bits (that has been readed).
void SkipBits(int n) override;
// Refill the bit cache.
void FillBitCache();
};
} // namespace utils
} // namespace maconv

119
src/utils/buffer_stream.cc Normal file
View File

@ -0,0 +1,119 @@
/*
Create a stream from a buffer.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "utils/buffer_stream.h"
namespace maconv {
namespace utils {
// "RawDataStreamBuf" constructor.
RawDataStreamBuf::RawDataStreamBuf(uint8_t *data, uint32_t length)
{
SetData(data, length);
}
// Set the data and length for this stream buffer.
void RawDataStreamBuf::SetData(uint8_t *data, uint32_t length)
{
char *start = reinterpret_cast<char *>(data);
char *end = reinterpret_cast<char *>(data + length);
setg(start, start, end);
setp(start, end);
}
// Set the position indicator relative to some other position.
auto RawDataStreamBuf::seekoff(off_type off, std::ios_base::seekdir dir,
std::ios_base::openmode which) -> pos_type
{
// If dir is beginning or end: set an absolute position directly.
switch (dir) {
case std::ios_base::beg:
return seekpos(off, which);
case std::ios_base::end:
return seekpos(off + (egptr() - eback()), which);
}
// Set position from cursor (return twice if both in and out are set).
if (which & std::ios_base::in)
seekpos(off + (gptr() - eback()), std::ios_base::in);
if (which & std::ios_base::out)
seekpos(off + (pptr() - eback()), std::ios_base::out);
return ((which & std::ios_base::in) ? gptr() : pptr()) - eback();
}
// Set the position indicator to an absolute position.
auto RawDataStreamBuf::seekpos(pos_type pos, std::ios_base::openmode which)
-> pos_type
{
if (which & std::ios_base::in)
setg(eback(), eback() + pos, egptr());
if (which & std::ios_base::out)
pbump(pos - (pptr() - eback()));
return pos;
}
// Get the number of characters available for input.
std::streamsize RawDataStreamBuf::showmanyc()
{
return egptr() - gptr();
}
// Reads count characters from the input sequence.
std::streamsize RawDataStreamBuf::xsgetn(char *p, std::streamsize n)
{
std::streamsize length = std::min(n, egptr() - gptr());
std::copy(gptr(), gptr() + length, p);
gbump(length);
return length;
}
// Write count characters to the output sequence.
std::streamsize RawDataStreamBuf::xsputn(const char *p, std::streamsize n)
{
std::streamsize length = std::min(n, epptr() - pptr());
std::copy(p, p + length, pptr());
pbump(length);
return length;
}
// "BufferStreamBuf" constructor.
BufferStreamBuf::BufferStreamBuf(uint32_t length)
: buffer{new uint8_t[length]}
{
SetData(buffer.get(), length);
}
} // namespace utils
} // namespace maconv

69
src/utils/buffer_stream.h Normal file
View File

@ -0,0 +1,69 @@
/*
Create a stream from a buffer.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <streambuf>
#include <memory>
namespace maconv {
namespace utils {
// A stream buffer from raw memory data.
struct RawDataStreamBuf : std::streambuf {
RawDataStreamBuf() = default;
RawDataStreamBuf(uint8_t *data, uint32_t length);
// Set the data and length for this stream buffer.
void SetData(uint8_t *data, uint32_t length);
protected:
// Get the number of characters available for input.
std::streamsize showmanyc() override;
// Set the position indicator relative to some other position.
pos_type seekoff(off_type off, std::ios_base::seekdir dir,
std::ios_base::openmode which) override;
// Set the position indicator to an absolute position.
pos_type seekpos(pos_type pos, std::ios_base::openmode which) override;
// Read count characters from the input sequence.
std::streamsize xsgetn(char *p, std::streamsize n) override;
// Write count characters to the output sequence.
std::streamsize xsputn(const char *s, std::streamsize n) override;
};
// A stream buffer from a buffer.
struct BufferStreamBuf : RawDataStreamBuf {
BufferStreamBuf(uint32_t length);
std::unique_ptr<uint8_t[]> buffer;
};
} // namespace utils
} // namespace maconv

4113
vendors/CLI11.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

46
vendors/libhfs/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,46 @@
#
# Build libHFS.
#
# Copyright (C) 2017, Guillaume Gonnet
# License GPL3
project(libhfs)
# Note: this CMake file is an adaptation of the default autoconf file from
# libhfs.
# Check that a function exists.
include(CheckFunctionExists)
check_function_exists(mktime HAVE_MKTIME)
# Add "HAVE_CONFIG_H" definition.
add_definitions(-DHAVE_CONFIG_H)
# Configure "config.h" file.
configure_file(config.h.in config.h)
# Source files.
set(LIBHFS_SRC
"block.h" "block.c"
"btree.h" "btree.c"
"data.h" "data.c"
"file.h" "file.c"
"low.h" "low.c"
"medium.h" "medium.c"
"node.h" "node.c"
"os.h" "os-unix.c"
"record.h" "record.c"
"version.h" "version.c"
"volume.h" "volume.c"
"hfs.h" "hfs.c"
"apple.h"
"config.h"
)
# Include all headers from this folder.
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Create the library.
add_library(hfs STATIC ${LIBHFS_SRC})

272
vendors/libhfs/apple.h vendored Normal file
View File

@ -0,0 +1,272 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: apple.h,v 1.1 1998/04/11 08:27:11 rob Exp $
*/
typedef signed char Char;
typedef unsigned char UChar;
typedef signed char SignedByte;
typedef signed short Integer;
typedef unsigned short UInteger;
typedef signed long LongInt;
typedef unsigned long ULongInt;
typedef char Str15[16];
typedef char Str31[32];
typedef long OSType;
typedef struct {
Integer sbSig; /* device signature (should be 0x4552) */
Integer sbBlkSize; /* block size of the device (in bytes) */
LongInt sbBlkCount; /* number of blocks on the device */
Integer sbDevType; /* reserved */
Integer sbDevId; /* reserved */
LongInt sbData; /* reserved */
Integer sbDrvrCount; /* number of driver descriptor entries */
LongInt ddBlock; /* first driver's starting block */
Integer ddSize; /* size of the driver, in 512-byte blocks */
Integer ddType; /* driver operating system type (MacOS = 1) */
Integer ddPad[243]; /* additional drivers, if any */
} Block0;
typedef struct {
Integer pmSig; /* partition signature (0x504d or 0x5453) */
Integer pmSigPad; /* reserved */
LongInt pmMapBlkCnt; /* number of blocks in partition map */
LongInt pmPyPartStart; /* first physical block of partition */
LongInt pmPartBlkCnt; /* number of blocks in partition */
Char pmPartName[33]; /* partition name */
Char pmParType[33]; /* partition type */
LongInt pmLgDataStart; /* first logical block of data area */
LongInt pmDataCnt; /* number of blocks in data area */
LongInt pmPartStatus; /* partition status information */
LongInt pmLgBootStart; /* first logical block of boot code */
LongInt pmBootSize; /* size of boot code, in bytes */
LongInt pmBootAddr; /* boot code load address */
LongInt pmBootAddr2; /* reserved */
LongInt pmBootEntry; /* boot code entry point */
LongInt pmBootEntry2; /* reserved */
LongInt pmBootCksum; /* boot code checksum */
Char pmProcessor[17];/* processor type */
Integer pmPad[188]; /* reserved */
} Partition;
typedef struct {
Integer bbID; /* boot blocks signature */
LongInt bbEntry; /* entry point to boot code */
Integer bbVersion; /* boot blocks version number */
Integer bbPageFlags; /* used internally */
Str15 bbSysName; /* System filename */
Str15 bbShellName; /* Finder filename */
Str15 bbDbg1Name; /* debugger filename */
Str15 bbDbg2Name; /* debugger filename */
Str15 bbScreenName; /* name of startup screen */
Str15 bbHelloName; /* name of startup program */
Str15 bbScrapName; /* name of system scrap file */
Integer bbCntFCBs; /* number of FCBs to allocate */
Integer bbCntEvts; /* number of event queue elements */
LongInt bb128KSHeap; /* system heap size on 128K Mac */
LongInt bb256KSHeap; /* used internally */
LongInt bbSysHeapSize; /* system heap size on all machines */
Integer filler; /* reserved */
LongInt bbSysHeapExtra; /* additional system heap space */
LongInt bbSysHeapFract; /* fraction of RAM for system heap */
} BootBlkHdr;
typedef struct {
UInteger xdrStABN; /* first allocation block */
UInteger xdrNumABlks; /* number of allocation blocks */
} ExtDescriptor;
typedef ExtDescriptor ExtDataRec[3];
typedef struct {
SignedByte xkrKeyLen; /* key length */
SignedByte xkrFkType; /* fork type (0x00/0xff == data/resource */
ULongInt xkrFNum; /* file number */
UInteger xkrFABN; /* starting file allocation block */
} ExtKeyRec;
typedef struct {
SignedByte ckrKeyLen; /* key length */
SignedByte ckrResrv1; /* reserved */
ULongInt ckrParID; /* parent directory ID */
Str31 ckrCName; /* catalog node name */
} CatKeyRec;
typedef struct {
Integer v; /* vertical coordinate */
Integer h; /* horizontal coordinate */
} Point;
typedef struct {
Integer top; /* top edge of rectangle */
Integer left; /* left edge */
Integer bottom; /* bottom edge */
Integer right; /* right edge */
} Rect;
typedef struct {
Rect frRect; /* folder's rectangle */
Integer frFlags; /* flags */
Point frLocation; /* folder's location */
Integer frView; /* folder's view */
} DInfo;
typedef struct {
Point frScroll; /* scroll position */
LongInt frOpenChain; /* directory ID chain of open folders */
Integer frUnused; /* reserved */
Integer frComment; /* comment ID */
LongInt frPutAway; /* directory ID */
} DXInfo;
typedef struct {
OSType fdType; /* file type */
OSType fdCreator; /* file's creator */
Integer fdFlags; /* flags */
Point fdLocation; /* file's location */
Integer fdFldr; /* file's window */
} FInfo;
typedef struct {
Integer fdIconID; /* icon ID */
Integer fdUnused[4]; /* reserved */
Integer fdComment; /* comment ID */
LongInt fdPutAway; /* home directory ID */
} FXInfo;
typedef struct {
Integer drSigWord; /* volume signature (0x4244 for HFS) */
LongInt drCrDate; /* date and time of volume creation */
LongInt drLsMod; /* date and time of last modification */
Integer drAtrb; /* volume attributes */
UInteger drNmFls; /* number of files in root directory */
UInteger drVBMSt; /* first block of volume bit map (always 3) */
UInteger drAllocPtr; /* start of next allocation search */
UInteger drNmAlBlks; /* number of allocation blocks in volume */
ULongInt drAlBlkSiz; /* size (in bytes) of allocation blocks */
ULongInt drClpSiz; /* default clump size */
UInteger drAlBlSt; /* first allocation block in volume */
LongInt drNxtCNID; /* next unused catalog node ID (dir/file ID) */
UInteger drFreeBks; /* number of unused allocation blocks */
char drVN[28]; /* volume name (1-27 chars) */
LongInt drVolBkUp; /* date and time of last backup */
Integer drVSeqNum; /* volume backup sequence number */
ULongInt drWrCnt; /* volume write count */
ULongInt drXTClpSiz; /* clump size for extents overflow file */
ULongInt drCTClpSiz; /* clump size for catalog file */
UInteger drNmRtDirs; /* number of directories in root directory */
ULongInt drFilCnt; /* number of files in volume */
ULongInt drDirCnt; /* number of directories in volume */
LongInt drFndrInfo[8]; /* information used by the Finder */
UInteger drEmbedSigWord; /* type of embedded volume */
ExtDescriptor drEmbedExtent; /* location of embedded volume */
ULongInt drXTFlSize; /* size (in bytes) of extents overflow file */
ExtDataRec drXTExtRec; /* first extent record for extents file */
ULongInt drCTFlSize; /* size (in bytes) of catalog file */
ExtDataRec drCTExtRec; /* first extent record for catalog file */
} MDB;
typedef enum {
cdrDirRec = 1,
cdrFilRec = 2,
cdrThdRec = 3,
cdrFThdRec = 4
} CatDataType;
typedef struct {
SignedByte cdrType; /* record type */
SignedByte cdrResrv2; /* reserved */
union {
struct { /* cdrDirRec */
Integer dirFlags; /* directory flags */
UInteger dirVal; /* directory valence */
ULongInt dirDirID; /* directory ID */
LongInt dirCrDat; /* date and time of creation */
LongInt dirMdDat; /* date and time of last modification */
LongInt dirBkDat; /* date and time of last backup */
DInfo dirUsrInfo; /* Finder information */
DXInfo dirFndrInfo; /* additional Finder information */
LongInt dirResrv[4]; /* reserved */
} dir;
struct { /* cdrFilRec */
SignedByte
filFlags; /* file flags */
SignedByte
filTyp; /* file type */
FInfo filUsrWds; /* Finder information */
ULongInt filFlNum; /* file ID */
UInteger filStBlk; /* first alloc block of data fork */
ULongInt filLgLen; /* logical EOF of data fork */
ULongInt filPyLen; /* physical EOF of data fork */
UInteger filRStBlk; /* first alloc block of resource fork */
ULongInt filRLgLen; /* logical EOF of resource fork */
ULongInt filRPyLen; /* physical EOF of resource fork */
LongInt filCrDat; /* date and time of creation */
LongInt filMdDat; /* date and time of last modification */
LongInt filBkDat; /* date and time of last backup */
FXInfo filFndrInfo; /* additional Finder information */
UInteger filClpSize; /* file clump size */
ExtDataRec
filExtRec; /* first data fork extent record */
ExtDataRec
filRExtRec; /* first resource fork extent record */
LongInt filResrv; /* reserved */
} fil;
struct { /* cdrThdRec */
LongInt thdResrv[2]; /* reserved */
ULongInt thdParID; /* parent ID for this directory */
Str31 thdCName; /* name of this directory */
} dthd;
struct { /* cdrFThdRec */
LongInt fthdResrv[2]; /* reserved */
ULongInt fthdParID; /* parent ID for this file */
Str31 fthdCName; /* name of this file */
} fthd;
} u;
} CatDataRec;
typedef struct {
ULongInt ndFLink; /* forward link */
ULongInt ndBLink; /* backward link */
SignedByte ndType; /* node type */
SignedByte ndNHeight; /* node level */
UInteger ndNRecs; /* number of records in node */
Integer ndResv2; /* reserved */
} NodeDescriptor;
enum {
ndIndxNode = (SignedByte) 0x00,
ndHdrNode = (SignedByte) 0x01,
ndMapNode = (SignedByte) 0x02,
ndLeafNode = (SignedByte) 0xff
};
typedef struct {
UInteger bthDepth; /* current depth of tree */
ULongInt bthRoot; /* number of root node */
ULongInt bthNRecs; /* number of leaf records in tree */
ULongInt bthFNode; /* number of first leaf node */
ULongInt bthLNode; /* number of last leaf node */
UInteger bthNodeSize; /* size of a node */
UInteger bthKeyLen; /* maximum length of a key */
ULongInt bthNNodes; /* total number of nodes in tree */
ULongInt bthFree; /* number of free nodes */
SignedByte bthResv[76]; /* reserved */
} BTHdrRec;

807
vendors/libhfs/block.c vendored Normal file
View File

@ -0,0 +1,807 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: block.c,v 1.11 1998/11/02 22:08:52 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include "libhfs.h"
# include "volume.h"
# include "block.h"
# include "os.h"
# define INUSE(b) ((b)->flags & HFS_BUCKET_INUSE)
# define DIRTY(b) ((b)->flags & HFS_BUCKET_DIRTY)
/*
* NAME: block->init()
* DESCRIPTION: initialize a volume's block cache
*/
int b_init(hfsvol *vol)
{
bcache *cache;
int i;
ASSERT(vol->cache == 0);
cache = ALLOC(bcache, 1);
if (cache == 0)
ERROR(ENOMEM, 0);
vol->cache = cache;
cache->vol = vol;
cache->tail = &cache->chain[HFS_CACHESZ - 1];
cache->hits = 0;
cache->misses = 0;
for (i = 0; i < HFS_CACHESZ; ++i)
{
bucket *b = &cache->chain[i];
b->flags = 0;
b->count = 0;
b->bnum = 0;
b->data = &cache->pool[i];
b->cnext = b + 1;
b->cprev = b - 1;
b->hnext = 0;
b->hprev = 0;
}
cache->chain[0].cprev = cache->tail;
cache->tail->cnext = &cache->chain[0];
for (i = 0; i < HFS_HASHSZ; ++i)
cache->hash[i] = 0;
return 0;
fail:
return -1;
}
# ifdef DEBUG
/*
* NAME: block->showstats()
* DESCRIPTION: output cache hit/miss ratio
*/
void b_showstats(const bcache *cache)
{
fprintf(stderr, "BLOCK: CACHE vol 0x%lx \"%s\" hit/miss ratio = %.3f\n",
(unsigned long) cache->vol, cache->vol->mdb.drVN,
(float) cache->hits / (float) cache->misses);
}
/*
* NAME: block->dumpcache()
* DESCRIPTION: dump the cache tables for a volume
*/
void b_dumpcache(const bcache *cache)
{
const bucket *b;
int i;
fprintf(stderr, "BLOCK CACHE DUMP:\n");
for (i = 0, b = cache->tail->cnext; i < HFS_CACHESZ; ++i, b = b->cnext)
{
if (INUSE(b))
{
fprintf(stderr, "\t %lu", b->bnum);
if (DIRTY(b))
fprintf(stderr, "*");
fprintf(stderr, ":%u", b->count);
}
}
fprintf(stderr, "\n");
fprintf(stderr, "BLOCK HASH DUMP:\n");
for (i = 0; i < HFS_HASHSZ; ++i)
{
int seen = 0;
for (b = cache->hash[i]; b; b = b->hnext)
{
if (! seen)
fprintf(stderr, " %d:", i);
if (INUSE(b))
{
fprintf(stderr, " %lu", b->bnum);
if (DIRTY(b))
fprintf(stderr, "*");
fprintf(stderr, ":%u", b->count);
}
seen = 1;
}
if (seen)
fprintf(stderr, "\n");
}
}
# endif
/*
* NAME: fillchain()
* DESCRIPTION: fill a chain of bucket buffers with a single read
*/
static
int fillchain(hfsvol *vol, bucket **bptr, unsigned int *count)
{
bucket *blist[HFS_BLOCKBUFSZ], **start = bptr;
unsigned long bnum;
unsigned int len, i;
for (len = 0; len < HFS_BLOCKBUFSZ &&
(unsigned int) (bptr - start) < *count; ++bptr)
{
if (INUSE(*bptr))
continue;
if (len > 0 && (*bptr)->bnum != bnum)
break;
blist[len++] = *bptr;
bnum = (*bptr)->bnum + 1;
}
*count = bptr - start;
if (len == 0)
goto done;
else if (len == 1)
{
if (b_readpb(vol, vol->vstart + blist[0]->bnum,
blist[0]->data, 1) == -1)
goto fail;
}
else
{
block buffer[HFS_BLOCKBUFSZ];
if (b_readpb(vol, vol->vstart + blist[0]->bnum, buffer, len) == -1)
goto fail;
for (i = 0; i < len; ++i)
memcpy(blist[i]->data, buffer[i], HFS_BLOCKSZ);
}
for (i = 0; i < len; ++i)
{
blist[i]->flags |= HFS_BUCKET_INUSE;
blist[i]->flags &= ~HFS_BUCKET_DIRTY;
}
done:
return 0;
fail:
return -1;
}
/*
* NAME: flushchain()
* DESCRIPTION: store a chain of bucket buffers with a single write
*/
static
int flushchain(hfsvol *vol, bucket **bptr, unsigned int *count)
{
bucket *blist[HFS_BLOCKBUFSZ], **start = bptr;
unsigned long bnum;
unsigned int len, i;
for (len = 0; len < HFS_BLOCKBUFSZ &&
(unsigned int) (bptr - start) < *count; ++bptr)
{
if (! INUSE(*bptr) || ! DIRTY(*bptr))
continue;
if (len > 0 && (*bptr)->bnum != bnum)
break;
blist[len++] = *bptr;
bnum = (*bptr)->bnum + 1;
}
*count = bptr - start;
if (len == 0)
goto done;
else if (len == 1)
{
if (b_writepb(vol, vol->vstart + blist[0]->bnum,
blist[0]->data, 1) == -1)
goto fail;
}
else
{
block buffer[HFS_BLOCKBUFSZ];
for (i = 0; i < len; ++i)
memcpy(buffer[i], blist[i]->data, HFS_BLOCKSZ);
if (b_writepb(vol, vol->vstart + blist[0]->bnum, buffer, len) == -1)
goto fail;
}
for (i = 0; i < len; ++i)
blist[i]->flags &= ~HFS_BUCKET_DIRTY;
done:
return 0;
fail:
return -1;
}
/*
* NAME: compare()
* DESCRIPTION: comparison function for qsort of cache bucket pointers
*/
static
int compare(const bucket **b1, const bucket **b2)
{
long diff;
diff = (*b1)->bnum - (*b2)->bnum;
if (diff < 0)
return -1;
else if (diff > 0)
return 1;
else
return 0;
}
/*
* NAME: dobuckets()
* DESCRIPTION: fill or flush an array of cache buckets to a volume
*/
static
int dobuckets(hfsvol *vol, bucket **chain, unsigned int len,
int (*func)(hfsvol *, bucket **, unsigned int *))
{
unsigned int count, i;
int result = 0;
qsort(chain, len, sizeof(*chain),
(int (*)(const void *, const void *)) compare);
for (i = 0; i < len; i += count)
{
count = len - i;
if (func(vol, chain + i, &count) == -1)
result = -1;
}
return result;
}
# define fillbuckets(vol, chain, len) dobuckets(vol, chain, len, fillchain)
# define flushbuckets(vol, chain, len) dobuckets(vol, chain, len, flushchain)
/*
* NAME: block->flush()
* DESCRIPTION: commit dirty cache blocks to a volume
*/
int b_flush(hfsvol *vol)
{
bcache *cache = vol->cache;
bucket *chain[HFS_CACHESZ];
int i;
if (cache == 0 || (vol->flags & HFS_VOL_READONLY))
goto done;
for (i = 0; i < HFS_CACHESZ; ++i)
chain[i] = &cache->chain[i];
if (flushbuckets(vol, chain, HFS_CACHESZ) == -1)
goto fail;
done:
# ifdef DEBUG
if (cache)
b_showstats(cache);
# endif
return 0;
fail:
return -1;
}
/*
* NAME: block->finish()
* DESCRIPTION: commit and free a volume's block cache
*/
int b_finish(hfsvol *vol)
{
int result = 0;
if (vol->cache == 0)
goto done;
# ifdef DEBUG
b_dumpcache(vol->cache);
# endif
result = b_flush(vol);
FREE(vol->cache);
vol->cache = 0;
done:
return result;
}
/*
* NAME: findbucket()
* DESCRIPTION: locate a bucket in the cache, and/or its hash slot
*/
static
bucket *findbucket(bcache *cache, unsigned long bnum, bucket ***hslot)
{
bucket *b;
*hslot = &cache->hash[bnum & (HFS_HASHSZ - 1)];
for (b = **hslot; b; b = b->hnext)
{
if (INUSE(b) && b->bnum == bnum)
break;
}
return b;
}
/*
* NAME: reuse()
* DESCRIPTION: free a bucket for reuse, flushing if necessary
*/
static
int reuse(bcache *cache, bucket *b, unsigned long bnum)
{
bucket *chain[HFS_BLOCKBUFSZ], *bptr;
int i;
# ifdef DEBUG
if (INUSE(b))
fprintf(stderr, "BLOCK: CACHE reusing bucket containing "
"vol 0x%lx block %lu:%u\n",
(unsigned long) cache->vol, b->bnum, b->count);
# endif
if (INUSE(b) && DIRTY(b))
{
/* flush most recently unused buckets */
for (bptr = b, i = 0; i < HFS_BLOCKBUFSZ; ++i)
{
chain[i] = bptr;
bptr = bptr->cprev;
}
if (flushbuckets(cache->vol, chain, HFS_BLOCKBUFSZ) == -1)
goto fail;
}
b->flags &= ~HFS_BUCKET_INUSE;
b->count = 1;
b->bnum = bnum;
return 0;
fail:
return -1;
}
/*
* NAME: cplace()
* DESCRIPTION: move a bucket to an appropriate place near head of the chain
*/
static
void cplace(bcache *cache, bucket *b)
{
bucket *p;
for (p = cache->tail->cnext; p->count > 1; p = p->cnext)
--p->count;
b->cnext->cprev = b->cprev;
b->cprev->cnext = b->cnext;
if (cache->tail == b)
cache->tail = b->cprev;
b->cprev = p->cprev;
b->cnext = p;
p->cprev->cnext = b;
p->cprev = b;
}
/*
* NAME: hplace()
* DESCRIPTION: move a bucket to the head of its hash slot
*/
static
void hplace(bucket **hslot, bucket *b)
{
if (*hslot != b)
{
if (b->hprev)
*b->hprev = b->hnext;
if (b->hnext)
b->hnext->hprev = b->hprev;
b->hprev = hslot;
b->hnext = *hslot;
if (*hslot)
(*hslot)->hprev = &b->hnext;
*hslot = b;
}
}
/*
* NAME: getbucket()
* DESCRIPTION: fetch a bucket from the cache, or an empty one to be filled
*/
static
bucket *getbucket(bcache *cache, unsigned long bnum, int fill)
{
bucket **hslot, *b, *p, *bptr,
*chain[HFS_BLOCKBUFSZ], **slots[HFS_BLOCKBUFSZ];
b = findbucket(cache, bnum, &hslot);
if (b)
{
/* cache hit; move towards head of cache chain */
++cache->hits;
if (++b->count > b->cprev->count &&
b != cache->tail->cnext)
{
p = b->cprev;
p->cprev->cnext = b;
b->cnext->cprev = p;
p->cnext = b->cnext;
b->cprev = p->cprev;
p->cprev = b;
b->cnext = p;
if (cache->tail == b)
cache->tail = p;
}
}
else
{
/* cache miss; reuse least-used cache bucket */
++cache->misses;
b = cache->tail;
if (reuse(cache, b, bnum) == -1)
goto fail;
if (fill)
{
unsigned int len = 0;
chain[len] = b;
slots[len++] = hslot;
for (bptr = b->cprev;
len < (HFS_BLOCKBUFSZ >> 1) && ++bnum < cache->vol->vlen;
bptr = bptr->cprev)
{
if (findbucket(cache, bnum, &hslot))
break;
if (reuse(cache, bptr, bnum) == -1)
goto fail;
chain[len] = bptr;
slots[len++] = hslot;
}
if (fillbuckets(cache->vol, chain, len) == -1)
goto fail;
while (--len)
{
cplace(cache, chain[len]);
hplace(slots[len], chain[len]);
}
hslot = slots[0];
}
/* move bucket to appropriate place in chain */
cplace(cache, b);
}
/* insert at front of hash chain */
hplace(hslot, b);
return b;
fail:
return 0;
}
/*
* NAME: block->readpb()
* DESCRIPTION: read blocks from the physical medium (bypassing cache)
*/
int b_readpb(hfsvol *vol, unsigned long bnum, block *bp, unsigned int blen)
{
unsigned long nblocks;
# ifdef DEBUG
fprintf(stderr, "BLOCK: READ vol 0x%lx block %lu",
(unsigned long) vol, bnum);
if (blen > 1)
fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1);
else
fprintf(stderr, "\n");
# endif
nblocks = os_seek(&vol->priv, bnum);
if (nblocks == (unsigned long) -1)
goto fail;
if (nblocks != bnum)
ERROR(EIO, "block seek failed for read");
nblocks = os_read(&vol->priv, bp, blen);
if (nblocks == (unsigned long) -1)
goto fail;
if (nblocks != blen)
ERROR(EIO, "incomplete block read");
return 0;
fail:
return -1;
}
/*
* NAME: block->writepb()
* DESCRIPTION: write blocks to the physical medium (bypassing cache)
*/
int b_writepb(hfsvol *vol, unsigned long bnum, const block *bp,
unsigned int blen)
{
unsigned long nblocks;
# ifdef DEBUG
fprintf(stderr, "BLOCK: WRITE vol 0x%lx block %lu",
(unsigned long) vol, bnum);
if (blen > 1)
fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1);
else
fprintf(stderr, "\n");
# endif
nblocks = os_seek(&vol->priv, bnum);
if (nblocks == (unsigned long) -1)
goto fail;
if (nblocks != bnum)
ERROR(EIO, "block seek failed for write");
nblocks = os_write(&vol->priv, bp, blen);
if (nblocks == (unsigned long) -1)
goto fail;
if (nblocks != blen)
ERROR(EIO, "incomplete block write");
return 0;
fail:
return -1;
}
/*
* NAME: block->readlb()
* DESCRIPTION: read a logical block from a volume (or from the cache)
*/
int b_readlb(hfsvol *vol, unsigned long bnum, block *bp)
{
if (vol->vlen > 0 && bnum >= vol->vlen)
ERROR(EIO, "read nonexistent logical block");
if (vol->cache)
{
bucket *b;
b = getbucket(vol->cache, bnum, 1);
if (b == 0)
goto fail;
memcpy(bp, b->data, HFS_BLOCKSZ);
}
else
{
if (b_readpb(vol, vol->vstart + bnum, bp, 1) == -1)
goto fail;
}
return 0;
fail:
return -1;
}
/*
* NAME: block->writelb()
* DESCRIPTION: write a logical block to a volume (or to the cache)
*/
int b_writelb(hfsvol *vol, unsigned long bnum, const block *bp)
{
if (vol->vlen > 0 && bnum >= vol->vlen)
ERROR(EIO, "write nonexistent logical block");
if (vol->cache)
{
bucket *b;
b = getbucket(vol->cache, bnum, 0);
if (b == 0)
goto fail;
if (! INUSE(b) ||
memcmp(b->data, bp, HFS_BLOCKSZ) != 0)
{
memcpy(b->data, bp, HFS_BLOCKSZ);
b->flags |= HFS_BUCKET_INUSE | HFS_BUCKET_DIRTY;
}
}
else
{
if (b_writepb(vol, vol->vstart + bnum, bp, 1) == -1)
goto fail;
}
return 0;
fail:
return -1;
}
/*
* NAME: block->readab()
* DESCRIPTION: read a block from an allocation block from a volume
*/
int b_readab(hfsvol *vol, unsigned int anum, unsigned int index, block *bp)
{
/* verify the allocation block exists and is marked as in-use */
if (anum >= vol->mdb.drNmAlBlks)
ERROR(EIO, "read nonexistent allocation block");
else if (vol->vbm && ! BMTST(vol->vbm, anum))
ERROR(EIO, "read unallocated block");
return b_readlb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + index, bp);
fail:
return -1;
}
/*
* NAME: block->writeab()
* DESCRIPTION: write a block to an allocation block to a volume
*/
int b_writeab(hfsvol *vol,
unsigned int anum, unsigned int index, const block *bp)
{
/* verify the allocation block exists and is marked as in-use */
if (anum >= vol->mdb.drNmAlBlks)
ERROR(EIO, "write nonexistent allocation block");
else if (vol->vbm && ! BMTST(vol->vbm, anum))
ERROR(EIO, "write unallocated block");
if (v_dirty(vol) == -1)
goto fail;
return b_writelb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + index, bp);
fail:
return -1;
}
/*
* NAME: block->size()
* DESCRIPTION: return the number of physical blocks on a volume's medium
*/
unsigned long b_size(hfsvol *vol)
{
unsigned long low, high, mid;
block b;
high = os_seek(&vol->priv, -1);
if (high != (unsigned long) -1 && high > 0)
return high;
/* manual size detection: first check there is at least 1 block in medium */
if (b_readpb(vol, 0, &b, 1) == -1)
ERROR(EIO, "size of medium indeterminable or empty");
for (low = 0, high = 2880;
high > 0 && b_readpb(vol, high - 1, &b, 1) != -1;
high <<= 1)
low = high - 1;
if (high == 0)
ERROR(EIO, "size of medium indeterminable or too large");
/* common case: 1440K floppy */
if (low == 2879 && b_readpb(vol, 2880, &b, 1) == -1)
return 2880;
/* binary search for other sizes */
while (low < high - 1)
{
mid = (low + high) >> 1;
if (b_readpb(vol, mid, &b, 1) == -1)
high = mid;
else
low = mid;
}
return low + 1;
fail:
return 0;
}

40
vendors/libhfs/block.h vendored Normal file
View File

@ -0,0 +1,40 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: block.h,v 1.10 1998/11/02 22:08:53 rob Exp $
*/
int b_init(hfsvol *);
int b_flush(hfsvol *);
int b_finish(hfsvol *);
int b_readpb(hfsvol *, unsigned long, block *, unsigned int);
int b_writepb(hfsvol *, unsigned long, const block *, unsigned int);
int b_readlb(hfsvol *, unsigned long, block *);
int b_writelb(hfsvol *, unsigned long, const block *);
int b_readab(hfsvol *, unsigned int, unsigned int, block *);
int b_writeab(hfsvol *, unsigned int, unsigned int, const block *);
unsigned long b_size(hfsvol *);
# ifdef DEBUG
void b_showstats(const bcache *);
void b_dumpcache(const bcache *);
# endif

700
vendors/libhfs/btree.c vendored Normal file
View File

@ -0,0 +1,700 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: btree.c,v 1.10 1998/11/02 22:08:54 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include "libhfs.h"
# include "btree.h"
# include "data.h"
# include "file.h"
# include "block.h"
# include "node.h"
/*
* NAME: btree->getnode()
* DESCRIPTION: retrieve a numbered node from a B*-tree file
*/
int bt_getnode(node *np, btree *bt, unsigned long nnum)
{
block *bp = &np->data;
const byte *ptr;
int i;
np->bt = bt;
np->nnum = nnum;
# if 0
fprintf(stderr, "BTREE: GET vol \"%s\" btree \"%s\" node %lu\n",
bt->f.vol->mdb.drVN, bt->f.name, np->nnum);
# endif
/* verify the node exists and is marked as in-use */
if (nnum > 0 && nnum >= bt->hdr.bthNNodes)
ERROR(EIO, "read nonexistent b*-tree node");
else if (bt->map && ! BMTST(bt->map, nnum))
ERROR(EIO, "read unallocated b*-tree node");
if (f_getblock(&bt->f, nnum, bp) == -1)
goto fail;
ptr = *bp;
d_fetchul(&ptr, &np->nd.ndFLink);
d_fetchul(&ptr, &np->nd.ndBLink);
d_fetchsb(&ptr, &np->nd.ndType);
d_fetchsb(&ptr, &np->nd.ndNHeight);
d_fetchuw(&ptr, &np->nd.ndNRecs);
d_fetchsw(&ptr, &np->nd.ndResv2);
if (np->nd.ndNRecs > HFS_MAX_NRECS)
ERROR(EIO, "too many b*-tree node records");
i = np->nd.ndNRecs + 1;
ptr = *bp + HFS_BLOCKSZ - (2 * i);
while (i--)
d_fetchuw(&ptr, &np->roff[i]);
return 0;
fail:
return -1;
}
/*
* NAME: btree->putnode()
* DESCRIPTION: store a numbered node into a B*-tree file
*/
int bt_putnode(node *np)
{
btree *bt = np->bt;
block *bp = &np->data;
byte *ptr;
int i;
# if 0
fprintf(stderr, "BTREE: PUT vol \"%s\" btree \"%s\" node %lu\n",
bt->f.vol->mdb.drVN, bt->f.name, np->nnum);
# endif
/* verify the node exists and is marked as in-use */
if (np->nnum > 0 && np->nnum >= bt->hdr.bthNNodes)
ERROR(EIO, "write nonexistent b*-tree node");
else if (bt->map && ! BMTST(bt->map, np->nnum))
ERROR(EIO, "write unallocated b*-tree node");
ptr = *bp;
d_storeul(&ptr, np->nd.ndFLink);
d_storeul(&ptr, np->nd.ndBLink);
d_storesb(&ptr, np->nd.ndType);
d_storesb(&ptr, np->nd.ndNHeight);
d_storeuw(&ptr, np->nd.ndNRecs);
d_storesw(&ptr, np->nd.ndResv2);
if (np->nd.ndNRecs > HFS_MAX_NRECS)
ERROR(EIO, "too many b*-tree node records");
i = np->nd.ndNRecs + 1;
ptr = *bp + HFS_BLOCKSZ - (2 * i);
while (i--)
d_storeuw(&ptr, np->roff[i]);
return f_putblock(&bt->f, np->nnum, bp);
fail:
return -1;
}
/*
* NAME: btree->readhdr()
* DESCRIPTION: read the header node of a B*-tree
*/
int bt_readhdr(btree *bt)
{
const byte *ptr;
byte *map = 0;
int i;
unsigned long nnum;
if (bt_getnode(&bt->hdrnd, bt, 0) == -1)
goto fail;
if (bt->hdrnd.nd.ndType != ndHdrNode ||
bt->hdrnd.nd.ndNRecs != 3 ||
bt->hdrnd.roff[0] != 0x00e ||
bt->hdrnd.roff[1] != 0x078 ||
bt->hdrnd.roff[2] != 0x0f8 ||
bt->hdrnd.roff[3] != 0x1f8)
ERROR(EIO, "malformed b*-tree header node");
/* read header record */
ptr = HFS_NODEREC(bt->hdrnd, 0);
d_fetchuw(&ptr, &bt->hdr.bthDepth);
d_fetchul(&ptr, &bt->hdr.bthRoot);
d_fetchul(&ptr, &bt->hdr.bthNRecs);
d_fetchul(&ptr, &bt->hdr.bthFNode);
d_fetchul(&ptr, &bt->hdr.bthLNode);
d_fetchuw(&ptr, &bt->hdr.bthNodeSize);
d_fetchuw(&ptr, &bt->hdr.bthKeyLen);
d_fetchul(&ptr, &bt->hdr.bthNNodes);
d_fetchul(&ptr, &bt->hdr.bthFree);
for (i = 0; i < 76; ++i)
d_fetchsb(&ptr, &bt->hdr.bthResv[i]);
if (bt->hdr.bthNodeSize != HFS_BLOCKSZ)
ERROR(EINVAL, "unsupported b*-tree node size");
/* read map record; construct btree bitmap */
/* don't set bt->map until we're done, since getnode() checks it */
map = ALLOC(byte, HFS_MAP1SZ);
if (map == 0)
ERROR(ENOMEM, 0);
memcpy(map, HFS_NODEREC(bt->hdrnd, 2), HFS_MAP1SZ);
bt->mapsz = HFS_MAP1SZ;
/* read continuation map records, if any */
nnum = bt->hdrnd.nd.ndFLink;
while (nnum)
{
node n;
byte *newmap;
if (bt_getnode(&n, bt, nnum) == -1)
goto fail;
if (n.nd.ndType != ndMapNode ||
n.nd.ndNRecs != 1 ||
n.roff[0] != 0x00e ||
n.roff[1] != 0x1fa)
ERROR(EIO, "malformed b*-tree map node");
newmap = REALLOC(map, byte, bt->mapsz + HFS_MAPXSZ);
if (newmap == 0)
ERROR(ENOMEM, 0);
map = newmap;
memcpy(map + bt->mapsz, HFS_NODEREC(n, 0), HFS_MAPXSZ);
bt->mapsz += HFS_MAPXSZ;
nnum = n.nd.ndFLink;
}
bt->map = map;
return 0;
fail:
FREE(map);
return -1;
}
/*
* NAME: btree->writehdr()
* DESCRIPTION: write the header node of a B*-tree
*/
int bt_writehdr(btree *bt)
{
byte *ptr, *map;
unsigned long mapsz, nnum;
int i;
ASSERT(bt->hdrnd.bt == bt &&
bt->hdrnd.nnum == 0 &&
bt->hdrnd.nd.ndType == ndHdrNode &&
bt->hdrnd.nd.ndNRecs == 3);
ptr = HFS_NODEREC(bt->hdrnd, 0);
d_storeuw(&ptr, bt->hdr.bthDepth);
d_storeul(&ptr, bt->hdr.bthRoot);
d_storeul(&ptr, bt->hdr.bthNRecs);
d_storeul(&ptr, bt->hdr.bthFNode);
d_storeul(&ptr, bt->hdr.bthLNode);
d_storeuw(&ptr, bt->hdr.bthNodeSize);
d_storeuw(&ptr, bt->hdr.bthKeyLen);
d_storeul(&ptr, bt->hdr.bthNNodes);
d_storeul(&ptr, bt->hdr.bthFree);
for (i = 0; i < 76; ++i)
d_storesb(&ptr, bt->hdr.bthResv[i]);
memcpy(HFS_NODEREC(bt->hdrnd, 2), bt->map, HFS_MAP1SZ);
if (bt_putnode(&bt->hdrnd) == -1)
goto fail;
map = bt->map + HFS_MAP1SZ;
mapsz = bt->mapsz - HFS_MAP1SZ;
nnum = bt->hdrnd.nd.ndFLink;
while (mapsz)
{
node n;
if (nnum == 0)
ERROR(EIO, "truncated b*-tree map");
if (bt_getnode(&n, bt, nnum) == -1)
goto fail;
if (n.nd.ndType != ndMapNode ||
n.nd.ndNRecs != 1 ||
n.roff[0] != 0x00e ||
n.roff[1] != 0x1fa)
ERROR(EIO, "malformed b*-tree map node");
memcpy(HFS_NODEREC(n, 0), map, HFS_MAPXSZ);
if (bt_putnode(&n) == -1)
goto fail;
map += HFS_MAPXSZ;
mapsz -= HFS_MAPXSZ;
nnum = n.nd.ndFLink;
}
bt->flags &= ~HFS_BT_UPDATE_HDR;
return 0;
fail:
return -1;
}
/* High-Level B*-Tree Routines ============================================= */
/*
* NAME: btree->space()
* DESCRIPTION: assert space for new records, or extend the file
*/
int bt_space(btree *bt, unsigned int nrecs)
{
unsigned int nnodes;
long space;
nnodes = nrecs * (bt->hdr.bthDepth + 1);
if (nnodes <= bt->hdr.bthFree)
goto done;
/* make sure the extents tree has room too */
if (bt != &bt->f.vol->ext)
{
if (bt_space(&bt->f.vol->ext, 1) == -1)
goto fail;
}
space = f_alloc(&bt->f);
if (space == -1)
goto fail;
nnodes = space * (bt->f.vol->mdb.drAlBlkSiz / bt->hdr.bthNodeSize);
bt->hdr.bthNNodes += nnodes;
bt->hdr.bthFree += nnodes;
bt->flags |= HFS_BT_UPDATE_HDR;
bt->f.vol->flags |= HFS_VOL_UPDATE_ALTMDB;
while (bt->hdr.bthNNodes > bt->mapsz * 8)
{
byte *newmap;
node mapnd;
/* extend tree map */
newmap = REALLOC(bt->map, byte, bt->mapsz + HFS_MAPXSZ);
if (newmap == 0)
ERROR(ENOMEM, 0);
memset(newmap + bt->mapsz, 0, HFS_MAPXSZ);
bt->map = newmap;
bt->mapsz += HFS_MAPXSZ;
n_init(&mapnd, bt, ndMapNode, 0);
if (n_new(&mapnd) == -1)
goto fail;
mapnd.nd.ndNRecs = 1;
mapnd.roff[1] = 0x1fa;
/* link the new map node */
if (bt->hdrnd.nd.ndFLink == 0)
{
bt->hdrnd.nd.ndFLink = mapnd.nnum;
mapnd.nd.ndBLink = 0;
}
else
{
node n;
unsigned long nnum;
nnum = bt->hdrnd.nd.ndFLink;
while (1)
{
if (bt_getnode(&n, bt, nnum) == -1)
goto fail;
if (n.nd.ndFLink == 0)
break;
nnum = n.nd.ndFLink;
}
n.nd.ndFLink = mapnd.nnum;
mapnd.nd.ndBLink = n.nnum;
if (bt_putnode(&n) == -1)
goto fail;
}
if (bt_putnode(&mapnd) == -1)
goto fail;
}
done:
return 0;
fail:
return -1;
}
/*
* NAME: insertx()
* DESCRIPTION: recursively locate a node and insert a record
*/
static
int insertx(node *np, byte *record, int *reclen)
{
node child;
byte *rec;
int result = 0;
if (n_search(np, record))
ERROR(EIO, "b*-tree record already exists");
switch (np->nd.ndType)
{
case ndIndxNode:
if (np->rnum == -1)
rec = HFS_NODEREC(*np, 0);
else
rec = HFS_NODEREC(*np, np->rnum);
if (bt_getnode(&child, np->bt, d_getul(HFS_RECDATA(rec))) == -1 ||
insertx(&child, record, reclen) == -1)
goto fail;
if (np->rnum == -1)
{
n_index(&child, rec, 0);
if (*reclen == 0)
{
result = bt_putnode(np);
goto done;
}
}
if (*reclen)
result = n_insert(np, record, reclen);
break;
case ndLeafNode:
result = n_insert(np, record, reclen);
break;
default:
ERROR(EIO, "unexpected b*-tree node");
}
done:
return result;
fail:
return -1;
}
/*
* NAME: btree->insert()
* DESCRIPTION: insert a new node record into a tree
*/
int bt_insert(btree *bt, const byte *record, unsigned int reclen)
{
node root;
byte newrec[HFS_MAX_RECLEN];
if (bt->hdr.bthRoot == 0)
{
/* create root node */
n_init(&root, bt, ndLeafNode, 1);
if (n_new(&root) == -1 ||
bt_putnode(&root) == -1)
goto fail;
bt->hdr.bthDepth = 1;
bt->hdr.bthRoot = root.nnum;
bt->hdr.bthFNode = root.nnum;
bt->hdr.bthLNode = root.nnum;
bt->flags |= HFS_BT_UPDATE_HDR;
}
else if (bt_getnode(&root, bt, bt->hdr.bthRoot) == -1)
goto fail;
memcpy(newrec, record, reclen);
if (insertx(&root, newrec, &reclen) == -1)
goto fail;
if (reclen)
{
byte oroot[HFS_MAX_RECLEN];
unsigned int orootlen;
/* root node was split; create a new root */
n_index(&root, oroot, &orootlen);
n_init(&root, bt, ndIndxNode, root.nd.ndNHeight + 1);
if (n_new(&root) == -1)
goto fail;
++bt->hdr.bthDepth;
bt->hdr.bthRoot = root.nnum;
bt->flags |= HFS_BT_UPDATE_HDR;
/* insert index records for new root */
n_search(&root, oroot);
n_insertx(&root, oroot, orootlen);
n_search(&root, newrec);
n_insertx(&root, newrec, reclen);
if (bt_putnode(&root) == -1)
goto fail;
}
++bt->hdr.bthNRecs;
bt->flags |= HFS_BT_UPDATE_HDR;
return 0;
fail:
return -1;
}
/*
* NAME: deletex()
* DESCRIPTION: recursively locate a node and delete a record
*/
static
int deletex(node *np, const byte *key, byte *record, int *flag)
{
node child;
byte *rec;
int found, result = 0;
found = n_search(np, key);
switch (np->nd.ndType)
{
case ndIndxNode:
if (np->rnum == -1)
ERROR(EIO, "b*-tree record not found");
rec = HFS_NODEREC(*np, np->rnum);
if (bt_getnode(&child, np->bt, d_getul(HFS_RECDATA(rec))) == -1 ||
deletex(&child, key, rec, flag) == -1)
goto fail;
if (*flag)
{
*flag = 0;
if (HFS_RECKEYLEN(rec) == 0)
{
result = n_delete(np, record, flag);
break;
}
if (np->rnum == 0)
{
/* propagate index record change into parent */
n_index(np, record, 0);
*flag = 1;
}
result = bt_putnode(np);
}
break;
case ndLeafNode:
if (found == 0)
ERROR(EIO, "b*-tree record not found");
result = n_delete(np, record, flag);
break;
default:
ERROR(EIO, "unexpected b*-tree node");
}
return result;
fail:
return -1;
}
/*
* NAME: btree->delete()
* DESCRIPTION: remove a node record from a tree
*/
int bt_delete(btree *bt, const byte *key)
{
node root;
byte record[HFS_MAX_RECLEN];
int flag = 0;
if (bt->hdr.bthRoot == 0)
ERROR(EIO, "empty b*-tree");
if (bt_getnode(&root, bt, bt->hdr.bthRoot) == -1 ||
deletex(&root, key, record, &flag) == -1)
goto fail;
if (bt->hdr.bthDepth > 1 && root.nd.ndNRecs == 1)
{
const byte *rec;
/* root only has one record; eliminate it and decrease the tree depth */
rec = HFS_NODEREC(root, 0);
--bt->hdr.bthDepth;
bt->hdr.bthRoot = d_getul(HFS_RECDATA(rec));
if (n_free(&root) == -1)
goto fail;
}
else if (bt->hdr.bthDepth == 1 && root.nd.ndNRecs == 0)
{
/* root node was deleted */
bt->hdr.bthDepth = 0;
bt->hdr.bthRoot = 0;
}
--bt->hdr.bthNRecs;
bt->flags |= HFS_BT_UPDATE_HDR;
return 0;
fail:
return -1;
}
/*
* NAME: btree->search()
* DESCRIPTION: locate a data record given a search key
*/
int bt_search(btree *bt, const byte *key, node *np)
{
int found = 0;
unsigned long nnum;
nnum = bt->hdr.bthRoot;
if (nnum == 0)
ERROR(ENOENT, 0);
while (1)
{
const byte *rec;
if (bt_getnode(np, bt, nnum) == -1)
{
found = -1;
goto fail;
}
found = n_search(np, key);
switch (np->nd.ndType)
{
case ndIndxNode:
if (np->rnum == -1)
ERROR(ENOENT, 0);
rec = HFS_NODEREC(*np, np->rnum);
nnum = d_getul(HFS_RECDATA(rec));
break;
case ndLeafNode:
if (! found)
ERROR(ENOENT, 0);
goto done;
default:
found = -1;
ERROR(EIO, "unexpected b*-tree node");
}
}
done:
fail:
return found;
}

33
vendors/libhfs/btree.h vendored Normal file
View File

@ -0,0 +1,33 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: btree.h,v 1.8 1998/11/02 22:08:55 rob Exp $
*/
int bt_getnode(node *, btree *, unsigned long);
int bt_putnode(node *);
int bt_readhdr(btree *);
int bt_writehdr(btree *);
int bt_space(btree *, unsigned int);
int bt_insert(btree *, const byte *, unsigned int);
int bt_delete(btree *, const byte *);
int bt_search(btree *, const byte *, node *);

41
vendors/libhfs/config.h.in vendored Normal file
View File

@ -0,0 +1,41 @@
/* config.h.in. Generated automatically from configure.in by autoheader. */
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*****************************************************************************
* Definitions selected automatically by `configure' *
*****************************************************************************/
/* Define if you have the ANSI C header files. */
#cmakedefine STDC_HEADERS
/* Define if you want to enable diagnostic debugging support. */
#cmakedefine DEBUG
/* Define if you have the mktime function. */
#cmakedefine HAVE_MKTIME
/*****************************************************************************
* End of automatically configured definitions *
*****************************************************************************/
# ifdef DEBUG
# include <stdio.h>
# endif

481
vendors/libhfs/data.c vendored Normal file
View File

@ -0,0 +1,481 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: data.c,v 1.7 1998/11/02 22:08:57 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <string.h>
# include <time.h>
# include "data.h"
# define TIMEDIFF 2082844800UL
static
time_t tzdiff = -1;
const
unsigned char hfs_charorder[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x22, 0x23, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
0x47, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69,
0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f,
0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1,
0xa3, 0xa5, 0xa8, 0xaa, 0xab, 0xac, 0xad, 0xae,
0x54, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69,
0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f,
0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1,
0xa3, 0xa5, 0xa8, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
0x4c, 0x50, 0x5c, 0x62, 0x7d, 0x81, 0x9a, 0x55,
0x4a, 0x56, 0x4c, 0x4e, 0x50, 0x5c, 0x62, 0x64,
0x65, 0x66, 0x6f, 0x70, 0x71, 0x72, 0x7d, 0x89,
0x8a, 0x8b, 0x81, 0x83, 0x9c, 0x9d, 0x9e, 0x9a,
0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0x95,
0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0x52, 0x85,
0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
0xc9, 0xca, 0xcb, 0x57, 0x8c, 0xcc, 0x52, 0x85,
0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0x26,
0x27, 0xd4, 0x20, 0x4a, 0x4e, 0x83, 0x87, 0x87,
0xd5, 0xd6, 0x24, 0x25, 0x2d, 0x2e, 0xd7, 0xd8,
0xa7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
/*
* NAME: data->getsb()
* DESCRIPTION: marshal 1 signed byte into local host format
*/
signed char d_getsb(register const unsigned char *ptr)
{
return ptr[0];
}
/*
* NAME: data->getub()
* DESCRIPTION: marshal 1 unsigned byte into local host format
*/
unsigned char d_getub(register const unsigned char *ptr)
{
return ptr[0];
}
/*
* NAME: data->getsw()
* DESCRIPTION: marshal 2 signed bytes into local host format
*/
signed short d_getsw(register const unsigned char *ptr)
{
return
((( signed short) ptr[0] << 8) |
((unsigned short) ptr[1] << 0));
}
/*
* NAME: data->getuw()
* DESCRIPTION: marshal 2 unsigned bytes into local host format
*/
unsigned short d_getuw(register const unsigned char *ptr)
{
return
(((unsigned short) ptr[0] << 8) |
((unsigned short) ptr[1] << 0));
}
/*
* NAME: data->getsl()
* DESCRIPTION: marshal 4 signed bytes into local host format
*/
signed long d_getsl(register const unsigned char *ptr)
{
return
((( signed long) ptr[0] << 24) |
((unsigned long) ptr[1] << 16) |
((unsigned long) ptr[2] << 8) |
((unsigned long) ptr[3] << 0));
}
/*
* NAME: data->getul()
* DESCRIPTION: marshal 4 unsigned bytes into local host format
*/
unsigned long d_getul(register const unsigned char *ptr)
{
return
(((unsigned long) ptr[0] << 24) |
((unsigned long) ptr[1] << 16) |
((unsigned long) ptr[2] << 8) |
((unsigned long) ptr[3] << 0));
}
/*
* NAME: data->putsb()
* DESCRIPTION: marshal 1 signed byte out in big-endian format
*/
void d_putsb(register unsigned char *ptr,
register signed char data)
{
*ptr = data;
}
/*
* NAME: data->putub()
* DESCRIPTION: marshal 1 unsigned byte out in big-endian format
*/
void d_putub(register unsigned char *ptr,
register unsigned char data)
{
*ptr = data;
}
/*
* NAME: data->putsw()
* DESCRIPTION: marshal 2 signed bytes out in big-endian format
*/
void d_putsw(register unsigned char *ptr,
register signed short data)
{
*ptr++ = ((unsigned short) data & 0xff00) >> 8;
*ptr = ((unsigned short) data & 0x00ff) >> 0;
}
/*
* NAME: data->putuw()
* DESCRIPTION: marshal 2 unsigned bytes out in big-endian format
*/
void d_putuw(register unsigned char *ptr,
register unsigned short data)
{
*ptr++ = (data & 0xff00) >> 8;
*ptr = (data & 0x00ff) >> 0;
}
/*
* NAME: data->putsl()
* DESCRIPTION: marshal 4 signed bytes out in big-endian format
*/
void d_putsl(register unsigned char *ptr,
register signed long data)
{
*ptr++ = ((unsigned long) data & 0xff000000UL) >> 24;
*ptr++ = ((unsigned long) data & 0x00ff0000UL) >> 16;
*ptr++ = ((unsigned long) data & 0x0000ff00UL) >> 8;
*ptr = ((unsigned long) data & 0x000000ffUL) >> 0;
}
/*
* NAME: data->putul()
* DESCRIPTION: marshal 4 unsigned bytes out in big-endian format
*/
void d_putul(register unsigned char *ptr,
register unsigned long data)
{
*ptr++ = (data & 0xff000000UL) >> 24;
*ptr++ = (data & 0x00ff0000UL) >> 16;
*ptr++ = (data & 0x0000ff00UL) >> 8;
*ptr = (data & 0x000000ffUL) >> 0;
}
/*
* NAME: data->fetchsb()
* DESCRIPTION: incrementally retrieve a signed byte of data
*/
void d_fetchsb(register const unsigned char **ptr,
register signed char *dest)
{
*dest = *(*ptr)++;
}
/*
* NAME: data->fetchub()
* DESCRIPTION: incrementally retrieve an unsigned byte of data
*/
void d_fetchub(register const unsigned char **ptr,
register unsigned char *dest)
{
*dest = *(*ptr)++;
}
/*
* NAME: data->fetchsw()
* DESCRIPTION: incrementally retrieve a signed word of data
*/
void d_fetchsw(register const unsigned char **ptr,
register signed short *dest)
{
*dest =
((( signed short) (*ptr)[0] << 8) |
((unsigned short) (*ptr)[1] << 0));
*ptr += 2;
}
/*
* NAME: data->fetchuw()
* DESCRIPTION: incrementally retrieve an unsigned word of data
*/
void d_fetchuw(register const unsigned char **ptr,
register unsigned short *dest)
{
*dest =
(((unsigned short) (*ptr)[0] << 8) |
((unsigned short) (*ptr)[1] << 0));
*ptr += 2;
}
/*
* NAME: data->fetchsl()
* DESCRIPTION: incrementally retrieve a signed long word of data
*/
void d_fetchsl(register const unsigned char **ptr,
register signed long *dest)
{
*dest =
((( signed long) (*ptr)[0] << 24) |
((unsigned long) (*ptr)[1] << 16) |
((unsigned long) (*ptr)[2] << 8) |
((unsigned long) (*ptr)[3] << 0));
*ptr += 4;
}
/*
* NAME: data->fetchul()
* DESCRIPTION: incrementally retrieve an unsigned long word of data
*/
void d_fetchul(register const unsigned char **ptr,
register unsigned long *dest)
{
*dest =
(((unsigned long) (*ptr)[0] << 24) |
((unsigned long) (*ptr)[1] << 16) |
((unsigned long) (*ptr)[2] << 8) |
((unsigned long) (*ptr)[3] << 0));
*ptr += 4;
}
/*
* NAME: data->storesb()
* DESCRIPTION: incrementally store a signed byte of data
*/
void d_storesb(register unsigned char **ptr,
register signed char data)
{
*(*ptr)++ = data;
}
/*
* NAME: data->storeub()
* DESCRIPTION: incrementally store an unsigned byte of data
*/
void d_storeub(register unsigned char **ptr,
register unsigned char data)
{
*(*ptr)++ = data;
}
/*
* NAME: data->storesw()
* DESCRIPTION: incrementally store a signed word of data
*/
void d_storesw(register unsigned char **ptr,
register signed short data)
{
*(*ptr)++ = ((unsigned short) data & 0xff00) >> 8;
*(*ptr)++ = ((unsigned short) data & 0x00ff) >> 0;
}
/*
* NAME: data->storeuw()
* DESCRIPTION: incrementally store an unsigned word of data
*/
void d_storeuw(register unsigned char **ptr,
register unsigned short data)
{
*(*ptr)++ = (data & 0xff00) >> 8;
*(*ptr)++ = (data & 0x00ff) >> 0;
}
/*
* NAME: data->storesl()
* DESCRIPTION: incrementally store a signed long word of data
*/
void d_storesl(register unsigned char **ptr,
register signed long data)
{
*(*ptr)++ = ((unsigned long) data & 0xff000000UL) >> 24;
*(*ptr)++ = ((unsigned long) data & 0x00ff0000UL) >> 16;
*(*ptr)++ = ((unsigned long) data & 0x0000ff00UL) >> 8;
*(*ptr)++ = ((unsigned long) data & 0x000000ffUL) >> 0;
}
/*
* NAME: data->storeul()
* DESCRIPTION: incrementally store an unsigned long word of data
*/
void d_storeul(register unsigned char **ptr,
register unsigned long data)
{
*(*ptr)++ = (data & 0xff000000UL) >> 24;
*(*ptr)++ = (data & 0x00ff0000UL) >> 16;
*(*ptr)++ = (data & 0x0000ff00UL) >> 8;
*(*ptr)++ = (data & 0x000000ffUL) >> 0;
}
/*
* NAME: data->fetchstr()
* DESCRIPTION: incrementally retrieve a string
*/
void d_fetchstr(const unsigned char **ptr, char *dest, unsigned size)
{
unsigned len;
len = d_getub(*ptr);
if (len > 0 && len < size)
memcpy(dest, *ptr + 1, len);
else
len = 0;
dest[len] = 0;
*ptr += size;
}
/*
* NAME: data->storestr()
* DESCRIPTION: incrementally store a string
*/
void d_storestr(unsigned char **ptr, const char *src, unsigned size)
{
unsigned len;
len = strlen(src);
if (len > --size)
len = 0;
d_storeub(ptr, len);
memcpy(*ptr, src, len);
memset(*ptr + len, 0, size - len);
*ptr += size;
}
/*
* NAME: data->relstring()
* DESCRIPTION: compare two strings as per MacOS for HFS
*/
int d_relstring(const char *str1, const char *str2)
{
register int diff;
while (*str1 && *str2)
{
diff = hfs_charorder[(unsigned char) *str1] -
hfs_charorder[(unsigned char) *str2];
if (diff)
return diff;
++str1, ++str2;
}
if (! *str1 && *str2)
return -1;
else if (*str1 && ! *str2)
return 1;
return 0;
}
/*
* NAME: calctzdiff()
* DESCRIPTION: calculate the timezone difference between local time and UTC
*/
static
void calctzdiff(void)
{
# ifdef HAVE_MKTIME
time_t t;
int isdst;
struct tm tm;
const struct tm *tmp;
time(&t);
isdst = localtime(&t)->tm_isdst;
tmp = gmtime(&t);
if (tmp)
{
tm = *tmp;
tm.tm_isdst = isdst;
tzdiff = t - mktime(&tm);
}
else
tzdiff = 0;
# else
tzdiff = 0;
# endif
}
/*
* NAME: data->ltime()
* DESCRIPTION: convert MacOS time to local time
*/
time_t d_ltime(unsigned long mtime)
{
if (tzdiff == -1)
calctzdiff();
return (time_t) (mtime - TIMEDIFF) - tzdiff;
}
/*
* NAME: data->mtime()
* DESCRIPTION: convert local time to MacOS time
*/
unsigned long d_mtime(time_t ltime)
{
if (tzdiff == -1)
calctzdiff();
return (unsigned long) (ltime + tzdiff) + TIMEDIFF;
}

66
vendors/libhfs/data.h vendored Normal file
View File

@ -0,0 +1,66 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: data.h,v 1.7 1998/11/02 22:08:58 rob Exp $
*/
#ifdef __cplusplus
extern "C" {
#endif
extern const unsigned char hfs_charorder[];
signed char d_getsb(register const unsigned char *);
unsigned char d_getub(register const unsigned char *);
signed short d_getsw(register const unsigned char *);
unsigned short d_getuw(register const unsigned char *);
signed long d_getsl(register const unsigned char *);
unsigned long d_getul(register const unsigned char *);
void d_putsb(register unsigned char *, register signed char);
void d_putub(register unsigned char *, register unsigned char);
void d_putsw(register unsigned char *, register signed short);
void d_putuw(register unsigned char *, register unsigned short);
void d_putsl(register unsigned char *, register signed long);
void d_putul(register unsigned char *, register unsigned long);
void d_fetchsb(register const unsigned char **, register signed char *);
void d_fetchub(register const unsigned char **, register unsigned char *);
void d_fetchsw(register const unsigned char **, register signed short *);
void d_fetchuw(register const unsigned char **, register unsigned short *);
void d_fetchsl(register const unsigned char **, register signed long *);
void d_fetchul(register const unsigned char **, register unsigned long *);
void d_storesb(register unsigned char **, register signed char);
void d_storeub(register unsigned char **, register unsigned char);
void d_storesw(register unsigned char **, register signed short);
void d_storeuw(register unsigned char **, register unsigned short);
void d_storesl(register unsigned char **, register signed long);
void d_storeul(register unsigned char **, register unsigned long);
void d_fetchstr(const unsigned char **, char *, unsigned);
void d_storestr(unsigned char **, const char *, unsigned);
int d_relstring(const char *, const char *);
time_t d_ltime(unsigned long);
unsigned long d_mtime(time_t);
#ifdef __cplusplus
}
#endif

520
vendors/libhfs/file.c vendored Normal file
View File

@ -0,0 +1,520 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: file.c,v 1.9 1998/11/02 22:08:59 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <string.h>
# include <errno.h>
# include "libhfs.h"
# include "file.h"
# include "btree.h"
# include "record.h"
# include "volume.h"
/*
* NAME: file->init()
* DESCRIPTION: initialize file structure
*/
void f_init(hfsfile *file, hfsvol *vol, long cnid, const char *name)
{
int i;
file->vol = vol;
file->parid = 0;
strcpy(file->name, name);
file->cat.cdrType = cdrFilRec;
file->cat.cdrResrv2 = 0;
file->cat.u.fil.filFlags = 0;
file->cat.u.fil.filTyp = 0;
file->cat.u.fil.filUsrWds.fdType = 0;
file->cat.u.fil.filUsrWds.fdCreator = 0;
file->cat.u.fil.filUsrWds.fdFlags = 0;
file->cat.u.fil.filUsrWds.fdLocation.v = 0;
file->cat.u.fil.filUsrWds.fdLocation.h = 0;
file->cat.u.fil.filUsrWds.fdFldr = 0;
file->cat.u.fil.filFlNum = cnid;
file->cat.u.fil.filStBlk = 0;
file->cat.u.fil.filLgLen = 0;
file->cat.u.fil.filPyLen = 0;
file->cat.u.fil.filRStBlk = 0;
file->cat.u.fil.filRLgLen = 0;
file->cat.u.fil.filRPyLen = 0;
file->cat.u.fil.filCrDat = 0;
file->cat.u.fil.filMdDat = 0;
file->cat.u.fil.filBkDat = 0;
file->cat.u.fil.filFndrInfo.fdIconID = 0;
for (i = 0; i < 4; ++i)
file->cat.u.fil.filFndrInfo.fdUnused[i] = 0;
file->cat.u.fil.filFndrInfo.fdComment = 0;
file->cat.u.fil.filFndrInfo.fdPutAway = 0;
file->cat.u.fil.filClpSize = 0;
for (i = 0; i < 3; ++i)
{
file->cat.u.fil.filExtRec[i].xdrStABN = 0;
file->cat.u.fil.filExtRec[i].xdrNumABlks = 0;
file->cat.u.fil.filRExtRec[i].xdrStABN = 0;
file->cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
}
file->cat.u.fil.filResrv = 0;
f_selectfork(file, fkData);
file->flags = 0;
file->prev = 0;
file->next = 0;
}
/*
* NAME: file->selectfork()
* DESCRIPTION: choose a fork for file operations
*/
void f_selectfork(hfsfile *file, int fork)
{
file->fork = fork;
memcpy(&file->ext, fork == fkData ?
&file->cat.u.fil.filExtRec : &file->cat.u.fil.filRExtRec,
sizeof(ExtDataRec));
file->fabn = 0;
file->pos = 0;
}
/*
* NAME: file->getptrs()
* DESCRIPTION: make pointers to the current fork's lengths and extents
*/
void f_getptrs(hfsfile *file, ExtDataRec **extrec,
unsigned long **lglen, unsigned long **pylen)
{
if (file->fork == fkData)
{
if (extrec)
*extrec = &file->cat.u.fil.filExtRec;
if (lglen)
*lglen = &file->cat.u.fil.filLgLen;
if (pylen)
*pylen = &file->cat.u.fil.filPyLen;
}
else
{
if (extrec)
*extrec = &file->cat.u.fil.filRExtRec;
if (lglen)
*lglen = &file->cat.u.fil.filRLgLen;
if (pylen)
*pylen = &file->cat.u.fil.filRPyLen;
}
}
/*
* NAME: file->doblock()
* DESCRIPTION: read or write a numbered block from a file
*/
int f_doblock(hfsfile *file, unsigned long num, block *bp,
int (*func)(hfsvol *, unsigned int, unsigned int, block *))
{
unsigned int abnum;
unsigned int blnum;
unsigned int fabn;
int i;
abnum = num / file->vol->lpa;
blnum = num % file->vol->lpa;
/* locate the appropriate extent record */
fabn = file->fabn;
if (abnum < fabn)
{
ExtDataRec *extrec;
f_getptrs(file, &extrec, 0, 0);
fabn = file->fabn = 0;
memcpy(&file->ext, extrec, sizeof(ExtDataRec));
}
else
abnum -= fabn;
while (1)
{
unsigned int n;
for (i = 0; i < 3; ++i)
{
n = file->ext[i].xdrNumABlks;
if (abnum < n)
return func(file->vol, file->ext[i].xdrStABN + abnum, blnum, bp);
fabn += n;
abnum -= n;
}
if (v_extsearch(file, fabn, &file->ext, 0) <= 0)
goto fail;
file->fabn = fabn;
}
fail:
return -1;
}
/*
* NAME: file->addextent()
* DESCRIPTION: add an extent to a file
*/
int f_addextent(hfsfile *file, ExtDescriptor *blocks)
{
hfsvol *vol = file->vol;
ExtDataRec *extrec;
unsigned long *pylen;
unsigned int start, end;
node n;
int i;
f_getptrs(file, &extrec, 0, &pylen);
start = file->fabn;
end = *pylen / vol->mdb.drAlBlkSiz;
n.nnum = 0;
i = -1;
while (start < end)
{
for (i = 0; i < 3; ++i)
{
unsigned int num;
num = file->ext[i].xdrNumABlks;
start += num;
if (start == end)
break;
else if (start > end)
ERROR(EIO, "file extents exceed file physical length");
else if (num == 0)
ERROR(EIO, "empty file extent");
}
if (start == end)
break;
if (v_extsearch(file, start, &file->ext, &n) <= 0)
goto fail;
file->fabn = start;
}
if (i >= 0 &&
file->ext[i].xdrStABN + file->ext[i].xdrNumABlks == blocks->xdrStABN)
file->ext[i].xdrNumABlks += blocks->xdrNumABlks;
else
{
/* create a new extent descriptor */
if (++i < 3)
file->ext[i] = *blocks;
else
{
ExtKeyRec key;
byte record[HFS_MAX_EXTRECLEN];
unsigned int reclen;
/* record is full; create a new one */
file->ext[0] = *blocks;
for (i = 1; i < 3; ++i)
{
file->ext[i].xdrStABN = 0;
file->ext[i].xdrNumABlks = 0;
}
file->fabn = start;
r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, end);
r_packextrec(&key, &file->ext, record, &reclen);
if (bt_insert(&vol->ext, record, reclen) == -1)
goto fail;
i = -1;
}
}
if (i >= 0)
{
/* store the modified extent record */
if (file->fabn)
{
if ((n.nnum == 0 &&
v_extsearch(file, file->fabn, 0, &n) <= 0) ||
v_putextrec(&file->ext, &n) == -1)
goto fail;
}
else
memcpy(extrec, &file->ext, sizeof(ExtDataRec));
}
*pylen += blocks->xdrNumABlks * vol->mdb.drAlBlkSiz;
file->flags |= HFS_FILE_UPDATE_CATREC;
return 0;
fail:
return -1;
}
/*
* NAME: file->alloc()
* DESCRIPTION: reserve allocation blocks for a file
*/
long f_alloc(hfsfile *file)
{
hfsvol *vol = file->vol;
unsigned long clumpsz;
ExtDescriptor blocks;
clumpsz = file->cat.u.fil.filClpSize;
if (clumpsz == 0)
{
if (file == &vol->ext.f)
clumpsz = vol->mdb.drXTClpSiz;
else if (file == &vol->cat.f)
clumpsz = vol->mdb.drCTClpSiz;
else
clumpsz = vol->mdb.drClpSiz;
}
blocks.xdrNumABlks = clumpsz / vol->mdb.drAlBlkSiz;
if (v_allocblocks(vol, &blocks) == -1)
goto fail;
if (f_addextent(file, &blocks) == -1)
{
v_freeblocks(vol, &blocks);
goto fail;
}
return blocks.xdrNumABlks;
fail:
return -1;
}
/*
* NAME: file->trunc()
* DESCRIPTION: release allocation blocks unneeded by a file
*/
int f_trunc(hfsfile *file)
{
hfsvol *vol = file->vol;
ExtDataRec *extrec;
unsigned long *lglen, *pylen, alblksz, newpylen;
unsigned int dlen, start, end;
node n;
int i;
if (vol->flags & HFS_VOL_READONLY)
goto done;
f_getptrs(file, &extrec, &lglen, &pylen);
alblksz = vol->mdb.drAlBlkSiz;
newpylen = (*lglen / alblksz + (*lglen % alblksz != 0)) * alblksz;
if (newpylen > *pylen)
ERROR(EIO, "file size exceeds physical length");
else if (newpylen == *pylen)
goto done;
dlen = (*pylen - newpylen) / alblksz;
start = file->fabn;
end = newpylen / alblksz;
if (start >= end)
{
start = file->fabn = 0;
memcpy(&file->ext, extrec, sizeof(ExtDataRec));
}
n.nnum = 0;
i = -1;
while (start < end)
{
for (i = 0; i < 3; ++i)
{
unsigned int num;
num = file->ext[i].xdrNumABlks;
start += num;
if (start >= end)
break;
else if (num == 0)
ERROR(EIO, "empty file extent");
}
if (start >= end)
break;
if (v_extsearch(file, start, &file->ext, &n) <= 0)
goto fail;
file->fabn = start;
}
if (start > end)
{
ExtDescriptor blocks;
file->ext[i].xdrNumABlks -= start - end;
dlen -= start - end;
blocks.xdrStABN = file->ext[i].xdrStABN + file->ext[i].xdrNumABlks;
blocks.xdrNumABlks = start - end;
if (v_freeblocks(vol, &blocks) == -1)
goto fail;
}
*pylen = newpylen;
file->flags |= HFS_FILE_UPDATE_CATREC;
do
{
while (dlen && ++i < 3)
{
unsigned int num;
num = file->ext[i].xdrNumABlks;
start += num;
if (num == 0)
ERROR(EIO, "empty file extent");
else if (num > dlen)
ERROR(EIO, "file extents exceed physical size");
dlen -= num;
if (v_freeblocks(vol, &file->ext[i]) == -1)
goto fail;
file->ext[i].xdrStABN = 0;
file->ext[i].xdrNumABlks = 0;
}
if (file->fabn)
{
if (n.nnum == 0 &&
v_extsearch(file, file->fabn, 0, &n) <= 0)
goto fail;
if (file->ext[0].xdrNumABlks)
{
if (v_putextrec(&file->ext, &n) == -1)
goto fail;
}
else
{
if (bt_delete(&vol->ext, HFS_NODEREC(n, n.rnum)) == -1)
goto fail;
n.nnum = 0;
}
}
else
memcpy(extrec, &file->ext, sizeof(ExtDataRec));
if (dlen)
{
if (v_extsearch(file, start, &file->ext, &n) <= 0)
goto fail;
file->fabn = start;
i = -1;
}
}
while (dlen);
done:
return 0;
fail:
return -1;
}
/*
* NAME: file->flush()
* DESCRIPTION: flush all pending changes to an open file
*/
int f_flush(hfsfile *file)
{
hfsvol *vol = file->vol;
if (vol->flags & HFS_VOL_READONLY)
goto done;
if (file->flags & HFS_FILE_UPDATE_CATREC)
{
node n;
file->cat.u.fil.filStBlk = file->cat.u.fil.filExtRec[0].xdrStABN;
file->cat.u.fil.filRStBlk = file->cat.u.fil.filRExtRec[0].xdrStABN;
if (v_catsearch(vol, file->parid, file->name, 0, 0, &n) <= 0 ||
v_putcatrec(&file->cat, &n) == -1)
goto fail;
file->flags &= ~HFS_FILE_UPDATE_CATREC;
}
done:
return 0;
fail:
return -1;
}

45
vendors/libhfs/file.h vendored Normal file
View File

@ -0,0 +1,45 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: file.h,v 1.6 1998/04/11 08:27:12 rob Exp $
*/
enum {
fkData = 0x00,
fkRsrc = 0xff
};
void f_init(hfsfile *, hfsvol *, long, const char *);
void f_selectfork(hfsfile *, int);
void f_getptrs(hfsfile *, ExtDataRec **, unsigned long **, unsigned long **);
int f_doblock(hfsfile *, unsigned long, block *,
int (*)(hfsvol *, unsigned int, unsigned int, block *));
# define f_getblock(file, num, bp) \
f_doblock((file), (num), (bp), b_readab)
# define f_putblock(file, num, bp) \
f_doblock((file), (num), (bp), \
(int (*)(hfsvol *, unsigned int, unsigned int, block *)) \
b_writeab)
int f_addextent(hfsfile *, ExtDescriptor *);
long f_alloc(hfsfile *);
int f_trunc(hfsfile *);
int f_flush(hfsfile *);

1848
vendors/libhfs/hfs.c vendored Normal file

File diff suppressed because it is too large Load Diff

188
vendors/libhfs/hfs.h vendored Normal file
View File

@ -0,0 +1,188 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: hfs.h,v 1.11 1998/11/02 22:09:01 rob Exp $
*/
#ifdef __cplusplus
extern "C" {
#endif
# include <time.h>
# define HFS_BLOCKSZ 512
# define HFS_BLOCKSZ_BITS 9
# define HFS_MAX_FLEN 31
# define HFS_MAX_VLEN 27
typedef struct _hfsvol_ hfsvol;
typedef struct _hfsfile_ hfsfile;
typedef struct _hfsdir_ hfsdir;
typedef struct {
char name[HFS_MAX_VLEN + 1]; /* name of volume (MacOS Standard Roman) */
int flags; /* volume flags */
unsigned long totbytes; /* total bytes on volume */
unsigned long freebytes; /* free bytes on volume */
unsigned long alblocksz; /* volume allocation block size */
unsigned long clumpsz; /* default file clump size */
unsigned long numfiles; /* number of files in volume */
unsigned long numdirs; /* number of directories in volume */
time_t crdate; /* volume creation date */
time_t mddate; /* last volume modification date */
time_t bkdate; /* last volume backup date */
unsigned long blessed; /* CNID of MacOS System Folder */
} hfsvolent;
typedef struct {
char name[HFS_MAX_FLEN + 1]; /* catalog name (MacOS Standard Roman) */
int flags; /* bit flags */
unsigned long cnid; /* catalog node id (CNID) */
unsigned long parid; /* CNID of parent directory */
time_t crdate; /* date of creation */
time_t mddate; /* date of last modification */
time_t bkdate; /* date of last backup */
short fdflags; /* Macintosh Finder flags */
struct {
signed short v; /* Finder icon vertical coordinate */
signed short h; /* horizontal coordinate */
} fdlocation;
union {
struct {
unsigned long dsize; /* size of data fork */
unsigned long rsize; /* size of resource fork */
char type[5]; /* file type code (plus null) */
char creator[5]; /* file creator code (plus null) */
} file;
struct {
unsigned short valence; /* number of items in directory */
struct {
signed short top; /* top edge of folder's rectangle */
signed short left; /* left edge */
signed short bottom; /* bottom edge */
signed short right; /* right edge */
} rect;
} dir;
} u;
} hfsdirent;
# define HFS_ISDIR 0x0001
# define HFS_ISLOCKED 0x0002
# define HFS_CNID_ROOTPAR 1
# define HFS_CNID_ROOTDIR 2
# define HFS_CNID_EXT 3
# define HFS_CNID_CAT 4
# define HFS_CNID_BADALLOC 5
# define HFS_FNDR_ISONDESK (1 << 0)
# define HFS_FNDR_COLOR 0x0e
# define HFS_FNDR_COLORRESERVED (1 << 4)
# define HFS_FNDR_REQUIRESSWITCHLAUNCH (1 << 5)
# define HFS_FNDR_ISSHARED (1 << 6)
# define HFS_FNDR_HASNOINITS (1 << 7)
# define HFS_FNDR_HASBEENINITED (1 << 8)
# define HFS_FNDR_RESERVED (1 << 9)
# define HFS_FNDR_HASCUSTOMICON (1 << 10)
# define HFS_FNDR_ISSTATIONERY (1 << 11)
# define HFS_FNDR_NAMELOCKED (1 << 12)
# define HFS_FNDR_HASBUNDLE (1 << 13)
# define HFS_FNDR_ISINVISIBLE (1 << 14)
# define HFS_FNDR_ISALIAS (1 << 15)
extern const char *hfs_error;
extern const unsigned char hfs_charorder[];
# define HFS_MODE_RDONLY 0
# define HFS_MODE_RDWR 1
# define HFS_MODE_ANY 2
# define HFS_MODE_MASK 0x0003
# define HFS_OPT_NOCACHE 0x0100
# define HFS_OPT_2048 0x0200
# define HFS_OPT_ZERO 0x0400
# define HFS_SEEK_SET 0
# define HFS_SEEK_CUR 1
# define HFS_SEEK_END 2
hfsvol *hfs_mount(const char *, int, int);
int hfs_flush(hfsvol *);
void hfs_flushall(void);
int hfs_umount(hfsvol *);
void hfs_umountall(void);
hfsvol *hfs_getvol(const char *);
void hfs_setvol(hfsvol *);
int hfs_vstat(hfsvol *, hfsvolent *);
int hfs_vsetattr(hfsvol *, hfsvolent *);
int hfs_chdir(hfsvol *, const char *);
unsigned long hfs_getcwd(hfsvol *);
int hfs_setcwd(hfsvol *, unsigned long);
int hfs_dirinfo(hfsvol *, unsigned long *, char *);
hfsdir *hfs_opendir(hfsvol *, const char *);
int hfs_readdir(hfsdir *, hfsdirent *);
int hfs_closedir(hfsdir *);
hfsfile *hfs_create(hfsvol *, const char *, const char *, const char *);
hfsfile *hfs_open(hfsvol *, const char *);
int hfs_setfork(hfsfile *, int);
int hfs_getfork(hfsfile *);
unsigned long hfs_read(hfsfile *, void *, unsigned long);
unsigned long hfs_write(hfsfile *, const void *, unsigned long);
int hfs_truncate(hfsfile *, unsigned long);
unsigned long hfs_seek(hfsfile *, long, int);
int hfs_close(hfsfile *);
int hfs_stat(hfsvol *, const char *, hfsdirent *);
int hfs_fstat(hfsfile *, hfsdirent *);
int hfs_setattr(hfsvol *, const char *, const hfsdirent *);
int hfs_fsetattr(hfsfile *, const hfsdirent *);
int hfs_mkdir(hfsvol *, const char *);
int hfs_rmdir(hfsvol *, const char *);
int hfs_delete(hfsvol *, const char *);
int hfs_rename(hfsvol *, const char *, const char *);
int hfs_zero(const char *, unsigned int, unsigned long *);
int hfs_mkpart(const char *, unsigned long);
int hfs_nparts(const char *);
int hfs_format(const char *, int, int,
const char *, unsigned int, const unsigned long []);
#ifdef __cplusplus
}
#endif

226
vendors/libhfs/libhfs.h vendored Normal file
View File

@ -0,0 +1,226 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: libhfs.h,v 1.7 1998/11/02 22:09:02 rob Exp $
*/
# include "hfs.h"
# include "apple.h"
extern int errno;
# define ERROR(code, str) \
do { hfs_error = (str), errno = (code); goto fail; } while (0)
# ifdef DEBUG
# define ASSERT(cond) do { if (! (cond)) abort(); } while (0)
# else
# define ASSERT(cond) /* nothing */
# endif
# define SIZE(type, n) ((size_t) (sizeof(type) * (n)))
# define ALLOC(type, n) ((type *) malloc(SIZE(type, n)))
# define ALLOCX(type, n) ((n) ? ALLOC(type, n) : (type *) 0)
# define FREE(ptr) ((ptr) ? (void) free((void *) ptr) : (void) 0)
# define REALLOC(ptr, type, n) \
((type *) ((ptr) ? realloc(ptr, SIZE(type, n)) : malloc(SIZE(type, n))))
# define REALLOCX(ptr, type, n) \
((n) ? REALLOC(ptr, type, n) : (FREE(ptr), (type *) 0))
# define BMTST(bm, num) \
(((const byte *) (bm))[(num) >> 3] & (0x80 >> ((num) & 0x07)))
# define BMSET(bm, num) \
(((byte *) (bm))[(num) >> 3] |= (0x80 >> ((num) & 0x07)))
# define BMCLR(bm, num) \
(((byte *) (bm))[(num) >> 3] &= ~(0x80 >> ((num) & 0x07)))
# define STRINGIZE(x) #x
# define STR(x) STRINGIZE(x)
typedef unsigned char byte;
typedef byte block[HFS_BLOCKSZ];
typedef struct _bucket_ {
int flags; /* bit flags */
unsigned int count; /* number of times this block is requested */
unsigned long bnum; /* logical block number */
block *data; /* pointer to block contents */
struct _bucket_ *cnext; /* next bucket in cache chain */
struct _bucket_ *cprev; /* previous bucket in cache chain */
struct _bucket_ *hnext; /* next bucket in hash chain */
struct _bucket_ **hprev; /* previous bucket's pointer to this bucket */
} bucket;
# define HFS_BUCKET_INUSE 0x01
# define HFS_BUCKET_DIRTY 0x02
# define HFS_CACHESZ 128
# define HFS_HASHSZ 32
# define HFS_BLOCKBUFSZ 16
typedef struct {
struct _hfsvol_ *vol; /* volume to which cache belongs */
bucket *tail; /* end of bucket chain */
unsigned int hits; /* number of cache hits */
unsigned int misses; /* number of cache misses */
bucket chain[HFS_CACHESZ]; /* cache bucket chain */
bucket *hash[HFS_HASHSZ]; /* hash table for bucket chain */
block pool[HFS_CACHESZ]; /* physical blocks in cache */
} bcache;
# define HFS_MAP1SZ 256
# define HFS_MAPXSZ 492
# define HFS_NODEREC(nd, rnum) ((nd).data + (nd).roff[rnum])
# define HFS_RECLEN(nd, rnum) ((nd).roff[(rnum) + 1] - (nd).roff[rnum])
# define HFS_RECKEYLEN(ptr) (*(const byte *) (ptr))
# define HFS_RECKEYSKIP(ptr) ((size_t) ((1 + HFS_RECKEYLEN(ptr) + 1) & ~1))
# define HFS_RECDATA(ptr) ((ptr) + HFS_RECKEYSKIP(ptr))
# define HFS_SETKEYLEN(ptr, x) (*(byte *) (ptr) = (x))
# define HFS_CATDATALEN sizeof(CatDataRec)
# define HFS_EXTDATALEN sizeof(ExtDataRec)
# define HFS_MAX_DATALEN (HFS_CATDATALEN > HFS_EXTDATALEN ? \
HFS_CATDATALEN : HFS_EXTDATALEN)
# define HFS_CATKEYLEN sizeof(CatKeyRec)
# define HFS_EXTKEYLEN sizeof(ExtKeyRec)
# define HFS_MAX_KEYLEN (HFS_CATKEYLEN > HFS_EXTKEYLEN ? \
HFS_CATKEYLEN : HFS_EXTKEYLEN)
# define HFS_MAX_CATRECLEN (HFS_CATKEYLEN + HFS_CATDATALEN)
# define HFS_MAX_EXTRECLEN (HFS_EXTKEYLEN + HFS_EXTDATALEN)
# define HFS_MAX_RECLEN (HFS_MAX_KEYLEN + HFS_MAX_DATALEN)
# define HFS_SIGWORD 0x4244
# define HFS_SIGWORD_MFS ((Integer) 0xd2d7)
# define HFS_ATRB_BUSY (1 << 6)
# define HFS_ATRB_HLOCKED (1 << 7)
# define HFS_ATRB_UMOUNTED (1 << 8)
# define HFS_ATRB_BBSPARED (1 << 9)
# define HFS_ATRB_BVINCONSIS (1 << 11)
# define HFS_ATRB_COPYPROT (1 << 14)
# define HFS_ATRB_SLOCKED (1 << 15)
struct _hfsfile_ {
struct _hfsvol_ *vol; /* pointer to volume descriptor */
unsigned long parid; /* parent directory ID of this file */
char name[HFS_MAX_FLEN + 1]; /* catalog name of this file */
CatDataRec cat; /* catalog information */
ExtDataRec ext; /* current extent record */
unsigned int fabn; /* starting file allocation block number */
int fork; /* current selected fork for I/O */
unsigned long pos; /* current file seek pointer */
int flags; /* bit flags */
struct _hfsfile_ *prev;
struct _hfsfile_ *next;
};
# define HFS_FILE_UPDATE_CATREC 0x01
# define HFS_MAX_NRECS 35 /* maximum based on minimum record size */
typedef struct _node_ {
struct _btree_ *bt; /* btree to which this node belongs */
unsigned long nnum; /* node index */
NodeDescriptor nd; /* node descriptor */
int rnum; /* current record index */
UInteger roff[HFS_MAX_NRECS + 1];
/* record offsets */
block data; /* raw contents of node */
} node;
struct _hfsdir_ {
struct _hfsvol_ *vol; /* associated volume */
unsigned long dirid; /* directory ID of interest (or 0) */
node n; /* current B*-tree node */
struct _hfsvol_ *vptr; /* current volume pointer */
struct _hfsdir_ *prev;
struct _hfsdir_ *next;
};
typedef void (*keyunpackfunc)(const byte *, void *);
typedef int (*keycomparefunc)(const void *, const void *);
typedef struct _btree_ {
hfsfile f; /* subset file information */
node hdrnd; /* header node */
BTHdrRec hdr; /* header record */
byte *map; /* usage bitmap */
unsigned long mapsz; /* number of bytes in bitmap */
int flags; /* bit flags */
keyunpackfunc keyunpack; /* key unpacking function */
keycomparefunc keycompare; /* key comparison function */
} btree;
# define HFS_BT_UPDATE_HDR 0x01
struct _hfsvol_ {
void *priv; /* OS-dependent private descriptor data */
int flags; /* bit flags */
int pnum; /* ordinal HFS partition number */
unsigned long vstart; /* logical block offset to start of volume */
unsigned long vlen; /* number of logical blocks in volume */
unsigned int lpa; /* number of logical blocks per allocation block */
bcache *cache; /* cache of recently used blocks */
MDB mdb; /* master directory block */
block *vbm; /* volume bitmap */
unsigned short vbmsz; /* number of blocks in bitmap */
btree ext; /* B*-tree control block for extents overflow file */
btree cat; /* B*-tree control block for catalog file */
unsigned long cwd; /* directory id of current working directory */
int refs; /* number of external references to this volume */
hfsfile *files; /* list of open files */
hfsdir *dirs; /* list of open directories */
struct _hfsvol_ *prev;
struct _hfsvol_ *next;
};
# define HFS_VOL_OPEN 0x0001
# define HFS_VOL_MOUNTED 0x0002
# define HFS_VOL_READONLY 0x0004
# define HFS_VOL_USINGCACHE 0x0008
# define HFS_VOL_UPDATE_MDB 0x0010
# define HFS_VOL_UPDATE_ALTMDB 0x0020
# define HFS_VOL_UPDATE_VBM 0x0040
# define HFS_VOL_OPT_MASK 0xff00
extern hfsvol *hfs_mounts;

516
vendors/libhfs/libhfs.txt vendored Normal file
View File

@ -0,0 +1,516 @@
This file documents the libhfs.a library for accessing HFS volumes.
Copyright (C) 1996-1998 Robert Leslie
$Id: libhfs.txt,v 1.11 1998/11/02 22:08:47 rob Exp $
===============================================================================
Exported Data
const char *hfs_error;
This contains a pointer to a C string describing the last HFS error.
It is generally only valid after an HFS routine has returned an error
code (-1 or a NULL pointer).
This string is encoded using ISO 8859-1.
In all cases when an error occurs, the global variable `errno' is also
set to an appropriate value.
unsigned char hfs_charorder[];
This array contains the relative sorting order of characters in HFS
filenames according to the semantics of the Macintosh character set
and the MacOS string comparison routines as used by HFS. The array can
be indexed by unsigned character quantities; the resulting value can be
compared to other array values to determine the relative sorting order
of the corresponding character indices.
Public Routines
----- Volume Routines -----
hfsvol *hfs_mount(const char *path, int pnum, int flags);
This routine attempts to open an HFS volume from a source pathname. The
given `pnum' indicates which ordinal HFS partition is to be mounted,
or can be 0 to indicate the entire medium should be mounted (ignoring
any partition structure). If this value is not 0, the requested
partition must exist.
The `flags' argument specifies how the volume should be mounted.
HFS_MODE_RDONLY means the volume should be mounted read-only.
HFS_MODE_RDWR means the volume must be opened read/write. HFS_MODE_ANY
means the volume can be mounted either read-only or read/write, with
preference for the latter.
The `flags' argument may also specify volume options. HFS_OPT_NOCACHE
means not to perform any internal block caching, such as would be
unnecessary for a volume residing in RAM, or if the associated overhead
is not desired. HFS_OPT_ZERO means that newly-allocated blocks should be
zero-initialized before use, primarily as a security feature for systems
on which blocks may otherwise contain random data. Neither of these
options should normally be necessary, and both may affect performance.
If an error occurs, this function returns NULL. Otherwise a pointer to a
volume structure is returned. This pointer is used to access the volume
and must eventually be passed to hfs_umount() to flush and close the
volume and free all associated memory.
int hfs_flush(hfsvol *vol);
This routine causes all pending changes to be flushed to an HFS volume.
If a volume is kept open for a long period of time, it would be wise
to call this periodically to avoid corrupting the volume due to
unforeseen circumstances (power failure, floppy eject, etc.)
If an error occurs, this function returns -1. Otherwise it returns 0.
void hfs_flushall(void);
This routine is similar to hfs_flush() except that all mounted volumes
are flushed, and errors are not reported.
int hfs_umount(hfsvol *vol);
The specified HFS volume is unmounted; all open files and directories
on the volume are closed, all pending changes to the volume are
flushed, and all memory allocated for the volume is freed.
All volumes opened with hfs_mount() must eventually be closed with
hfs_umount(), or they will risk corruption.
If an error occurs, this function returns -1. Otherwise it returns 0.
In either case, the volume structure pointer will become invalid, as
will all pointers to open file or directory structures associated with
the volume.
void hfs_umountall(void);
This routine is similar to hfs_umount() except that all mounted volumes
are closed, and errors are not reported.
This routine may be useful to call just before a process terminates to
make sure any remaining open volumes are properly closed.
hfsvol *hfs_getvol(const char *name);
This routines searches all mounted volumes for one having the given
`name', and returns its volume structure pointer. If more than one
volume have the same name, the most recently mounted one is returned. If
no volume matches the given name, a NULL pointer is returned.
The given `name' is assumed to be encoded using MacOS Standard Roman.
If a NULL pointer is passed to this routine, the current volume is
returned, if any.
void hfs_setvol(hfsvol *vol);
The routine changes the "current" volume. Most HFS routines will accept
a NULL volume pointer to mean the current volume; by default, the
current volume is the last one which was mounted.
int hfs_vstat(hfsvol *vol, hfsvolent *ent);
This routine fills the volume entity structure `*ent' with information
about a mounted volume. The fields of the structure are defined in
the hfs.h header file.
This routine returns 0 unless a NULL pointer is passed for the volume
and no volume is current, in which case it returns -1.
int hfs_vsetattr(hfsvol *vol, hfsvolent *ent);
This routine allows some attributes of a volume to be changed. The
attributes which may be changed are: ent->clumpsz, ent->crdate,
ent->mddate, ent->bkdate, and ent->blessed. Note that the default file
clump size may only be changed to be a multiple of the volume's
allocation block size, and the "blessed" folder must either be 0 or a
valid folder CNID.
To change the volume's name, use hfs_rename().
If an error occurs, this function returns -1. Otherwise it returns 0.
----- Directory Routines -----
int hfs_chdir(hfsvol *vol, const char *path);
The "current working directory" for the given volume is changed.
`path' can be either a relative or absolute HFS path.
The given `path' is assumed to be encoded using MacOS Standard Roman.
If an error occurs, this function returns -1. Otherwise it returns 0.
long hfs_getcwd(hfsvol *vol);
The internal directory ID of the current working directory for the
given volume is returned. This value is typically only useful for
passing to hfs_setcwd() or hfs_dirinfo().
int hfs_setcwd(hfsvol *vol, long id);
This routine changes the current working directory for the given
volume. A directory must exist with the given id.
If an error occurs, this function returns -1. Otherwise it returns 0.
int hfs_dirinfo(hfsvol *vol, long *id, char *name);
This function looks up the given directory ID `*id' and stores in its
place the directory ID of its parent. If `name' is not NULL, the name
of the (child) directory is also stored in the buffer pointed to by it,
which must be at least HFS_MAX_FLEN + 1 (32) bytes long.
The string `name' will be encoded using MacOS Standard Roman.
If an error occurs, this function returns -1. Otherwise it returns 0.
This function can be called repeatedly to construct a full pathname
to the current working directory. The root directory of a volume
always has a directory ID of HFS_CNID_ROOTDIR, and a pseudo-parent ID
of HFS_CNID_ROOTPAR.
hfsdir *hfs_opendir(hfsvol *vol, const char *path);
This function prepares to read the contents of a directory. `path'
must be either an absolute or relative pathname to the desired HFS
directory. As a special case, if `path' is an empty string, a
"meta-directory" will be opened containing the root directories from
all of the currently mounted volumes.
The string `path' is assumed to be encoded using MacOS Standard Roman.
This function returns a pointer which must be passed to the other
directory-related routines to read the directory.
If an error occurs, this function returns a NULL pointer.
int hfs_readdir(hfsdir *dir, hfsdirent *ent);
This routine fills the directory entity structure `*ent' with
information about the next item in the given open directory. The
fields of the structure are defined in the hfs.h header file.
If an error occurs, this function returns -1. Otherwise it returns 0.
When no more items occur in the directory, this function returns -1
and sets `errno' to ENOENT.
int hfs_closedir(hfsdir *dir);
This function closes an open directory and frees all associated
memory.
If an error occurs, this function returns -1. Otherwise it returns 0.
In either case, the directory structure pointer will no longer be valid.
----- File Routines -----
hfsfile *hfs_create(hfsvol *vol, const char *path,
const char *type, const char *creator);
This routine creates a new, empty file with the given path, type, and
creator. The type and creator must be strings of length 4, and have
particular meaning under MacOS.
The given `path' is assumed to be encoded using MacOS Standard Roman.
If the creation is successful, the file is opened and a pointer to a
file structure is returned, the same as if hfs_open() had been called.
If an error occurs, this function returns a NULL pointer.
hfsfile *hfs_open(hfsvol *vol, const char *path);
This function opens an HFS file in preparation for I/O. Both forks of
the file may be manipulated once the file is opened; hfs_setfork() is
used to select the current fork. By default, the data fork is current.
The given `path' is assumed to be encoded using MacOS Standard Roman.
A pointer to a file structure is returned. This pointer should be
passed to other routines to manipulate the file.
If an error occurs, this function returns a NULL pointer.
int hfs_setfork(hfsfile *file, int fork);
This routine selects the current fork in an open file for I/O. HFS
files have two forks, data and resource. Resource forks normally contain
structured data, although these HFS routines make no distinction
between forks when reading or writing. It is up to higher-level
applications to make sense of the information read or written from
either fork.
If 0 is passed to this routine, the data fork is selected. Otherwise
the resource fork is selected. The seek pointer for the file is
automatically reset to the beginning of the newly selected fork.
As a side effect, this routine causes any excess disk blocks allocated
for the fork which was current before the call to be freed; normally
extra blocks are allocated during file writes to promote contiguity.
This routine will return -1 if an error occurs in this process;
otherwise it will return 0. The current fork will have been changed
regardless.
int hfs_getfork(hfsfile *file);
This routine returns an indication of which fork is currently active
for I/O operations on the given file. If 0 is returned, the data fork
is selected. Otherwise the resource fork is selected.
long hfs_read(hfsfile *file, void *ptr, unsigned long len);
This routine reads up to `len' bytes from the current fork of an HFS
file and places them into the buffer pointed to by `ptr' (which must be
at least `len' bytes long.) The number of bytes actually read is
returned, and may be less than `len' if the end of the file is reached.
If this routine returns 0, there is no more data to be read from the
file. If an error occurs, this routine will return -1.
It is most efficient to read data in multiples of HFS_BLOCKSZ byte
blocks at a time.
long hfs_write(hfsfile *file, const void *ptr, unsigned long len);
This routine writes up to `len' bytes of data to the current fork of an
HFS file from the buffer pointed to by `ptr'. The number of bytes
actually written is returned. If an error occurs, this routine will
return -1.
If the end of the file is reached before all bytes have been written,
the file is automatically extended.
It is most efficient to write data in multiples of HFS_BLOCKSZ byte
blocks at a time.
int hfs_truncate(hfsfile *file, unsigned long len);
This routine causes the current fork of the specified open file to be
truncated to at most `len' bytes.
The disk blocks associated with the freed portion of the file are not
actually deallocated until either the current fork is changed or the
file is closed.
If an error occurs, this function returns -1. Otherwise it returns 0.
long hfs_seek(hfsfile *file, long offset, int from);
This routine changes the current seek pointer for the specified open
file. This pointer determines where the next call to hfs_read() or
hfs_write() will read or write data within the current fork.
If `from' is HFS_SEEK_SET, the pointer is set to the absolute position
given by `offset'.
If `from' is HFS_SEEK_CUR, the pointer is offset from its current
position by the amount `offset'. Positive offsets seek forward; negative
offsets seek backward.
If `from' is HFS_SEEK_END, the pointer is offset from the end of the
file by the amount `offset', which ought not be positive.
It is not presently possible to set the seek pointer beyond the logical
end of the file.
The new absolute position of the seek pointer is returned, unless an
invalid argument was specified, in which case -1 is returned.
int hfs_close(hfsfile *file);
This routine causes all pending changes to the specified file to be
flushed, and all storage associated with the file structure to be
freed. Any excess disk blocks associated with the file are also
deallocated at this time.
If an error occurs, this routine returns -1. Otherwise it returns 0.
In either case, the file structure pointer will no longer be valid.
----- Catalog Routines -----
int hfs_stat(hfsvol *vol, const char *path, hfsdirent *ent);
This routine fills the directory entity structure `*ent' with
information about the file or directory specified by `path' on the
given volume. The fields of the structure are defined in the hfs.h
header file.
The given `path' is assumed to be encoded using MacOS Standard Roman.
If there is no such path, or if another error occurs, this routine
returns -1. Otherwise it returns 0.
int hfs_fstat(hfsfile *file, hfsdirent *ent);
This routine is similar to hfs_stat() except it returns information
about a file that is already open.
If an error occurs, this routine returns -1. Otherwise it returns 0.
int hfs_setattr(hfsvol *vol, const char *path, const hfsdirent *ent);
This routine changes various attributes of an existing file or
directory. The attributes which may be changed are: ent->crdate,
ent->mddate, ent->bkdate, ent->fdflags, ent->fdlocation,
ent->u.file.type, ent->u.file.creator, and ent->u.dir.rect. Also, the
locked status of a file may be changed with ent->flags & HFS_ISLOCKED.
The given `path' is assumed to be encoded using MacOS Standard Roman.
If an error occurs, this routine returns -1. Otherwise it returns 0.
int hfs_fsetattr(hfsfile *file, const hfsdirent *ent);
This routine is similar to hfs_setattr() except it manipulates a file
that is already open.
If an error occurs, this routine returns -1. Otherwise it returns 0.
int hfs_mkdir(hfsvol *vol, const char *path);
This routine creates a new, empty directory with the given path.
All parent directories must already exist, but there must not already
be a file or directory with the complete given path.
The given `path' is assumed to be encoded using MacOS Standard Roman.
If an error occurs, this function returns -1. Otherwise it returns 0.
int hfs_rmdir(hfsvol *vol, const char *path);
This routine deletes the directory with the given path. The directory
must be empty.
The given `path' is assumed to be encoded using MacOS Standard Roman.
If an error occurs, this function returns -1. Otherwise it returns 0.
int hfs_delete(hfsvol *vol, const char *path);
This routine deletes both forks of the file with the given path.
The given `path' is assumed to be encoded using MacOS Standard Roman.
If an error occurs, this function returns -1. Otherwise it returns 0.
int hfs_rename(hfsvol *vol, const char *srcpath, const char *dstpath);
This routine moves and/or renames the given `srcpath' to `dstpath'.
The source must exist; the destination must not exist, unless it is a
directory, in which case an attempt will be made to move the source
into the destination directory without changing its name.
If both `srcpath' and `dstpath' refer to root directories, the volume
specified by `srcpath' will be renamed. Note that volume names may
only have 1-27 (HFS_MAX_VLEN) characters, while all other names may
have 1-31 (HFS_MAX_FLEN) characters.
The given `srcpath' and `dstpath' are assumed to be encoded using MacOS
Standard Roman.
If an error occurs, this function returns -1. Otherwise it returns 0.
----- Media Routines -----
int hfs_zero(const char *path, unsigned int maxparts,
unsigned long *blocks);
This routine initializes a medium with a new, empty driver descriptor
record and partition map. This is only necessary if it is desired to
partition the medium; the medium can be used as a whole without
partitions by specifying 0 to the routines which require a partition
number.
The partition map will be empty, with the exception of an entry for the
partition map itself, plus an entry for the rest of the medium as free
space. To be useful, one or more HFS partitions should be created with
hfs_mkpart().
The partition map will be created just large enough to allow `maxparts'
individual partitions to be created, not counting the partitions created
automatically by this routine. This number should be conservative, as
it may be impossible to create more than this many partitions for the
lifetime of the medium without re-initializing.
If `blocks' is not NULL, the total number of blocks available for
partitioning (after the partition map structures have been created) will
be stored at this location.
If an error occurs, this function returns -1. Otherwise it returns 0.
int hfs_mkpart(const char *path, unsigned long len);
This routine creates a new HFS partition having `len' blocks on the
given medium. Space for the partition will be taken from the available
free space as indicated in the existing partition map.
It may not be possible to create the requested partition if there are
not enough free contiguous blocks on the medium, or if there is only
one slot left in the partition map and the request does not specify
all the remaining blocks in the free space. (The partition map cannot
leave any blocks in the medium unaccounted for.)
If an error occurs, this function returns -1. Otherwise it returns 0.
int hfs_nparts(const char *path);
This routine determines the number of HFS partitions present on the
given medium, if any. If the medium specified by `path' is not
partitioned, -1 will be returned. Otherwise, a number denoting the total
number of HFS partitions is returned, including (possibly) 0.
The number returned by this routine can help determine if a particular
medium is partitioned, and if so, the allowable range of partition
numbers which can be passed to the routines which require one. However,
passing 0 as a partition number always refers to the entire medium,
ignoring all partitions.
If an error occurs, this function returns -1.
int hfs_format(const char *path, int pnum, int mode, const char *vname,
int nbadblocks, const unsigned long badblocks[]);
This routine writes a new HFS file system to the specified `path', which
should be a block device or a writable file. The size of the volume is
determined either by the maximum size of the device or size of the file,
or by the size of the indicated partition within the medium.
If `pnum' is > 0, it selects an ordinal HFS partition in the device
to receive the file system. The partition must already exist; an error
will result if it cannot be found. With `pnum' == 0, any partition
structure on the existing medium will be ignored, and the entire
device will be used for the new HFS volume.
Volume options may be specified in the `mode' argument. In addition to
the options accepted by hfs_mount(), HFS_OPT_2048 may be specified to
request that the volume allocation blocks be aligned on physical
2048-byte block boundaries. Such a constraint is necessary to support
some hybrid CD-ROM file system formats, but is otherwise unnecessary and
may result in fewer allocation blocks altogether.
The volume is given the name `vname', which must be between 1 and
HFS_MAX_VLEN (27) characters in length inclusively, and cannot contain
any colons (':'). This string is assumed to be encoded using MacOS
Standard Roman.
It is possible to map out or "spare" bad blocks on the device such that
the file system will be made aware of these blocks and will not attempt
to use them to store data. To perform this magic, hfs_format() may be
passed an array of block numbers to spare. These numbers must
correspond to logical 512-byte blocks on the device and should be
relative to the beginning of the volume's partition, if any. If no
blocks need to be spared, 0 should be passed for `nbadblocks', and
`badblocks' may be a NULL pointer. Note that an error can occur if a
bad block occurs in a critical disk structure, or if there are too
many bad blocks (more than 25%) in the volume.
If an error occurs, this function returns -1. Otherwise it returns 0.
===============================================================================

470
vendors/libhfs/low.c vendored Normal file
View File

@ -0,0 +1,470 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: low.c,v 1.8 1998/11/02 22:09:03 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include "libhfs.h"
# include "low.h"
# include "data.h"
# include "block.h"
# include "file.h"
/*
* NAME: low->getddr()
* DESCRIPTION: read a driver descriptor record
*/
int l_getddr(hfsvol *vol, Block0 *ddr)
{
block b;
const byte *ptr = b;
int i;
if (b_readpb(vol, 0, &b, 1) == -1)
goto fail;
d_fetchsw(&ptr, &ddr->sbSig);
d_fetchsw(&ptr, &ddr->sbBlkSize);
d_fetchsl(&ptr, &ddr->sbBlkCount);
d_fetchsw(&ptr, &ddr->sbDevType);
d_fetchsw(&ptr, &ddr->sbDevId);
d_fetchsl(&ptr, &ddr->sbData);
d_fetchsw(&ptr, &ddr->sbDrvrCount);
d_fetchsl(&ptr, &ddr->ddBlock);
d_fetchsw(&ptr, &ddr->ddSize);
d_fetchsw(&ptr, &ddr->ddType);
for (i = 0; i < 243; ++i)
d_fetchsw(&ptr, &ddr->ddPad[i]);
ASSERT(ptr - b == HFS_BLOCKSZ);
return 0;
fail:
return -1;
}
/*
* NAME: low->putddr()
* DESCRIPTION: write a driver descriptor record
*/
int l_putddr(hfsvol *vol, const Block0 *ddr)
{
block b;
byte *ptr = b;
int i;
d_storesw(&ptr, ddr->sbSig);
d_storesw(&ptr, ddr->sbBlkSize);
d_storesl(&ptr, ddr->sbBlkCount);
d_storesw(&ptr, ddr->sbDevType);
d_storesw(&ptr, ddr->sbDevId);
d_storesl(&ptr, ddr->sbData);
d_storesw(&ptr, ddr->sbDrvrCount);
d_storesl(&ptr, ddr->ddBlock);
d_storesw(&ptr, ddr->ddSize);
d_storesw(&ptr, ddr->ddType);
for (i = 0; i < 243; ++i)
d_storesw(&ptr, ddr->ddPad[i]);
ASSERT(ptr - b == HFS_BLOCKSZ);
if (b_writepb(vol, 0, &b, 1) == -1)
goto fail;
return 0;
fail:
return -1;
}
/*
* NAME: low->getpmentry()
* DESCRIPTION: read a partition map entry
*/
int l_getpmentry(hfsvol *vol, Partition *map, unsigned long bnum)
{
block b;
const byte *ptr = b;
int i;
if (b_readpb(vol, bnum, &b, 1) == -1)
goto fail;
d_fetchsw(&ptr, &map->pmSig);
d_fetchsw(&ptr, &map->pmSigPad);
d_fetchsl(&ptr, &map->pmMapBlkCnt);
d_fetchsl(&ptr, &map->pmPyPartStart);
d_fetchsl(&ptr, &map->pmPartBlkCnt);
strncpy((char *) map->pmPartName, (const char *) ptr, 32);
map->pmPartName[32] = 0;
ptr += 32;
strncpy((char *) map->pmParType, (const char *) ptr, 32);
map->pmParType[32] = 0;
ptr += 32;
d_fetchsl(&ptr, &map->pmLgDataStart);
d_fetchsl(&ptr, &map->pmDataCnt);
d_fetchsl(&ptr, &map->pmPartStatus);
d_fetchsl(&ptr, &map->pmLgBootStart);
d_fetchsl(&ptr, &map->pmBootSize);
d_fetchsl(&ptr, &map->pmBootAddr);
d_fetchsl(&ptr, &map->pmBootAddr2);
d_fetchsl(&ptr, &map->pmBootEntry);
d_fetchsl(&ptr, &map->pmBootEntry2);
d_fetchsl(&ptr, &map->pmBootCksum);
strncpy((char *) map->pmProcessor, (const char *) ptr, 16);
map->pmProcessor[16] = 0;
ptr += 16;
for (i = 0; i < 188; ++i)
d_fetchsw(&ptr, &map->pmPad[i]);
ASSERT(ptr - b == HFS_BLOCKSZ);
return 0;
fail:
return -1;
}
/*
* NAME: low->putpmentry()
* DESCRIPTION: write a partition map entry
*/
int l_putpmentry(hfsvol *vol, const Partition *map, unsigned long bnum)
{
block b;
byte *ptr = b;
int i;
d_storesw(&ptr, map->pmSig);
d_storesw(&ptr, map->pmSigPad);
d_storesl(&ptr, map->pmMapBlkCnt);
d_storesl(&ptr, map->pmPyPartStart);
d_storesl(&ptr, map->pmPartBlkCnt);
memset(ptr, 0, 32);
strncpy((char *) ptr, (const char *) map->pmPartName, 32);
ptr += 32;
memset(ptr, 0, 32);
strncpy((char *) ptr, (const char *) map->pmParType, 32);
ptr += 32;
d_storesl(&ptr, map->pmLgDataStart);
d_storesl(&ptr, map->pmDataCnt);
d_storesl(&ptr, map->pmPartStatus);
d_storesl(&ptr, map->pmLgBootStart);
d_storesl(&ptr, map->pmBootSize);
d_storesl(&ptr, map->pmBootAddr);
d_storesl(&ptr, map->pmBootAddr2);
d_storesl(&ptr, map->pmBootEntry);
d_storesl(&ptr, map->pmBootEntry2);
d_storesl(&ptr, map->pmBootCksum);
memset(ptr, 0, 16);
strncpy((char *) ptr, (const char *) map->pmProcessor, 16);
ptr += 16;
for (i = 0; i < 188; ++i)
d_storesw(&ptr, map->pmPad[i]);
ASSERT(ptr - b == HFS_BLOCKSZ);
if (b_writepb(vol, bnum, &b, 1) == -1)
goto fail;
return 0;
fail:
return -1;
}
/*
* NAME: low->getbb()
* DESCRIPTION: read a volume's boot blocks
*/
int l_getbb(hfsvol *vol, BootBlkHdr *bb, byte *bootcode)
{
block b;
const byte *ptr = b;
if (b_readlb(vol, 0, &b) == -1)
goto fail;
d_fetchsw(&ptr, &bb->bbID);
d_fetchsl(&ptr, &bb->bbEntry);
d_fetchsw(&ptr, &bb->bbVersion);
d_fetchsw(&ptr, &bb->bbPageFlags);
d_fetchstr(&ptr, bb->bbSysName, sizeof(bb->bbSysName));
d_fetchstr(&ptr, bb->bbShellName, sizeof(bb->bbShellName));
d_fetchstr(&ptr, bb->bbDbg1Name, sizeof(bb->bbDbg1Name));
d_fetchstr(&ptr, bb->bbDbg2Name, sizeof(bb->bbDbg2Name));
d_fetchstr(&ptr, bb->bbScreenName, sizeof(bb->bbScreenName));
d_fetchstr(&ptr, bb->bbHelloName, sizeof(bb->bbHelloName));
d_fetchstr(&ptr, bb->bbScrapName, sizeof(bb->bbScrapName));
d_fetchsw(&ptr, &bb->bbCntFCBs);
d_fetchsw(&ptr, &bb->bbCntEvts);
d_fetchsl(&ptr, &bb->bb128KSHeap);
d_fetchsl(&ptr, &bb->bb256KSHeap);
d_fetchsl(&ptr, &bb->bbSysHeapSize);
d_fetchsw(&ptr, &bb->filler);
d_fetchsl(&ptr, &bb->bbSysHeapExtra);
d_fetchsl(&ptr, &bb->bbSysHeapFract);
ASSERT(ptr - b == 148);
if (bootcode)
{
memcpy(bootcode, ptr, HFS_BOOTCODE1LEN);
if (b_readlb(vol, 1, &b) == -1)
goto fail;
memcpy(bootcode + HFS_BOOTCODE1LEN, b, HFS_BOOTCODE2LEN);
}
return 0;
fail:
return -1;
}
/*
* NAME: low->putbb()
* DESCRIPTION: write a volume's boot blocks
*/
int l_putbb(hfsvol *vol, const BootBlkHdr *bb, const byte *bootcode)
{
block b;
byte *ptr = b;
d_storesw(&ptr, bb->bbID);
d_storesl(&ptr, bb->bbEntry);
d_storesw(&ptr, bb->bbVersion);
d_storesw(&ptr, bb->bbPageFlags);
d_storestr(&ptr, bb->bbSysName, sizeof(bb->bbSysName));
d_storestr(&ptr, bb->bbShellName, sizeof(bb->bbShellName));
d_storestr(&ptr, bb->bbDbg1Name, sizeof(bb->bbDbg1Name));
d_storestr(&ptr, bb->bbDbg2Name, sizeof(bb->bbDbg2Name));
d_storestr(&ptr, bb->bbScreenName, sizeof(bb->bbScreenName));
d_storestr(&ptr, bb->bbHelloName, sizeof(bb->bbHelloName));
d_storestr(&ptr, bb->bbScrapName, sizeof(bb->bbScrapName));
d_storesw(&ptr, bb->bbCntFCBs);
d_storesw(&ptr, bb->bbCntEvts);
d_storesl(&ptr, bb->bb128KSHeap);
d_storesl(&ptr, bb->bb256KSHeap);
d_storesl(&ptr, bb->bbSysHeapSize);
d_storesw(&ptr, bb->filler);
d_storesl(&ptr, bb->bbSysHeapExtra);
d_storesl(&ptr, bb->bbSysHeapFract);
ASSERT(ptr - b == 148);
if (bootcode)
memcpy(ptr, bootcode, HFS_BOOTCODE1LEN);
else
memset(ptr, 0, HFS_BOOTCODE1LEN);
if (b_writelb(vol, 0, &b) == -1)
goto fail;
if (bootcode)
memcpy(&b, bootcode + HFS_BOOTCODE1LEN, HFS_BOOTCODE2LEN);
else
memset(&b, 0, HFS_BOOTCODE2LEN);
if (b_writelb(vol, 1, &b) == -1)
goto fail;
return 0;
fail:
return -1;
}
/*
* NAME: low->getmdb()
* DESCRIPTION: read a master directory block
*/
int l_getmdb(hfsvol *vol, MDB *mdb, int backup)
{
block b;
const byte *ptr = b;
int i;
if (b_readlb(vol, backup ? vol->vlen - 2 : 2, &b) == -1)
goto fail;
d_fetchsw(&ptr, &mdb->drSigWord);
d_fetchsl(&ptr, &mdb->drCrDate);
d_fetchsl(&ptr, &mdb->drLsMod);
d_fetchsw(&ptr, &mdb->drAtrb);
d_fetchuw(&ptr, &mdb->drNmFls);
d_fetchuw(&ptr, &mdb->drVBMSt);
d_fetchuw(&ptr, &mdb->drAllocPtr);
d_fetchuw(&ptr, &mdb->drNmAlBlks);
d_fetchul(&ptr, &mdb->drAlBlkSiz);
d_fetchul(&ptr, &mdb->drClpSiz);
d_fetchuw(&ptr, &mdb->drAlBlSt);
d_fetchsl(&ptr, &mdb->drNxtCNID);
d_fetchuw(&ptr, &mdb->drFreeBks);
d_fetchstr(&ptr, mdb->drVN, sizeof(mdb->drVN));
ASSERT(ptr - b == 64);
d_fetchsl(&ptr, &mdb->drVolBkUp);
d_fetchsw(&ptr, &mdb->drVSeqNum);
d_fetchul(&ptr, &mdb->drWrCnt);
d_fetchul(&ptr, &mdb->drXTClpSiz);
d_fetchul(&ptr, &mdb->drCTClpSiz);
d_fetchuw(&ptr, &mdb->drNmRtDirs);
d_fetchul(&ptr, &mdb->drFilCnt);
d_fetchul(&ptr, &mdb->drDirCnt);
for (i = 0; i < 8; ++i)
d_fetchsl(&ptr, &mdb->drFndrInfo[i]);
ASSERT(ptr - b == 124);
d_fetchuw(&ptr, &mdb->drEmbedSigWord);
d_fetchuw(&ptr, &mdb->drEmbedExtent.xdrStABN);
d_fetchuw(&ptr, &mdb->drEmbedExtent.xdrNumABlks);
d_fetchul(&ptr, &mdb->drXTFlSize);
for (i = 0; i < 3; ++i)
{
d_fetchuw(&ptr, &mdb->drXTExtRec[i].xdrStABN);
d_fetchuw(&ptr, &mdb->drXTExtRec[i].xdrNumABlks);
}
ASSERT(ptr - b == 146);
d_fetchul(&ptr, &mdb->drCTFlSize);
for (i = 0; i < 3; ++i)
{
d_fetchuw(&ptr, &mdb->drCTExtRec[i].xdrStABN);
d_fetchuw(&ptr, &mdb->drCTExtRec[i].xdrNumABlks);
}
ASSERT(ptr - b == 162);
return 0;
fail:
return -1;
}
/*
* NAME: low->putmdb()
* DESCRIPTION: write master directory block(s)
*/
int l_putmdb(hfsvol *vol, const MDB *mdb, int backup)
{
block b;
byte *ptr = b;
int i;
d_storesw(&ptr, mdb->drSigWord);
d_storesl(&ptr, mdb->drCrDate);
d_storesl(&ptr, mdb->drLsMod);
d_storesw(&ptr, mdb->drAtrb);
d_storeuw(&ptr, mdb->drNmFls);
d_storeuw(&ptr, mdb->drVBMSt);
d_storeuw(&ptr, mdb->drAllocPtr);
d_storeuw(&ptr, mdb->drNmAlBlks);
d_storeul(&ptr, mdb->drAlBlkSiz);
d_storeul(&ptr, mdb->drClpSiz);
d_storeuw(&ptr, mdb->drAlBlSt);
d_storesl(&ptr, mdb->drNxtCNID);
d_storeuw(&ptr, mdb->drFreeBks);
d_storestr(&ptr, mdb->drVN, sizeof(mdb->drVN));
ASSERT(ptr - b == 64);
d_storesl(&ptr, mdb->drVolBkUp);
d_storesw(&ptr, mdb->drVSeqNum);
d_storeul(&ptr, mdb->drWrCnt);
d_storeul(&ptr, mdb->drXTClpSiz);
d_storeul(&ptr, mdb->drCTClpSiz);
d_storeuw(&ptr, mdb->drNmRtDirs);
d_storeul(&ptr, mdb->drFilCnt);
d_storeul(&ptr, mdb->drDirCnt);
for (i = 0; i < 8; ++i)
d_storesl(&ptr, mdb->drFndrInfo[i]);
ASSERT(ptr - b == 124);
d_storeuw(&ptr, mdb->drEmbedSigWord);
d_storeuw(&ptr, mdb->drEmbedExtent.xdrStABN);
d_storeuw(&ptr, mdb->drEmbedExtent.xdrNumABlks);
d_storeul(&ptr, mdb->drXTFlSize);
for (i = 0; i < 3; ++i)
{
d_storeuw(&ptr, mdb->drXTExtRec[i].xdrStABN);
d_storeuw(&ptr, mdb->drXTExtRec[i].xdrNumABlks);
}
ASSERT(ptr - b == 146);
d_storeul(&ptr, mdb->drCTFlSize);
for (i = 0; i < 3; ++i)
{
d_storeuw(&ptr, mdb->drCTExtRec[i].xdrStABN);
d_storeuw(&ptr, mdb->drCTExtRec[i].xdrNumABlks);
}
ASSERT(ptr - b == 162);
memset(ptr, 0, HFS_BLOCKSZ - (ptr - b));
if (b_writelb(vol, 2, &b) == -1 ||
(backup && b_writelb(vol, vol->vlen - 2, &b) == -1))
goto fail;
return 0;
fail:
return -1;
}

44
vendors/libhfs/low.h vendored Normal file
View File

@ -0,0 +1,44 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: low.h,v 1.6 1998/04/11 08:27:13 rob Exp $
*/
# define HFS_DDR_SIGWORD 0x4552
# define HFS_PM_SIGWORD 0x504d
# define HFS_PM_SIGWORD_OLD 0x5453
# define HFS_BB_SIGWORD 0x4c4b
# define HFS_BOOTCODE1LEN (HFS_BLOCKSZ - 148)
# define HFS_BOOTCODE2LEN HFS_BLOCKSZ
# define HFS_BOOTCODELEN (HFS_BOOTCODE1LEN + HFS_BOOTCODE2LEN)
int l_getddr(hfsvol *, Block0 *);
int l_putddr(hfsvol *, const Block0 *);
int l_getpmentry(hfsvol *, Partition *, unsigned long);
int l_putpmentry(hfsvol *, const Partition *, unsigned long);
int l_getbb(hfsvol *, BootBlkHdr *, byte *);
int l_putbb(hfsvol *, const BootBlkHdr *, const byte *);
int l_getmdb(hfsvol *, MDB *, int);
int l_putmdb(hfsvol *, const MDB *, int);

318
vendors/libhfs/medium.c vendored Normal file
View File

@ -0,0 +1,318 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: medium.c,v 1.4 1998/11/02 22:09:04 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include "libhfs.h"
# include "block.h"
# include "low.h"
# include "medium.h"
/* Driver Descriptor Record Routines ======================================= */
/*
* NAME: medium->zeroddr()
* DESCRIPTION: write a new/empty driver descriptor record
*/
int m_zeroddr(hfsvol *vol)
{
Block0 ddr;
int i;
ASSERT(vol->pnum == 0 && vol->vlen != 0);
ddr.sbSig = HFS_DDR_SIGWORD;
ddr.sbBlkSize = HFS_BLOCKSZ;
ddr.sbBlkCount = vol->vlen;
ddr.sbDevType = 0;
ddr.sbDevId = 0;
ddr.sbData = 0;
ddr.sbDrvrCount = 0;
ddr.ddBlock = 0;
ddr.ddSize = 0;
ddr.ddType = 0;
for (i = 0; i < 243; ++i)
ddr.ddPad[i] = 0;
return l_putddr(vol, &ddr);
}
/* Partition Map Routines ================================================== */
/*
* NAME: medium->zeropm()
* DESCRIPTION: write new/empty partition map
*/
int m_zeropm(hfsvol *vol, unsigned int maxparts)
{
Partition map;
unsigned int i;
ASSERT(vol->pnum == 0 && vol->vlen != 0);
if (maxparts < 2)
ERROR(EINVAL, "must allow at least 2 partitions");
/* first entry: partition map itself */
map.pmSig = HFS_PM_SIGWORD;
map.pmSigPad = 0;
map.pmMapBlkCnt = 2;
map.pmPyPartStart = 1;
map.pmPartBlkCnt = maxparts;
strcpy((char *) map.pmPartName, "Apple");
strcpy((char *) map.pmParType, "Apple_partition_map");
map.pmLgDataStart = 0;
map.pmDataCnt = map.pmPartBlkCnt;
map.pmPartStatus = 0;
map.pmLgBootStart = 0;
map.pmBootSize = 0;
map.pmBootAddr = 0;
map.pmBootAddr2 = 0;
map.pmBootEntry = 0;
map.pmBootEntry2 = 0;
map.pmBootCksum = 0;
strcpy((char *) map.pmProcessor, "");
for (i = 0; i < 188; ++i)
map.pmPad[i] = 0;
if (l_putpmentry(vol, &map, 1) == -1)
goto fail;
/* second entry: rest of medium */
map.pmPyPartStart = 1 + maxparts;
map.pmPartBlkCnt = vol->vlen - 1 - maxparts;
strcpy((char *) map.pmPartName, "Extra");
strcpy((char *) map.pmParType, "Apple_Free");
map.pmDataCnt = map.pmPartBlkCnt;
if (l_putpmentry(vol, &map, 2) == -1)
goto fail;
/* zero rest of partition map's partition */
if (maxparts > 2)
{
block b;
memset(&b, 0, sizeof(b));
for (i = 3; i <= maxparts; ++i)
{
if (b_writepb(vol, i, &b, 1) == -1)
goto fail;
}
}
return 0;
fail:
return -1;
}
/*
* NAME: medium->findpmentry()
* DESCRIPTION: locate a partition map entry
*/
int m_findpmentry(hfsvol *vol, const char *type,
Partition *map, unsigned long *start)
{
unsigned long bnum;
int found = 0;
if (start && *start > 0)
{
bnum = *start;
if (bnum++ >= (unsigned long) map->pmMapBlkCnt)
ERROR(EINVAL, "partition not found");
}
else
bnum = 1;
while (1)
{
if (l_getpmentry(vol, map, bnum) == -1)
{
found = -1;
goto fail;
}
if (map->pmSig != HFS_PM_SIGWORD)
{
found = -1;
if (map->pmSig == HFS_PM_SIGWORD_OLD)
ERROR(EINVAL, "old partition map format not supported");
else
ERROR(EINVAL, "invalid partition map");
}
if (strcmp((char *) map->pmParType, type) == 0)
{
found = 1;
goto done;
}
if (bnum++ >= (unsigned long) map->pmMapBlkCnt)
ERROR(EINVAL, "partition not found");
}
done:
if (start)
*start = bnum;
fail:
return found;
}
/*
* NAME: medium->mkpart()
* DESCRIPTION: create a new partition from available free space
*/
int m_mkpart(hfsvol *vol,
const char *name, const char *type, unsigned long len)
{
Partition map;
unsigned int nparts, maxparts;
unsigned long bnum, start, remain;
int found;
if (strlen(name) > 32 ||
strlen(type) > 32)
ERROR(EINVAL, "partition name/type can each be at most 32 chars");
if (len == 0)
ERROR(EINVAL, "partition length must be > 0");
found = m_findpmentry(vol, "Apple_partition_map", &map, 0);
if (found == -1)
goto fail;
if (! found)
ERROR(EIO, "cannot find partition map's partition");
nparts = map.pmMapBlkCnt;
maxparts = map.pmPartBlkCnt;
bnum = 0;
do
{
found = m_findpmentry(vol, "Apple_Free", &map, &bnum);
if (found == -1)
goto fail;
if (! found)
ERROR(ENOSPC, "no available partitions");
}
while (len > (unsigned long) map.pmPartBlkCnt);
start = (unsigned long) map.pmPyPartStart + len;
remain = (unsigned long) map.pmPartBlkCnt - len;
if (remain && nparts >= maxparts)
ERROR(EINVAL, "must allocate all blocks in free space");
map.pmPartBlkCnt = len;
strcpy((char *) map.pmPartName, name);
strcpy((char *) map.pmParType, type);
map.pmLgDataStart = 0;
map.pmDataCnt = len;
map.pmPartStatus = 0;
if (l_putpmentry(vol, &map, bnum) == -1)
goto fail;
if (remain)
{
map.pmPyPartStart = start;
map.pmPartBlkCnt = remain;
strcpy((char *) map.pmPartName, "Extra");
strcpy((char *) map.pmParType, "Apple_Free");
map.pmDataCnt = remain;
if (l_putpmentry(vol, &map, ++nparts) == -1)
goto fail;
for (bnum = 1; bnum <= nparts; ++bnum)
{
if (l_getpmentry(vol, &map, bnum) == -1)
goto fail;
map.pmMapBlkCnt = nparts;
if (l_putpmentry(vol, &map, bnum) == -1)
goto fail;
}
}
return 0;
fail:
return -1;
}
/* Boot Blocks Routines ==================================================== */
/*
* NAME: medium->zerobb()
* DESCRIPTION: write new/empty volume boot blocks
*/
int m_zerobb(hfsvol *vol)
{
block b;
memset(&b, 0, sizeof(b));
if (b_writelb(vol, 0, &b) == -1 ||
b_writelb(vol, 1, &b) == -1)
goto fail;
return 0;
fail:
return -1;
}

42
vendors/libhfs/medium.h vendored Normal file
View File

@ -0,0 +1,42 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: medium.h,v 1.3 1998/04/11 08:27:13 rob Exp $
*/
/*
* Partition Types:
*
* "Apple_partition_map" partition map
* "Apple_Driver" device driver
* "Apple_Driver43" SCSI Manager 4.3 device driver
* "Apple_MFS" Macintosh 64K ROM filesystem
* "Apple_HFS" Macintosh hierarchical filesystem
* "Apple_Unix_SVR2" Unix filesystem
* "Apple_PRODOS" ProDOS filesystem
* "Apple_Free" unused
* "Apple_Scratch" empty
*/
int m_zeroddr(hfsvol *);
int m_zeropm(hfsvol *, unsigned int);
int m_findpmentry(hfsvol *, const char *, Partition *, unsigned long *);
int m_mkpart(hfsvol *, const char *, const char *, unsigned long);
int m_zerobb(hfsvol *);

473
vendors/libhfs/node.c vendored Normal file
View File

@ -0,0 +1,473 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: node.c,v 1.9 1998/11/02 22:09:05 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include "libhfs.h"
# include "node.h"
# include "data.h"
# include "btree.h"
/* total bytes used by records (NOT including record offsets) */
# define NODEUSED(n) \
((size_t) ((n).roff[(n).nd.ndNRecs] - (n).roff[0]))
/* total bytes available for new records (INCLUDING record offsets) */
# define NODEFREE(n) \
((size_t) (HFS_BLOCKSZ - (n).roff[(n).nd.ndNRecs] - \
2 * ((n).nd.ndNRecs + 1)))
/*
* NAME: node->init()
* DESCRIPTION: construct an empty node
*/
void n_init(node *np, btree *bt, int type, int height)
{
np->bt = bt;
np->nnum = (unsigned long) -1;
np->nd.ndFLink = 0;
np->nd.ndBLink = 0;
np->nd.ndType = type;
np->nd.ndNHeight = height;
np->nd.ndNRecs = 0;
np->nd.ndResv2 = 0;
np->rnum = -1;
np->roff[0] = 0x00e;
memset(&np->data, 0, sizeof(np->data));
}
/*
* NAME: node->new()
* DESCRIPTION: allocate a new b*-tree node
*/
int n_new(node *np)
{
btree *bt = np->bt;
unsigned long num;
if (bt->hdr.bthFree == 0)
ERROR(EIO, "b*-tree full");
num = 0;
while (num < bt->hdr.bthNNodes && BMTST(bt->map, num))
++num;
if (num == bt->hdr.bthNNodes)
ERROR(EIO, "free b*-tree node not found");
np->nnum = num;
BMSET(bt->map, num);
--bt->hdr.bthFree;
bt->flags |= HFS_BT_UPDATE_HDR;
return 0;
fail:
return -1;
}
/*
* NAME: node->free()
* DESCRIPTION: deallocate and remove a b*-tree node
*/
int n_free(node *np)
{
btree *bt = np->bt;
node sib;
if (bt->hdr.bthFNode == np->nnum)
bt->hdr.bthFNode = np->nd.ndFLink;
if (bt->hdr.bthLNode == np->nnum)
bt->hdr.bthLNode = np->nd.ndBLink;
if (np->nd.ndFLink > 0)
{
if (bt_getnode(&sib, bt, np->nd.ndFLink) == -1)
goto fail;
sib.nd.ndBLink = np->nd.ndBLink;
if (bt_putnode(&sib) == -1)
goto fail;
}
if (np->nd.ndBLink > 0)
{
if (bt_getnode(&sib, bt, np->nd.ndBLink) == -1)
goto fail;
sib.nd.ndFLink = np->nd.ndFLink;
if (bt_putnode(&sib) == -1)
goto fail;
}
BMCLR(bt->map, np->nnum);
++bt->hdr.bthFree;
bt->flags |= HFS_BT_UPDATE_HDR;
return 0;
fail:
return -1;
}
/*
* NAME: compact()
* DESCRIPTION: clean up a node, removing deleted records
*/
static
void compact(node *np)
{
byte *ptr;
int offset, nrecs, i;
offset = 0x00e;
ptr = np->data + offset;
nrecs = 0;
for (i = 0; i < np->nd.ndNRecs; ++i)
{
const byte *rec;
int reclen;
rec = HFS_NODEREC(*np, i);
reclen = HFS_RECLEN(*np, i);
if (HFS_RECKEYLEN(rec) > 0)
{
np->roff[nrecs++] = offset;
offset += reclen;
if (ptr == rec)
ptr += reclen;
else
{
while (reclen--)
*ptr++ = *rec++;
}
}
}
np->roff[nrecs] = offset;
np->nd.ndNRecs = nrecs;
}
/*
* NAME: node->search()
* DESCRIPTION: locate a record in a node, or the record it should follow
*/
int n_search(node *np, const byte *pkey)
{
const btree *bt = np->bt;
byte key1[HFS_MAX_KEYLEN], key2[HFS_MAX_KEYLEN];
int i, comp = -1;
bt->keyunpack(pkey, key2);
for (i = np->nd.ndNRecs; i--; )
{
const byte *rec;
rec = HFS_NODEREC(*np, i);
if (HFS_RECKEYLEN(rec) == 0)
continue; /* deleted record */
bt->keyunpack(rec, key1);
comp = bt->keycompare(key1, key2);
if (comp <= 0)
break;
}
np->rnum = i;
return comp == 0;
}
/*
* NAME: node->index()
* DESCRIPTION: create an index record from a key and node pointer
*/
void n_index(const node *np, byte *record, unsigned int *reclen)
{
const byte *key = HFS_NODEREC(*np, 0);
if (np->bt == &np->bt->f.vol->cat)
{
/* force the key length to be 0x25 */
HFS_SETKEYLEN(record, 0x25);
memset(record + 1, 0, 0x25);
memcpy(record + 1, key + 1, HFS_RECKEYLEN(key));
}
else
memcpy(record, key, HFS_RECKEYSKIP(key));
d_putul(HFS_RECDATA(record), np->nnum);
if (reclen)
*reclen = HFS_RECKEYSKIP(record) + 4;
}
/*
* NAME: split()
* DESCRIPTION: divide a node into two and insert a record
*/
static
int split(node *left, byte *record, unsigned int *reclen)
{
btree *bt = left->bt;
node n, *right = &n, *side = 0;
int mark, i;
/* create a second node by cloning the first */
*right = *left;
if (n_new(right) == -1)
goto fail;
left->nd.ndFLink = right->nnum;
right->nd.ndBLink = left->nnum;
/* divide all records evenly between the two nodes */
mark = (NODEUSED(*left) + 2 * left->nd.ndNRecs + *reclen + 2) >> 1;
if (left->rnum == -1)
{
side = left;
mark -= *reclen + 2;
}
for (i = 0; i < left->nd.ndNRecs; ++i)
{
node *np;
byte *rec;
np = (mark > 0) ? right : left;
rec = HFS_NODEREC(*np, i);
mark -= HFS_RECLEN(*np, i) + 2;
HFS_SETKEYLEN(rec, 0);
if (left->rnum == i)
{
side = (mark > 0) ? left : right;
mark -= *reclen + 2;
}
}
compact(left);
compact(right);
/* insert the new record and store the modified nodes */
ASSERT(side);
n_search(side, record);
n_insertx(side, record, *reclen);
if (bt_putnode(left) == -1 ||
bt_putnode(right) == -1)
goto fail;
/* create an index record in the parent for the new node */
n_index(right, record, reclen);
/* update link pointers */
if (bt->hdr.bthLNode == left->nnum)
{
bt->hdr.bthLNode = right->nnum;
bt->flags |= HFS_BT_UPDATE_HDR;
}
if (right->nd.ndFLink > 0)
{
node sib;
if (bt_getnode(&sib, right->bt, right->nd.ndFLink) == -1)
goto fail;
sib.nd.ndBLink = right->nnum;
if (bt_putnode(&sib) == -1)
goto fail;
}
return 0;
fail:
return -1;
}
/*
* NAME: node->insertx()
* DESCRIPTION: insert a record into a node (which must already have room)
*/
void n_insertx(node *np, const byte *record, unsigned int reclen)
{
int rnum, i;
byte *ptr;
rnum = np->rnum + 1;
/* push other records down to make room */
for (ptr = HFS_NODEREC(*np, np->nd.ndNRecs) + reclen;
ptr > HFS_NODEREC(*np, rnum) + reclen; --ptr)
*(ptr - 1) = *(ptr - 1 - reclen);
++np->nd.ndNRecs;
for (i = np->nd.ndNRecs; i > rnum; --i)
np->roff[i] = np->roff[i - 1] + reclen;
/* write the new record */
memcpy(HFS_NODEREC(*np, rnum), record, reclen);
}
/*
* NAME: node->insert()
* DESCRIPTION: insert a new record into a node; return a record for parent
*/
int n_insert(node *np, byte *record, unsigned int *reclen)
{
/* check for free space */
if (np->nd.ndNRecs >= HFS_MAX_NRECS ||
*reclen + 2 > NODEFREE(*np))
return split(np, record, reclen);
n_insertx(np, record, *reclen);
*reclen = 0;
return bt_putnode(np);
}
/*
* NAME: join()
* DESCRIPTION: combine two nodes into a single node
*/
static
int join(node *left, node *right, byte *record, int *flag)
{
int i, offset;
/* copy records and offsets */
memcpy(HFS_NODEREC(*left, left->nd.ndNRecs),
HFS_NODEREC(*right, 0), NODEUSED(*right));
offset = left->roff[left->nd.ndNRecs] - right->roff[0];
for (i = 1; i <= right->nd.ndNRecs; ++i)
left->roff[++left->nd.ndNRecs] = offset + right->roff[i];
if (bt_putnode(left) == -1)
goto fail;
/* eliminate node and update link pointers */
if (n_free(right) == -1)
goto fail;
HFS_SETKEYLEN(record, 0);
*flag = 1;
return 0;
fail:
return -1;
}
/*
* NAME: node->delete()
* DESCRIPTION: remove a record from a node
*/
int n_delete(node *np, byte *record, int *flag)
{
byte *rec;
rec = HFS_NODEREC(*np, np->rnum);
HFS_SETKEYLEN(rec, 0);
compact(np);
if (np->nd.ndNRecs == 0)
{
if (n_free(np) == -1)
goto fail;
HFS_SETKEYLEN(record, 0);
*flag = 1;
return 0;
}
/* see if we can join with our left sibling */
if (np->nd.ndBLink > 0)
{
node left;
if (bt_getnode(&left, np->bt, np->nd.ndBLink) == -1)
goto fail;
if (np->nd.ndNRecs + left.nd.ndNRecs <= HFS_MAX_NRECS &&
NODEUSED(*np) + 2 * np->nd.ndNRecs <= NODEFREE(left))
return join(&left, np, record, flag);
}
if (np->rnum == 0)
{
/* special case: first record changed; update parent record key */
n_index(np, record, 0);
*flag = 1;
}
return bt_putnode(np);
fail:
return -1;
}

34
vendors/libhfs/node.h vendored Normal file
View File

@ -0,0 +1,34 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: node.h,v 1.7 1998/11/02 22:09:06 rob Exp $
*/
void n_init(node *, btree *, int, int);
int n_new(node *);
int n_free(node *);
int n_search(node *, const byte *);
void n_index(const node *, byte *, unsigned int *);
void n_insertx(node *, const byte *, unsigned int);
int n_insert(node *, byte *, unsigned int *);
int n_delete(node *, byte *, int *);

188
vendors/libhfs/os-unix.c vendored Normal file
View File

@ -0,0 +1,188 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: unix.c,v 1.8 1998/11/02 22:09:13 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <fcntl.h>
# include <unistd.h>
# include <errno.h>
# include <sys/stat.h>
# include "libhfs.h"
# include "os.h"
// Ignore warning on pointer-to-int and int-to-pinter conversions.
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
/*
* NAME: os->open()
* DESCRIPTION: open and lock a new descriptor from the given path and mode
*/
int os_open(void **priv, const char *path, int mode)
{
int fd;
struct flock lock;
switch (mode)
{
case HFS_MODE_RDONLY:
mode = O_RDONLY;
break;
case HFS_MODE_RDWR:
default:
mode = O_RDWR;
break;
}
fd = open(path, mode);
if (fd == -1)
ERROR(errno, "error opening medium");
/* lock descriptor against concurrent access */
lock.l_type = (mode == O_RDONLY) ? F_RDLCK : F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
if (fcntl(fd, F_SETLK, &lock) == -1 &&
(errno == EACCES || errno == EAGAIN))
ERROR(EAGAIN, "unable to obtain lock for medium");
*priv = (void *) fd;
return 0;
fail:
if (fd != -1)
close(fd);
return -1;
}
/*
* NAME: os->close()
* DESCRIPTION: close an open descriptor
*/
int os_close(void **priv)
{
int fd = (int) *priv;
*priv = (void *) -1;
if (close(fd) == -1)
ERROR(errno, "error closing medium");
return 0;
fail:
return -1;
}
/*
* NAME: os->same()
* DESCRIPTION: return 1 iff path is same as the open descriptor
*/
int os_same(void **priv, const char *path)
{
int fd = (int) *priv;
struct stat fdev, dev;
if (fstat(fd, &fdev) == -1 ||
stat(path, &dev) == -1)
ERROR(errno, "can't get path information");
return fdev.st_dev == dev.st_dev &&
fdev.st_ino == dev.st_ino;
fail:
return -1;
}
/*
* NAME: os->seek()
* DESCRIPTION: set a descriptor's seek pointer (offset in blocks)
*/
unsigned long os_seek(void **priv, unsigned long offset)
{
int fd = (int) *priv;
off_t result;
/* offset == -1 special; seek to last block of device */
if (offset == (unsigned long) -1)
result = lseek(fd, 0, SEEK_END);
else
result = lseek(fd, offset << HFS_BLOCKSZ_BITS, SEEK_SET);
if (result == -1)
ERROR(errno, "error seeking medium");
return (unsigned long) result >> HFS_BLOCKSZ_BITS;
fail:
return -1;
}
/*
* NAME: os->read()
* DESCRIPTION: read blocks from an open descriptor
*/
unsigned long os_read(void **priv, void *buf, unsigned long len)
{
int fd = (int) *priv;
ssize_t result;
result = read(fd, buf, len << HFS_BLOCKSZ_BITS);
if (result == -1)
ERROR(errno, "error reading from medium");
return (unsigned long) result >> HFS_BLOCKSZ_BITS;
fail:
return -1;
}
/*
* NAME: os->write()
* DESCRIPTION: write blocks to an open descriptor
*/
unsigned long os_write(void **priv, const void *buf, unsigned long len)
{
int fd = (int) *priv;
ssize_t result;
result = write(fd, buf, len << HFS_BLOCKSZ_BITS);
if (result == -1)
ERROR(errno, "error writing to medium");
return (unsigned long) result >> HFS_BLOCKSZ_BITS;
fail:
return -1;
}

29
vendors/libhfs/os.h vendored Normal file
View File

@ -0,0 +1,29 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: os.h,v 1.6 1998/09/15 19:21:05 rob Exp $
*/
int os_open(void **, const char *, int);
int os_close(void **);
int os_same(void **, const char *);
unsigned long os_seek(void **, unsigned long);
unsigned long os_read(void **, void *, unsigned long);
unsigned long os_write(void **, const void *, unsigned long);

557
vendors/libhfs/record.c vendored Normal file
View File

@ -0,0 +1,557 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: record.c,v 1.9 1998/11/02 22:09:07 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <string.h>
# include "libhfs.h"
# include "record.h"
# include "data.h"
/*
* NAME: record->packcatkey()
* DESCRIPTION: pack a catalog record key
*/
void r_packcatkey(const CatKeyRec *key, byte *pkey, unsigned int *len)
{
const byte *start = pkey;
d_storesb(&pkey, key->ckrKeyLen);
d_storesb(&pkey, key->ckrResrv1);
d_storeul(&pkey, key->ckrParID);
d_storestr(&pkey, key->ckrCName, sizeof(key->ckrCName));
if (len)
*len = HFS_RECKEYSKIP(start);
}
/*
* NAME: record->unpackcatkey()
* DESCRIPTION: unpack a catalog record key
*/
void r_unpackcatkey(const byte *pkey, CatKeyRec *key)
{
d_fetchsb(&pkey, &key->ckrKeyLen);
d_fetchsb(&pkey, &key->ckrResrv1);
d_fetchul(&pkey, &key->ckrParID);
d_fetchstr(&pkey, key->ckrCName, sizeof(key->ckrCName));
}
/*
* NAME: record->packextkey()
* DESCRIPTION: pack an extents record key
*/
void r_packextkey(const ExtKeyRec *key, byte *pkey, unsigned int *len)
{
const byte *start = pkey;
d_storesb(&pkey, key->xkrKeyLen);
d_storesb(&pkey, key->xkrFkType);
d_storeul(&pkey, key->xkrFNum);
d_storeuw(&pkey, key->xkrFABN);
if (len)
*len = HFS_RECKEYSKIP(start);
}
/*
* NAME: record->unpackextkey()
* DESCRIPTION: unpack an extents record key
*/
void r_unpackextkey(const byte *pkey, ExtKeyRec *key)
{
d_fetchsb(&pkey, &key->xkrKeyLen);
d_fetchsb(&pkey, &key->xkrFkType);
d_fetchul(&pkey, &key->xkrFNum);
d_fetchuw(&pkey, &key->xkrFABN);
}
/*
* NAME: record->comparecatkeys()
* DESCRIPTION: compare two (packed) catalog record keys
*/
int r_comparecatkeys(const CatKeyRec *key1, const CatKeyRec *key2)
{
int diff;
diff = key1->ckrParID - key2->ckrParID;
if (diff)
return diff;
return d_relstring(key1->ckrCName, key2->ckrCName);
}
/*
* NAME: record->compareextkeys()
* DESCRIPTION: compare two (packed) extents record keys
*/
int r_compareextkeys(const ExtKeyRec *key1, const ExtKeyRec *key2)
{
int diff;
diff = key1->xkrFNum - key2->xkrFNum;
if (diff)
return diff;
diff = (unsigned char) key1->xkrFkType -
(unsigned char) key2->xkrFkType;
if (diff)
return diff;
return key1->xkrFABN - key2->xkrFABN;
}
/*
* NAME: record->packcatdata()
* DESCRIPTION: pack catalog record data
*/
void r_packcatdata(const CatDataRec *data, byte *pdata, unsigned int *len)
{
const byte *start = pdata;
int i;
d_storesb(&pdata, data->cdrType);
d_storesb(&pdata, data->cdrResrv2);
switch (data->cdrType)
{
case cdrDirRec:
d_storesw(&pdata, data->u.dir.dirFlags);
d_storeuw(&pdata, data->u.dir.dirVal);
d_storeul(&pdata, data->u.dir.dirDirID);
d_storesl(&pdata, data->u.dir.dirCrDat);
d_storesl(&pdata, data->u.dir.dirMdDat);
d_storesl(&pdata, data->u.dir.dirBkDat);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.top);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.left);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.bottom);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.right);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frFlags);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frLocation.v);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frLocation.h);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frView);
d_storesw(&pdata, data->u.dir.dirFndrInfo.frScroll.v);
d_storesw(&pdata, data->u.dir.dirFndrInfo.frScroll.h);
d_storesl(&pdata, data->u.dir.dirFndrInfo.frOpenChain);
d_storesw(&pdata, data->u.dir.dirFndrInfo.frUnused);
d_storesw(&pdata, data->u.dir.dirFndrInfo.frComment);
d_storesl(&pdata, data->u.dir.dirFndrInfo.frPutAway);
for (i = 0; i < 4; ++i)
d_storesl(&pdata, data->u.dir.dirResrv[i]);
break;
case cdrFilRec:
d_storesb(&pdata, data->u.fil.filFlags);
d_storesb(&pdata, data->u.fil.filTyp);
d_storesl(&pdata, data->u.fil.filUsrWds.fdType);
d_storesl(&pdata, data->u.fil.filUsrWds.fdCreator);
d_storesw(&pdata, data->u.fil.filUsrWds.fdFlags);
d_storesw(&pdata, data->u.fil.filUsrWds.fdLocation.v);
d_storesw(&pdata, data->u.fil.filUsrWds.fdLocation.h);
d_storesw(&pdata, data->u.fil.filUsrWds.fdFldr);
d_storeul(&pdata, data->u.fil.filFlNum);
d_storeuw(&pdata, data->u.fil.filStBlk);
d_storeul(&pdata, data->u.fil.filLgLen);
d_storeul(&pdata, data->u.fil.filPyLen);
d_storeuw(&pdata, data->u.fil.filRStBlk);
d_storeul(&pdata, data->u.fil.filRLgLen);
d_storeul(&pdata, data->u.fil.filRPyLen);
d_storesl(&pdata, data->u.fil.filCrDat);
d_storesl(&pdata, data->u.fil.filMdDat);
d_storesl(&pdata, data->u.fil.filBkDat);
d_storesw(&pdata, data->u.fil.filFndrInfo.fdIconID);
for (i = 0; i < 4; ++i)
d_storesw(&pdata, data->u.fil.filFndrInfo.fdUnused[i]);
d_storesw(&pdata, data->u.fil.filFndrInfo.fdComment);
d_storesl(&pdata, data->u.fil.filFndrInfo.fdPutAway);
d_storeuw(&pdata, data->u.fil.filClpSize);
for (i = 0; i < 3; ++i)
{
d_storeuw(&pdata, data->u.fil.filExtRec[i].xdrStABN);
d_storeuw(&pdata, data->u.fil.filExtRec[i].xdrNumABlks);
}
for (i = 0; i < 3; ++i)
{
d_storeuw(&pdata, data->u.fil.filRExtRec[i].xdrStABN);
d_storeuw(&pdata, data->u.fil.filRExtRec[i].xdrNumABlks);
}
d_storesl(&pdata, data->u.fil.filResrv);
break;
case cdrThdRec:
for (i = 0; i < 2; ++i)
d_storesl(&pdata, data->u.dthd.thdResrv[i]);
d_storeul(&pdata, data->u.dthd.thdParID);
d_storestr(&pdata, data->u.dthd.thdCName,
sizeof(data->u.dthd.thdCName));
break;
case cdrFThdRec:
for (i = 0; i < 2; ++i)
d_storesl(&pdata, data->u.fthd.fthdResrv[i]);
d_storeul(&pdata, data->u.fthd.fthdParID);
d_storestr(&pdata, data->u.fthd.fthdCName,
sizeof(data->u.fthd.fthdCName));
break;
default:
ASSERT(0);
}
if (len)
*len += pdata - start;
}
/*
* NAME: record->unpackcatdata()
* DESCRIPTION: unpack catalog record data
*/
void r_unpackcatdata(const byte *pdata, CatDataRec *data)
{
int i;
d_fetchsb(&pdata, &data->cdrType);
d_fetchsb(&pdata, &data->cdrResrv2);
switch (data->cdrType)
{
case cdrDirRec:
d_fetchsw(&pdata, &data->u.dir.dirFlags);
d_fetchuw(&pdata, &data->u.dir.dirVal);
d_fetchul(&pdata, &data->u.dir.dirDirID);
d_fetchsl(&pdata, &data->u.dir.dirCrDat);
d_fetchsl(&pdata, &data->u.dir.dirMdDat);
d_fetchsl(&pdata, &data->u.dir.dirBkDat);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.top);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.left);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.bottom);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.right);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frFlags);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frLocation.v);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frLocation.h);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frView);
d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frScroll.v);
d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frScroll.h);
d_fetchsl(&pdata, &data->u.dir.dirFndrInfo.frOpenChain);
d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frUnused);
d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frComment);
d_fetchsl(&pdata, &data->u.dir.dirFndrInfo.frPutAway);
for (i = 0; i < 4; ++i)
d_fetchsl(&pdata, &data->u.dir.dirResrv[i]);
break;
case cdrFilRec:
d_fetchsb(&pdata, &data->u.fil.filFlags);
d_fetchsb(&pdata, &data->u.fil.filTyp);
d_fetchsl(&pdata, &data->u.fil.filUsrWds.fdType);
d_fetchsl(&pdata, &data->u.fil.filUsrWds.fdCreator);
d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdFlags);
d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdLocation.v);
d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdLocation.h);
d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdFldr);
d_fetchul(&pdata, &data->u.fil.filFlNum);
d_fetchuw(&pdata, &data->u.fil.filStBlk);
d_fetchul(&pdata, &data->u.fil.filLgLen);
d_fetchul(&pdata, &data->u.fil.filPyLen);
d_fetchuw(&pdata, &data->u.fil.filRStBlk);
d_fetchul(&pdata, &data->u.fil.filRLgLen);
d_fetchul(&pdata, &data->u.fil.filRPyLen);
d_fetchsl(&pdata, &data->u.fil.filCrDat);
d_fetchsl(&pdata, &data->u.fil.filMdDat);
d_fetchsl(&pdata, &data->u.fil.filBkDat);
d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdIconID);
for (i = 0; i < 4; ++i)
d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdUnused[i]);
d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdComment);
d_fetchsl(&pdata, &data->u.fil.filFndrInfo.fdPutAway);
d_fetchuw(&pdata, &data->u.fil.filClpSize);
for (i = 0; i < 3; ++i)
{
d_fetchuw(&pdata, &data->u.fil.filExtRec[i].xdrStABN);
d_fetchuw(&pdata, &data->u.fil.filExtRec[i].xdrNumABlks);
}
for (i = 0; i < 3; ++i)
{
d_fetchuw(&pdata, &data->u.fil.filRExtRec[i].xdrStABN);
d_fetchuw(&pdata, &data->u.fil.filRExtRec[i].xdrNumABlks);
}
d_fetchsl(&pdata, &data->u.fil.filResrv);
break;
case cdrThdRec:
for (i = 0; i < 2; ++i)
d_fetchsl(&pdata, &data->u.dthd.thdResrv[i]);
d_fetchul(&pdata, &data->u.dthd.thdParID);
d_fetchstr(&pdata, data->u.dthd.thdCName,
sizeof(data->u.dthd.thdCName));
break;
case cdrFThdRec:
for (i = 0; i < 2; ++i)
d_fetchsl(&pdata, &data->u.fthd.fthdResrv[i]);
d_fetchul(&pdata, &data->u.fthd.fthdParID);
d_fetchstr(&pdata, data->u.fthd.fthdCName,
sizeof(data->u.fthd.fthdCName));
break;
default:
ASSERT(0);
}
}
/*
* NAME: record->packextdata()
* DESCRIPTION: pack extent record data
*/
void r_packextdata(const ExtDataRec *data, byte *pdata, unsigned int *len)
{
const byte *start = pdata;
int i;
for (i = 0; i < 3; ++i)
{
d_storeuw(&pdata, (*data)[i].xdrStABN);
d_storeuw(&pdata, (*data)[i].xdrNumABlks);
}
if (len)
*len += pdata - start;
}
/*
* NAME: record->unpackextdata()
* DESCRIPTION: unpack extent record data
*/
void r_unpackextdata(const byte *pdata, ExtDataRec *data)
{
int i;
for (i = 0; i < 3; ++i)
{
d_fetchuw(&pdata, &(*data)[i].xdrStABN);
d_fetchuw(&pdata, &(*data)[i].xdrNumABlks);
}
}
/*
* NAME: record->makecatkey()
* DESCRIPTION: construct a catalog record key
*/
void r_makecatkey(CatKeyRec *key, unsigned long parid, const char *name)
{
int len;
len = strlen(name) + 1;
key->ckrKeyLen = 0x05 + len + (len & 1);
key->ckrResrv1 = 0;
key->ckrParID = parid;
strcpy(key->ckrCName, name);
}
/*
* NAME: record->makeextkey()
* DESCRIPTION: construct an extents record key
*/
void r_makeextkey(ExtKeyRec *key,
int fork, unsigned long fnum, unsigned int fabn)
{
key->xkrKeyLen = 0x07;
key->xkrFkType = fork;
key->xkrFNum = fnum;
key->xkrFABN = fabn;
}
/*
* NAME: record->packcatrec()
* DESCRIPTION: create a packed catalog record
*/
void r_packcatrec(const CatKeyRec *key, const CatDataRec *data,
byte *precord, unsigned int *len)
{
r_packcatkey(key, precord, len);
r_packcatdata(data, HFS_RECDATA(precord), len);
}
/*
* NAME: record->packextrec()
* DESCRIPTION: create a packed extents record
*/
void r_packextrec(const ExtKeyRec *key, const ExtDataRec *data,
byte *precord, unsigned int *len)
{
r_packextkey(key, precord, len);
r_packextdata(data, HFS_RECDATA(precord), len);
}
/*
* NAME: record->packdirent()
* DESCRIPTION: make changes to a catalog record
*/
void r_packdirent(CatDataRec *data, const hfsdirent *ent)
{
switch (data->cdrType)
{
case cdrDirRec:
data->u.dir.dirCrDat = d_mtime(ent->crdate);
data->u.dir.dirMdDat = d_mtime(ent->mddate);
data->u.dir.dirBkDat = d_mtime(ent->bkdate);
data->u.dir.dirUsrInfo.frFlags = ent->fdflags;
data->u.dir.dirUsrInfo.frLocation.v = ent->fdlocation.v;
data->u.dir.dirUsrInfo.frLocation.h = ent->fdlocation.h;
data->u.dir.dirUsrInfo.frRect.top = ent->u.dir.rect.top;
data->u.dir.dirUsrInfo.frRect.left = ent->u.dir.rect.left;
data->u.dir.dirUsrInfo.frRect.bottom = ent->u.dir.rect.bottom;
data->u.dir.dirUsrInfo.frRect.right = ent->u.dir.rect.right;
break;
case cdrFilRec:
if (ent->flags & HFS_ISLOCKED)
data->u.fil.filFlags |= (1 << 0);
else
data->u.fil.filFlags &= ~(1 << 0);
data->u.fil.filCrDat = d_mtime(ent->crdate);
data->u.fil.filMdDat = d_mtime(ent->mddate);
data->u.fil.filBkDat = d_mtime(ent->bkdate);
data->u.fil.filUsrWds.fdFlags = ent->fdflags;
data->u.fil.filUsrWds.fdLocation.v = ent->fdlocation.v;
data->u.fil.filUsrWds.fdLocation.h = ent->fdlocation.h;
data->u.fil.filUsrWds.fdType =
d_getsl((const unsigned char *) ent->u.file.type);
data->u.fil.filUsrWds.fdCreator =
d_getsl((const unsigned char *) ent->u.file.creator);
break;
}
}
/*
* NAME: record->unpackdirent()
* DESCRIPTION: unpack catalog information into hfsdirent structure
*/
void r_unpackdirent(unsigned long parid, const char *name,
const CatDataRec *data, hfsdirent *ent)
{
strcpy(ent->name, name);
ent->parid = parid;
switch (data->cdrType)
{
case cdrDirRec:
ent->flags = HFS_ISDIR;
ent->cnid = data->u.dir.dirDirID;
ent->crdate = d_ltime(data->u.dir.dirCrDat);
ent->mddate = d_ltime(data->u.dir.dirMdDat);
ent->bkdate = d_ltime(data->u.dir.dirBkDat);
ent->fdflags = data->u.dir.dirUsrInfo.frFlags;
ent->fdlocation.v = data->u.dir.dirUsrInfo.frLocation.v;
ent->fdlocation.h = data->u.dir.dirUsrInfo.frLocation.h;
ent->u.dir.valence = data->u.dir.dirVal;
ent->u.dir.rect.top = data->u.dir.dirUsrInfo.frRect.top;
ent->u.dir.rect.left = data->u.dir.dirUsrInfo.frRect.left;
ent->u.dir.rect.bottom = data->u.dir.dirUsrInfo.frRect.bottom;
ent->u.dir.rect.right = data->u.dir.dirUsrInfo.frRect.right;
break;
case cdrFilRec:
ent->flags = (data->u.fil.filFlags & (1 << 0)) ? HFS_ISLOCKED : 0;
ent->cnid = data->u.fil.filFlNum;
ent->crdate = d_ltime(data->u.fil.filCrDat);
ent->mddate = d_ltime(data->u.fil.filMdDat);
ent->bkdate = d_ltime(data->u.fil.filBkDat);
ent->fdflags = data->u.fil.filUsrWds.fdFlags;
ent->fdlocation.v = data->u.fil.filUsrWds.fdLocation.v;
ent->fdlocation.h = data->u.fil.filUsrWds.fdLocation.h;
ent->u.file.dsize = data->u.fil.filLgLen;
ent->u.file.rsize = data->u.fil.filRLgLen;
d_putsl((unsigned char *) ent->u.file.type,
data->u.fil.filUsrWds.fdType);
d_putsl((unsigned char *) ent->u.file.creator,
data->u.fil.filUsrWds.fdCreator);
ent->u.file.type[4] = ent->u.file.creator[4] = 0;
break;
}
}

47
vendors/libhfs/record.h vendored Normal file
View File

@ -0,0 +1,47 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: record.h,v 1.7 1998/11/02 22:09:08 rob Exp $
*/
void r_packcatkey(const CatKeyRec *, byte *, unsigned int *);
void r_unpackcatkey(const byte *, CatKeyRec *);
void r_packextkey(const ExtKeyRec *, byte *, unsigned int *);
void r_unpackextkey(const byte *, ExtKeyRec *);
int r_comparecatkeys(const CatKeyRec *, const CatKeyRec *);
int r_compareextkeys(const ExtKeyRec *, const ExtKeyRec *);
void r_packcatdata(const CatDataRec *, byte *, unsigned int *);
void r_unpackcatdata(const byte *, CatDataRec *);
void r_packextdata(const ExtDataRec *, byte *, unsigned int *);
void r_unpackextdata(const byte *, ExtDataRec *);
void r_makecatkey(CatKeyRec *, unsigned long, const char *);
void r_makeextkey(ExtKeyRec *, int, unsigned long, unsigned int);
void r_packcatrec(const CatKeyRec *, const CatDataRec *,
byte *, unsigned int *);
void r_packextrec(const ExtKeyRec *, const ExtDataRec *,
byte *, unsigned int *);
void r_packdirent(CatDataRec *, const hfsdirent *);
void r_unpackdirent(unsigned long, const char *,
const CatDataRec *, hfsdirent *);

29
vendors/libhfs/version.c vendored Normal file
View File

@ -0,0 +1,29 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: version.c,v 1.11 1998/11/02 22:09:09 rob Exp $
*/
# include "version.h"
const char libhfs_rcsid[] =
"$Id: version.c,v 1.11 1998/11/02 22:09:09 rob Exp $";
const char libhfs_version[] = "libhfs version 3.2.6";
const char libhfs_copyright[] = "Copyright (C) 1996-1998 Robert Leslie";
const char libhfs_author[] = "Robert Leslie <rob@mars.org>";

26
vendors/libhfs/version.h vendored Normal file
View File

@ -0,0 +1,26 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: version.h,v 1.6 1998/09/18 22:56:38 rob Exp $
*/
extern const char libhfs_rcsid[];
extern const char libhfs_version[];
extern const char libhfs_copyright[];
extern const char libhfs_author[];

1202
vendors/libhfs/volume.c vendored Normal file

File diff suppressed because it is too large Load Diff

62
vendors/libhfs/volume.h vendored Normal file
View File

@ -0,0 +1,62 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: volume.h,v 1.7 1998/11/02 22:09:12 rob Exp $
*/
void v_init(hfsvol *, int);
int v_open(hfsvol *, const char *, int);
int v_flush(hfsvol *);
int v_close(hfsvol *);
int v_same(hfsvol *, const char *);
int v_geometry(hfsvol *, int);
int v_readmdb(hfsvol *);
int v_writemdb(hfsvol *);
int v_readvbm(hfsvol *);
int v_writevbm(hfsvol *);
int v_mount(hfsvol *);
int v_dirty(hfsvol *);
int v_catsearch(hfsvol *, unsigned long, const char *,
CatDataRec *, char *, node *);
int v_extsearch(hfsfile *, unsigned int, ExtDataRec *, node *);
int v_getthread(hfsvol *, unsigned long, CatDataRec *, node *, int);
# define v_getdthread(vol, id, thread, np) \
v_getthread(vol, id, thread, np, cdrThdRec)
# define v_getfthread(vol, id, thread, np) \
v_getthread(vol, id, thread, np, cdrFThdRec)
int v_putcatrec(const CatDataRec *, node *);
int v_putextrec(const ExtDataRec *, node *);
int v_allocblocks(hfsvol *, ExtDescriptor *);
int v_freeblocks(hfsvol *, const ExtDescriptor *);
int v_resolve(hfsvol **, const char *, CatDataRec *, long *, char *, node *);
int v_adjvalence(hfsvol *, unsigned long, int, int);
int v_mkdir(hfsvol *, unsigned long, const char *);
int v_scavenge(hfsvol *);

141
vendors/make_unique.hpp vendored Normal file
View File

@ -0,0 +1,141 @@
// Implementation of C++14's make_unique for C++11 compilers.
//
// This has been tested with:
// - MSVC 11.0 (Visual Studio 2012)
// - gcc 4.6.3
// - Xcode 4.4 (with clang "4.0")
//
// It is based off an implementation proposed by Stephan T. Lavavej for
// inclusion in the C++14 standard:
// http://isocpp.org/files/papers/N3656.txt
// Where appropriate, it borrows the use of MSVC's _VARIADIC_EXPAND_0X macro
// machinery to compensate for lack of variadic templates.
//
// This file injects make_unique into the std namespace, which I acknowledge is
// technically forbidden ([C++11: 17.6.4.2.2.1/1]), but is necessary in order
// to have syntax compatibility with C++14.
//
// I perform compiler version checking for MSVC, gcc, and clang to ensure that
// we don't add make_unique if it is already there (instead, we include
// <memory> to get the compiler-provided one). You can override the compiler
// version checking by defining the symbol COMPILER_SUPPORTS_MAKE_UNIQUE.
//
//
// ===============================================================================
// This file is released into the public domain. See LICENCE for more information.
// ===============================================================================
#pragma once
// If user hasn't specified COMPILER_SUPPORTS_MAKE_UNIQUE then try to figure out
// based on compiler version if std::make_unique is provided.
#if !defined(COMPILER_SUPPORTS_MAKE_UNIQUE)
#if defined(_MSC_VER)
// std::make_unique was added in MSVC 12.0
#if _MSC_VER >= 1800 // MSVC 12.0 (Visual Studio 2013)
#define COMPILER_SUPPORTS_MAKE_UNIQUE
#endif
#elif defined(__clang__)
// std::make_unique was added in clang 3.4, but not until Xcode 6.
// Annoyingly, Apple makes the clang version defines match the version
// of Xcode, not the version of clang.
#define CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
#if defined(__APPLE__) && CLANG_VERSION >= 60000
#define COMPILER_SUPPORTS_MAKE_UNIQUE
#elif !defined(__APPLE__) && CLANG_VERSION >= 30400
#define COMPILER_SUPPORTS_MAKE_UNIQUE
#endif
#elif defined(__GNUC__)
// std::make_unique was added in gcc 4.9, for standards versions greater
// than -std=c++11.
#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= 40900 && __cplusplus > 201103L
#define COMPILER_SUPPORTS_MAKE_UNIQUE
#endif
#endif
#endif
#if defined(COMPILER_SUPPORTS_MAKE_UNIQUE)
// If the compiler supports std::make_unique, then pull in <memory> to get it.
#include <memory>
#else
// Otherwise, the compiler doesn't provide it, so implement it ourselves.
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
namespace std {
template<class _Ty> struct _Unique_if {
typedef unique_ptr<_Ty> _Single_object;
};
template<class _Ty> struct _Unique_if<_Ty[]> {
typedef unique_ptr<_Ty[]> _Unknown_bound;
};
template<class _Ty, size_t N> struct _Unique_if<_Ty[N]> {
typedef void _Known_bound;
};
//
// template< class T, class... Args >
// unique_ptr<T> make_unique( Args&&... args);
//
#if defined(_MSC_VER) && (_MSC_VER < 1800)
// Macro machinery because MSVC 11.0 doesn't support variadic templates.
// The _VARIADIC_EXPAND_0X stuff is defined in <xstddef>
#define _MAKE_UNIQUE( \
TEMPLATE_LIST, PADDING_LIST, LIST, COMMA, X1, X2, X3, X4) \
template<class _Ty COMMA LIST(_CLASS_TYPE)> inline \
typename _Unique_if<_Ty>::_Single_object make_unique(LIST(_TYPE_REFREF_ARG)) \
{ \
return unique_ptr<_Ty>(new _Ty(LIST(_FORWARD_ARG))); \
} \
_VARIADIC_EXPAND_0X(_MAKE_UNIQUE, , , , )
#undef _MAKE_UNIQUE
#else // not MSVC 11.0 or earlier
template<class _Ty, class... Args>
typename _Unique_if<_Ty>::_Single_object
make_unique(Args&&... args) {
return unique_ptr<_Ty>(new _Ty(std::forward<Args>(args)...));
}
#endif
// template< class T >
// unique_ptr<T> make_unique( std::size_t size );
template<class _Ty>
typename _Unique_if<_Ty>::_Unknown_bound
make_unique(size_t n) {
typedef typename remove_extent<_Ty>::type U;
return unique_ptr<_Ty>(new U[n]());
}
// template< class T, class... Args >
// /* unspecified */ make_unique( Args&&... args ) = delete;
// MSVC 11.0 doesn't support deleted functions, so the best we can do
// is simply not define the function.
#if !(defined(_MSC_VER) && (_MSC_VER < 1800))
template<class T, class... Args>
typename _Unique_if<T>::_Known_bound
make_unique(Args&&...) = delete;
#endif
} // namespace std
#endif // !COMPILER_SUPPORTS_MAKE_UNIQUE

738
vendors/path.hpp vendored Normal file
View File

@ -0,0 +1,738 @@
/******************************************************************************
* Copyright (c) 2013 Dan Lecocq
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*****************************************************************************/
#ifndef APATHY__PATH_HPP
#define APATHY__PATH_HPP
/* C++ includes */
#include <vector>
#include <string>
#include <cstring>
#include <cstdlib>
#include <istream>
#include <sstream>
#include <iostream>
#include <iterator>
/* C includes */
#include <glob.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
/* A class for path manipulation */
namespace apathy {
class Path {
public:
/* This is the separator used on this particular system */
#ifdef __MSDOS__
#error "Platforms using backslashes not yet supported"
#else
static const char separator = '/';
#endif
/* A class meant to contain path segments */
struct Segment {
/* The actual string segment */
std::string segment;
Segment(std::string s=""): segment(s) {}
friend std::istream& operator>>(std::istream& stream, Segment& s) {
return std::getline(stream, s.segment, separator);
}
};
/**********************************************************************
* Constructors
*********************************************************************/
/* Default constructor
*
* Points to current directory */
Path(const std::string& path=""): path(path) {}
/* Our generalized constructor.
*
* This enables all sorts of type promotion (like int -> Path) for
* arguments into all the functions below. Anything that
* std::stringstream can support is implicitly supported as well
*
* @param p - path to construct */
template <class T>
Path(const T& p);
/**********************************************************************
* Operators
*********************************************************************/
/* Checks if the paths are exactly the same */
bool operator==(const Path& other) { return path == other.path; }
/* Check if the paths are not exactly the same */
bool operator!=(const Path& other) { return ! (*this == other); }
/* Append the provided segment to the path as a directory. This is the
* same as append(segment)
*
* @param segment - path segment to add to this path */
Path& operator<<(const Path& segment);
/* Append the provided segment to the path as a directory. This is the
* same as append(segment). Returns a /new/ path object rather than a
* reference.
*
* @param segment - path segment to add to this path */
Path operator+(const Path& segment) const;
/* Check if the two paths are equivalent
*
* Two paths are equivalent if they point to the same resource, even if
* they are not exact string matches
*
* @param other - path to compare to */
bool equivalent(const Path& other);
/* Return a string version of this path */
std::string string() const { return path; }
/* Return the name of the file */
std::string filename() const;
/* Return the extension of the file */
std::string extension() const;
/* Return a path object without the extension */
Path stem() const;
/**********************************************************************
* Manipulations
*********************************************************************/
/* Append the provided segment to the path as a directory. Alias for
* `operator<<`
*
* @param segment - path segment to add to this path */
Path& append(const Path& segment);
/* Evaluate the provided path relative to this path. If the second path
* is absolute, then return the second path.
*
* @param rel - path relative to this path to evaluate */
Path& relative(const Path& rel);
/* Move up one level in the directory structure */
Path& up();
/* Turn this into an absolute path
*
* If the path is already absolute, it has no effect. Otherwise, it is
* evaluated relative to the current working directory */
Path& absolute();
/* Sanitize this path
*
* This...
*
* 1) replaces runs of consecutive separators with a single separator
* 2) evaluates '..' to refer to the parent directory, and
* 3) strips out '/./' as referring to the current directory
*
* If the path was absolute to begin with, it will be absolute
* afterwards. If it was a relative path to begin with, it will only be
* converted to an absolute path if it uses enough '..'s to refer to
* directories above the current working directory */
Path& sanitize();
/* Make this path a directory
*
* If this path does not have a trailing directory separator, add one.
* If it already does, this does not affect the path */
Path& directory();
/* Trim this path of trailing separators, up to the leading separator.
* For example, on *nix systems:
*
* assert(Path("///").trim() == "/");
* assert(Path("/foo//").trim() == "/foo");
*/
Path& trim();
/**********************************************************************
* Copiers
*********************************************************************/
/* Return parent path
*
* Returns a new Path object referring to the parent directory. To
* move _this_ path to the parent directory, use the `up` function */
Path parent() const { return Path(Path(*this).up()); }
/**********************************************************************
* Member Utility Methods
*********************************************************************/
/* Returns a vector of each of the path segments in this path */
std::vector<Segment> split() const;
/**********************************************************************
* Type Tests
*********************************************************************/
/* Is the path an absolute path? */
bool is_absolute() const;
/* Does the path have a trailing slash? */
bool trailing_slash() const;
/* Does this path exist?
*
* Returns true if the path can be `stat`d */
bool exists() const;
/* Is this path an existing file?
*
* Only returns true if the path has stat.st_mode that is a regular
* file */
bool is_file() const;
/* Is this path an existing directory?
*
* Only returns true if the path has a stat.st_mode that is a
* directory */
bool is_directory() const;
/* How large is this file?
*
* Returns the file size in bytes. If the file doesn't exist, it
* returns 0 */
size_t size() const;
/**********************************************************************
* Static Utility Methods
*********************************************************************/
/* Return a brand new path as the concatenation of the two provided
* paths
*
* @param a - first part of the path to join
* @param b - second part of the path to join
*/
static Path join(const Path& a, const Path& b);
/* Return a branch new path as the concatenation of each segments
*
* @param segments - the path segments to concatenate
*/
static Path join(const std::vector<Segment>& segments);
/* Current working directory */
static Path cwd();
/* Create a file if one does not exist
*
* @param p - path to create
* @param mode - mode to create with */
static bool touch(const Path& p, mode_t mode=0777);
/* Move / rename a file
*
* @param source - original path
* @param dest - new path
* @param mkdirs - recursively make any needed directories? */
static bool move(const Path& source, const Path& dest,
bool mkdirs=false);
/* Remove a file
*
* @param path - path to remove */
static bool rm(const Path& path);
/* Recursively make directories
*
* @param p - path to recursively make
* @returns true if it was able to, false otherwise */
static bool makedirs(const Path& p, mode_t mode=0777);
/* Recursively remove directories
*
* @param p - path to recursively remove */
static bool rmdirs(const Path& p, bool ignore_errors=false);
/* List all the paths in a directory
*
* @param p - path to list items for */
static std::vector<Path> listdir(const Path& p);
/* Returns all matching globs
*
* @param pattern - the glob pattern to match */
static std::vector<Path> glob(const std::string& pattern);
/* So that we can write paths out to ostreams */
friend std::ostream& operator<<(std::ostream& stream, const Path& p) {
return stream << p.path;
}
private:
/* Our current path */
std::string path;
};
/* Constructor */
template <class T>
inline Path::Path(const T& p): path("") {
std::stringstream ss;
ss << p;
path = ss.str();
}
/**************************************************************************
* Operators
*************************************************************************/
inline Path& Path::operator<<(const Path& segment) {
return append(segment);
}
inline Path Path::operator+(const Path& segment) const {
Path result(path);
result.append(segment);
return result;
}
inline bool Path::equivalent(const Path& other) {
/* Make copies of both paths, sanitize, and ensure they're equal */
return Path(path).absolute().sanitize() ==
Path(other).absolute().sanitize();
}
inline std::string Path::filename() const {
size_t pos = path.rfind(separator);
if (pos != std::string::npos) {
return path.substr(pos + 1);
}
return path; // FIXED -- Filename for relative files
}
inline std::string Path::extension() const {
/* Make sure we only look in the filename, and not the path */
std::string name = filename();
size_t pos = name.rfind('.');
if (pos != std::string::npos) {
return name.substr(pos + 1);
}
return "";
}
inline Path Path::stem() const {
size_t sep_pos = path.rfind(separator);
size_t dot_pos = path.rfind('.');
if (dot_pos == std::string::npos) {
return Path(*this);
}
if (sep_pos == std::string::npos || sep_pos < dot_pos) {
return Path(path.substr(0, dot_pos));
} else {
return Path(*this);
}
}
/**************************************************************************
* Manipulators
*************************************************************************/
inline Path& Path::append(const Path& segment) {
/* First, check if the last character is the separator character.
* If not, then append one and then the segment. Otherwise, just
* the segment */
if (!trailing_slash()) {
path.push_back(separator);
}
path.append(segment.path);
return *this;
}
inline Path& Path::relative(const Path& rel) {
if (!rel.is_absolute()) {
return append(rel);
} else {
operator=(rel);
return *this;
}
}
inline Path& Path::up() {
/* Make sure we turn this into an absolute url if it's not already
* one */
if (path.size() == 0) {
path = "..";
return directory();
}
append("..").sanitize();
if (path.size() == 0) {
return *this;
}
return directory();
}
inline Path& Path::absolute() {
/* If the path doesn't begin with our separator, then it's not an
* absolute path, and should be appended to the current working
* directory */
if (!is_absolute()) {
/* Join our current working directory with the path */
operator=(join(cwd(), path));
}
return *this;
}
inline Path& Path::sanitize() {
/* Split the path up into segments */
std::vector<Segment> segments(split());
/* We may have to test this repeatedly, so let's check once */
bool relative = !is_absolute();
/* Now, we'll create a new set of segments */
std::vector<Segment> pruned;
for (size_t pos = 0; pos < segments.size(); ++pos) {
/* Skip over empty segments and '.' */
if (segments[pos].segment.size() == 0 ||
segments[pos].segment == ".") {
continue;
}
/* If there is a '..', then pop off a parent directory. However, if
* the path was relative to begin with, if the '..'s exceed the
* stack depth, then they should be appended to our path. If it was
* absolute to begin with, and we reach root, then '..' has no
* effect */
if (segments[pos].segment == "..") {
if (relative) {
if (pruned.size() && pruned.back().segment != "..") {
pruned.pop_back();
} else {
pruned.push_back(segments[pos]);
}
} else if (pruned.size()) {
pruned.pop_back();
}
continue;
}
pruned.push_back(segments[pos]);
}
bool was_directory = trailing_slash();
if (!relative) {
path = std::string(1, separator) + Path::join(pruned).path;
if (was_directory) {
return directory();
}
return *this;
}
/* It was a relative path */
path = Path::join(pruned).path;
if (path.length() && was_directory) {
return directory();
}
return *this;
}
inline Path& Path::directory() {
trim();
path.push_back(separator);
return *this;
}
inline Path& Path::trim() {
if (path.length() == 0) { return *this; }
size_t p = path.find_last_not_of(separator);
if (p != std::string::npos) {
path.erase(p + 1, path.size());
} else {
path = "";
}
return *this;
}
/**************************************************************************
* Member Utility Methods
*************************************************************************/
/* Returns a vector of each of the path segments in this path */
inline std::vector<Path::Segment> Path::split() const {
std::stringstream stream(path);
std::istream_iterator<Path::Segment> start(stream);
std::istream_iterator<Path::Segment> end;
std::vector<Path::Segment> results(start, end);
if (trailing_slash()) {
results.push_back(Path::Segment(""));
}
return results;
}
/**************************************************************************
* Tests
*************************************************************************/
inline bool Path::is_absolute() const {
return path.size() && path[0] == separator;
}
inline bool Path::trailing_slash() const {
return path.size() && path[path.length() - 1] == separator;
}
inline bool Path::exists() const {
struct stat buf;
if (stat(path.c_str(), &buf) != 0) {
return false;
}
return true;
}
inline bool Path::is_file() const {
struct stat buf;
if (stat(path.c_str(), &buf) != 0) {
return false;
} else {
return S_ISREG(buf.st_mode);
}
}
inline bool Path::is_directory() const {
struct stat buf;
if (stat(path.c_str(), &buf) != 0) {
return false;
} else {
return S_ISDIR(buf.st_mode);
}
}
inline size_t Path::size() const {
struct stat buf;
if (stat(path.c_str(), &buf) != 0) {
return 0;
} else {
return buf.st_size;
}
}
/**************************************************************************
* Static Utility Methods
*************************************************************************/
inline Path Path::join(const Path& a, const Path& b) {
Path p(a);
p.append(b);
return p;
}
inline Path Path::join(const std::vector<Segment>& segments) {
std::string path;
/* Now, we'll go through the segments, and join them with
* separator */
std::vector<Segment>::const_iterator it(segments.begin());
for(; it != segments.end(); ++it) {
path += it->segment;
if (it + 1 != segments.end()) {
path += std::string(1, separator);
}
}
return Path(path);
}
inline Path Path::cwd() {
Path p;
char * buf = getcwd(NULL, 0);
if (buf != NULL) {
p = std::string(buf);
free(buf);
} else {
perror("cwd");
}
/* Ensure this is a directory */
p.directory();
return p;
}
inline bool Path::touch(const Path& p, mode_t mode) {
int fd = open(p.path.c_str(), O_RDONLY | O_CREAT, mode);
if (fd == -1) {
makedirs(p);
fd = open(p.path.c_str(), O_RDONLY | O_CREAT, mode);
if (fd == -1) {
return false;
}
}
if (close(fd) == -1) {
perror("touch close");
return false;
}
return true;
}
inline bool Path::move(const Path& source, const Path& dest,
bool mkdirs) {
int result = rename(source.path.c_str(), dest.path.c_str());
if (result == 0) {
return true;
}
/* Otherwise, there was an error */
if (errno == ENOENT && mkdirs) {
makedirs(dest.parent());
return rename(source.path.c_str(), dest.path.c_str()) == 0;
}
return false;
}
inline bool Path::rm(const Path& path) {
if (remove(path.path.c_str()) != 0) {
perror("Remove");
return false;
}
return true;
}
inline bool Path::makedirs(const Path& p, mode_t mode) {
/* We need to make a copy of the path, that's an absolute path */
Path abs = Path(p).absolute();
/* Now, we'll try to make the directory / ensure it exists */
if (mkdir(abs.string().c_str(), mode) == 0) {
return true;
}
/* Otherwise, there was an error. There are some errors that
* may be recoverable */
if (errno == EEXIST) {
return abs.is_directory();
} else if(errno == ENOENT) {
/* We'll need to try to recursively make this directory. We
* don't need to worry about reaching the '/' path, and then
* getting to this point, because / always exists */
makedirs(abs.parent(), mode);
if (mkdir(abs.string().c_str(), mode) == 0) {
return true;
} else {
perror("makedirs");
return false;
}
} else {
perror("makedirs");
}
/* If it's none of these cases, then it's one of unrecoverable
* errors described in mkdir(2) */
return false;
}
inline bool Path::rmdirs(const Path& p, bool ignore_errors) {
/* If this path isn't a file, then complain */
if (!p.is_directory()) {
return false;
}
/* First, we list out all the members of the path, and anything
* that's a directory, we rmdirs(...) it. If it's a file, then we
* remove it */
std::vector<Path> subdirs(listdir(p));
std::vector<Path>::iterator it(subdirs.begin());
for (; it != subdirs.end(); ++it) {
if (it->is_directory() && !rmdirs(*it) && !ignore_errors) {
std::cout << "Failed rmdirs " << it->string() << std::endl;
} else if (it->is_file() &&
remove(it->path.c_str()) != 0 && !ignore_errors) {
std::cout << "Failed remove " << it->string() << std::endl;
}
}
/* Lastly, try to remove the directory itself */
bool result = (remove(p.path.c_str()) == 0);
errno = 0;
return result;
}
/* List all the paths in a directory
*
* @param p - path to list items for */
inline std::vector<Path> Path::listdir(const Path& p) {
Path base(p);
base.absolute();
std::vector<Path> results;
DIR* dir = opendir(base.string().c_str());
if (dir == NULL) {
/* If there was an error, return an empty vector */
return results;
}
/* Otherwise, go through everything */
for (dirent* ent = readdir(dir); ent != NULL; ent = readdir(dir)) {
Path cpy(base);
/* Skip the parent directory listing */
if (!strcmp(ent->d_name, "..")) {
continue;
}
/* Skip the self directory listing */
if (!strcmp(ent->d_name, ".")) {
continue;
}
cpy.relative(ent->d_name);
results.push_back(cpy);
}
errno = 0;
closedir(dir);
return results;
}
inline std::vector<Path> Path::glob(const std::string& pattern) {
/* First, we need a glob_t, and then we'll look at the results */
glob_t globbuf;
if (::glob(pattern.c_str(), 0, NULL, &globbuf) != 0) {
/* Then there was an error */
return std::vector<Path>();
}
std::vector<Path> results;
for (std::size_t i = 0; i < globbuf.gl_pathc; ++i) {
results.push_back(globbuf.gl_pathv[i]);
}
return results;
}
}
// Include "Path" class in global namespace.
using apathy::Path;
#endif