mirror of
https://github.com/ParksProjets/Maconv.git
synced 2024-06-03 19:29:28 +00:00
Release Maconv v1.0
This commit is contained in:
commit
ea24dbaeff
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/build/
|
86
CMakeLists.txt
Normal file
86
CMakeLists.txt
Normal 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
621
LICENSE
Normal 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
42
README.md
Normal 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
20
docs/README.md
Normal 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
0
docs/file-signature.md
Normal file
13
docs/formats/AppleDouble.md
Normal file
13
docs/formats/AppleDouble.md
Normal 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
|
60
docs/formats/AppleSingle.md
Normal file
60
docs/formats/AppleSingle.md
Normal 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 | File’s 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
9
docs/formats/BinHex.md
Normal 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
76
docs/formats/MacBinary.md
Normal 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
37
docs/libHFS.md
Normal 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.
|
72
docs/licenses/Retro68-Runtime.txt
Normal file
72
docs/licenses/Retro68-Runtime.txt
Normal 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
674
docs/licenses/Retro68.txt
Normal 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>.
|
528
docs/licenses/TheUnarchiver.txt
Normal file
528
docs/licenses/TheUnarchiver.txt
Normal 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
3
docs/stuffit/Stuffit.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Stuffit specifications
|
||||||
|
|
||||||
|
TODO.
|
50
docs/stuffit/Stuffit_v1.md
Normal file
50
docs/stuffit/Stuffit_v1.md
Normal 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
|
86
docs/stuffit/Stuffit_v5.md
Normal file
86
docs/stuffit/Stuffit_v5.md
Normal 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
115
src/commands.cc
Normal 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
52
src/commands.h
Normal 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
120
src/conv/appledouble.cc
Normal 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
177
src/conv/applesingle.cc
Normal 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
56
src/conv/binhex.cc
Normal 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
64
src/conv/converters.h
Normal 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
207
src/conv/macbinary.cc
Normal 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
131
src/conv/rsrc.cc
Normal 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
44
src/disk/disk.h
Normal 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
157
src/disk/extract.cc
Normal 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
170
src/disk/pack.cc
Normal 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
|
140
src/formats/file_signature.cc
Normal file
140
src/formats/file_signature.cc
Normal 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
|
69
src/formats/file_signature.h
Normal file
69
src/formats/file_signature.h
Normal 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
124
src/formats/formats.cc
Normal 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
111
src/formats/formats.h
Normal 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
56
src/formats/pack.cc
Normal 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
97
src/formats/unpack.cc
Normal 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
92
src/fs/file.cc
Normal 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
78
src/fs/file.h
Normal 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
124
src/fs/file_reader.cc
Normal 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
87
src/fs/file_reader.h
Normal 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
121
src/fs/file_writer.cc
Normal 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
67
src/fs/file_writer.h
Normal 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
134
src/maconv.1
Normal 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
137
src/main.cc
Normal 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
101
src/stuffit/methods.cc
Normal 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
87
src/stuffit/methods.h
Normal 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
|
487
src/stuffit/methods/algorithm13.cc
Normal file
487
src/stuffit/methods/algorithm13.cc
Normal 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
|
71
src/stuffit/methods/algorithm13.h
Normal file
71
src/stuffit/methods/algorithm13.h
Normal 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
|
336
src/stuffit/methods/arsenic.cc
Normal file
336
src/stuffit/methods/arsenic.cc
Normal 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
|
122
src/stuffit/methods/arsenic.h
Normal file
122
src/stuffit/methods/arsenic.h
Normal 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
|
174
src/stuffit/methods/compress.cc
Normal file
174
src/stuffit/methods/compress.cc
Normal 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
|
102
src/stuffit/methods/compress.h
Normal file
102
src/stuffit/methods/compress.h
Normal 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
|
78
src/stuffit/methods/rle90.cc
Normal file
78
src/stuffit/methods/rle90.cc
Normal 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
|
48
src/stuffit/methods/rle90.h
Normal file
48
src/stuffit/methods/rle90.h
Normal 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
137
src/stuffit/stuffit.cc
Normal 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
88
src/stuffit/stuffit.h
Normal 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
152
src/stuffit/stuffit_v1.cc
Normal 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
203
src/stuffit/stuffit_v5.cc
Normal 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
76
src/stuffit/utils/bwt.cc
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Burrows–Wheeler 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
51
src/stuffit/utils/bwt.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Burrows–Wheeler 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
78
src/stuffit/utils/crc.cc
Normal 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
41
src/stuffit/utils/crc.h
Normal 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
|
275
src/stuffit/utils/huffman.cc
Normal file
275
src/stuffit/utils/huffman.cc
Normal 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
122
src/stuffit/utils/huffman.h
Normal 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
128
src/utils/bit_reader.cc
Normal 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
100
src/utils/bit_reader.h
Normal 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
119
src/utils/buffer_stream.cc
Normal 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
69
src/utils/buffer_stream.h
Normal 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
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
46
vendors/libhfs/CMakeLists.txt
vendored
Normal 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
272
vendors/libhfs/apple.h
vendored
Normal 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
807
vendors/libhfs/block.c
vendored
Normal 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
40
vendors/libhfs/block.h
vendored
Normal 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
700
vendors/libhfs/btree.c
vendored
Normal 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
33
vendors/libhfs/btree.h
vendored
Normal 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
41
vendors/libhfs/config.h.in
vendored
Normal 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
481
vendors/libhfs/data.c
vendored
Normal 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
66
vendors/libhfs/data.h
vendored
Normal 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
520
vendors/libhfs/file.c
vendored
Normal 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
45
vendors/libhfs/file.h
vendored
Normal 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
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
188
vendors/libhfs/hfs.h
vendored
Normal 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
226
vendors/libhfs/libhfs.h
vendored
Normal 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
516
vendors/libhfs/libhfs.txt
vendored
Normal 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
470
vendors/libhfs/low.c
vendored
Normal 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
44
vendors/libhfs/low.h
vendored
Normal 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
318
vendors/libhfs/medium.c
vendored
Normal 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
42
vendors/libhfs/medium.h
vendored
Normal 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
473
vendors/libhfs/node.c
vendored
Normal 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
34
vendors/libhfs/node.h
vendored
Normal 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
188
vendors/libhfs/os-unix.c
vendored
Normal 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
29
vendors/libhfs/os.h
vendored
Normal 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
557
vendors/libhfs/record.c
vendored
Normal 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
47
vendors/libhfs/record.h
vendored
Normal 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
29
vendors/libhfs/version.c
vendored
Normal 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
26
vendors/libhfs/version.h
vendored
Normal 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
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
62
vendors/libhfs/volume.h
vendored
Normal 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
141
vendors/make_unique.hpp
vendored
Normal 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
738
vendors/path.hpp
vendored
Normal 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
|
Loading…
Reference in New Issue
Block a user