ref: ce1852b585349f8244fb132b199004f22d2a99fb
author: CYBERDEViL <cyberdevil@notabug.org>
date: Wed May 3 14:22:39 EDT 2023
Initial commit
--- /dev/null
+++ b/COPYING
@@ -1,0 +1,674 @@
+ 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
+
+ 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 <https://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
+<https://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
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
--- /dev/null
+++ b/Makefile
@@ -1,0 +1,22 @@
+CC=gcc
+CFLAGS=-Wall #-fanalyzer
+
+all: rewise
+
+rewise: src/rewise.c \
+ src/print.c src/print.h \
+ src/reader.c src/reader.h \
+ src/crc32.c src/crc32.h \
+ src/inflate.c src/inflate.h \
+ src/pefile.c src/pefile.h \
+ src/wisescript.c src/wisescript.c \
+ src/wiseoverlay.c src/wiseoverlay.h \
+ src/version.h \
+ src/errors.h
+ $(CC) src/rewise.c src/print.c src/reader.c src/crc32.c src/inflate.c \
+ src/pefile.c src/wisescript.c src/wiseoverlay.c -o $@ -I./src \
+ $(CFLAGS)
+
+
+clean:
+ rm -f rewise
--- /dev/null
+++ b/README.md
@@ -1,0 +1,69 @@
+# Reverse Engineering Wise
+
+Extract files from Wise installers without executing them.
+
+The aim of this project is to extract assets from old game installers made with
+Wise installer without executing the PE file (.exe), so they can be used with
+free software implementations of the game engine.
+
+```bash
+==============================================================
+ Welcome to REWise version 0.1.0
+==============================================================
+
+ Usage: rewise [OPERATION] [OPTIONS] INPUT_FILE
+
+ OPERATIONS
+ -x --extract OUTPUT_PATH Extract files.
+ -r --raw OUTPUT_PATH Extract all files in the overlay data. This does not move/rename files!
+ -l --list List files.
+ -V --verify Run extract without actually outputting files, crc32s will be checked.
+ -z --script-debug Print parsed WiseScript.bin
+ -v --version Print version and exit.
+ -h --help Display this HELP.
+
+ OPTIONS
+ -p --preserve Don't delete TMP files.
+ -t --tmp-path TMP_PATH Set temporary path, default: /tmp/
+ -d --debug Print debug info.
+ -s --silent Be silent, don't print anything.
+ -n --no-extract Don't extract anything. This will be ignored with -x or -r. It also will not try to remove TMP files, so -p won't do anything.
+
+ NOTES
+ - Path to directory OUTPUT_PATH and TMP_PATH should exist.
+```
+
+
+# State
+
+## What can be done better
+
+In general:
+
+ - Error handling.
+ - The inflate process is currently custom made with
+ `https://github.com/mnadareski/WiseUnpacker` as a source of information. It
+ is doing fine but it probably will be better and faster with a `zlib`
+ implementation.
+
+Values that are currently calculated that might be in the WiseHeader,
+somewhere in WiseScript.bin or a constant defined somewhere else are:
+
+ - Total inflated size
+ - The deflate-offset that is added to deflateStart defined in the
+ WiseScript.bin file.
+
+Other values that are of interest but not found yet are:
+
+ - To determine what Wise package/version was used other then the PE build
+ date.
+
+## Things that might be a problem
+
+ - REWise is only tested on Little Endian systems.
+
+
+# Other projects
+
+ - https://github.com/mnadareski/WiseUnpacker
+ - https://github.com/lmop/exwise
--- /dev/null
+++ b/imhex/WISESCRIPT.hexpat
@@ -1,0 +1,224 @@
+struct Header {+ char unknown_0000[44];
+};
+
+struct String {+ char value[];
+};
+
+struct InstStruct1 {+ char log_path[];
+ char font[];
+ char unknown_0100[14];
+ String ui_strings_1[56];
+};
+
+// 0x00 Wise deflated file header
+struct FileHeader {+ char unknown_2[2]; // 0x8000, 0x8100, 0x0000, 0x9800 0xA100
+ u32 deflateStart;
+ u32 deflateEnd;
+ u16 date;
+ u16 time;
+ u32 inflatedSize;
+ char unknown_20[20];
+ u32 crc32;
+ char filePath[];
+ char fileText[];
+ char terminator;
+};
+
+struct Unknown_0x03 {+ char unknown_1;
+ char string_1[];
+ char string_2[];
+};
+
+struct FormItem_0x04 {+ u8 no;
+ char command_string_1[];
+ // read this struct again until 'no' == 0 ???
+};
+
+struct Unknown_0x05 {+ // write .ini file?
+ char file[];
+ char heading[];
+ char values[];
+};
+
+struct Unknown_0x06 {+ char unknown[6];
+ u32 deflateStart;
+ u32 deflateEnd;
+ u32 inflatedSize;
+ char terminator;
+};
+
+struct Unknown_0x07 {+ char unknown_1;
+ char string_1[];
+ char string_2[];
+
+ if (unknown_1 == 0x02) {+ char string_3[];
+ }
+};
+
+struct Unknown_0x08 {+ char unknown_1;
+};
+
+struct Unknown_0x09 {+ // 0x0901 and 0x0920 are different
+ char unknown_1;
+ if (unknown_1 != 0x01 && unknown_1 != 0x20) {+ char unknown_2;
+ }
+ char string_1[];
+ char string_2[];
+ char string_3[];
+ char string_4[];
+ if (unknown_1 == 0x01 || unknown_1 == 0x20) {+ char string_5[];
+ }
+};
+
+struct Unknown_0x11 {+ char string_1[];
+};
+
+// Existing file on install medium (CD/DVD)
+struct ExistingFile_0x12 {+ char unknown_1; // 0C
+ char unknown_41[41];
+ char from_path[];
+ char unknown_string_1[];
+ char unknown_string_2[];
+ char to_path[];
+};
+
+struct Unknown_0x14 {+ u32 deflatedStart;
+ u32 deflatedEnd;
+ u32 inflatedSize;
+ char name[];
+ char message[];
+};
+
+struct Unknown_0x15 {+ char unknown_1;
+ char string_1[];
+ char string_2[];
+};
+
+struct TempFile_0x16 {+ char name[];
+};
+
+struct Unknown_0x17 {+ char unknown_1;
+ char unknown_4[4];
+ char string_1[];
+};
+
+struct Unknown_0x18 {+ // skip tailing zeros
+};
+
+struct Unknown_0x23 { // option?+ char unknown_1;
+ char string_1[];
+ char string_2[];
+};
+
+struct Unknown_0x31 {+ char unknown_1; // always 0x7F ?
+ char string_1[];
+};
+
+struct Unknown_0x0A {+ char unknown_2[2]; // 0x0200
+ char string_1[];
+ char string_2[];
+ char string_3[];
+};
+
+struct Unknown_0x0B {+ char unknown_1;
+ char string_1[];
+};
+
+struct Unknown_0x0C {+ char unknown_1;
+ char string_1[];
+ char string_2[];
+};
+
+struct Unknown_0x1C {+ char string_1[];
+};
+
+struct Unknown_0x1E {+ char unknown_2[2];
+};
+
+struct Container {+ char op;
+
+ match (op) {+ (0x00): FileHeader fileHeader;
+ (0x03): Unknown_0x03 unknown_0x03;
+ (0x04): FormItem_0x04 formItem;
+ (0x05): Unknown_0x05 unknown_0x05;
+ (0x06): Unknown_0x06 unknown_0x06;
+ (0x07): Unknown_0x07 unknown_0x07;
+ (0x08): Unknown_0x08 unknown_0x08;
+ (0x09): Unknown_0x09 unknown_0x09;
+ (0x11): Unknown_0x11 unknown_0x11;
+ (0x12): ExistingFile_0x12 existingFile_0x12;
+ (0x14): Unknown_0x14 unknown_0x14;
+ (0x15): Unknown_0x15 unknown_0x15;
+ (0x16): TempFile_0x16 tempFile_0x16;
+ (0x17): Unknown_0x17 unknown_0x17;
+ (0x23): Unknown_0x23 unknown_0x23;
+ (0x0A): Unknown_0x0A unknown_0x0A;
+ (0x0B): Unknown_0x0B unknown_0x0B;
+ (0x0C): Unknown_0x0C unknown_0x0C;
+ (0x1B): continue;
+ (0x1C): Unknown_0x1C unknown_0x1C;
+ (0x1E): Unknown_0x1E unknown_0x1E;
+ }
+};
+
+
+/*
+
+NAME MD5 FILE
+---- --- ----
+HL:CS (CD) 43cd9aff74e04fa1b59321b898d9a6bd counter-strike.exe
+HLGOTY (CD) f5b8b35ca02beeeb146e62a63a0273a6 SETUP.EXE
+CS15 eedcfcf6545cef92f26fb9a7fdd77c42 csv15full.exe
+RTCW (CD) f2d9e3e1eaaed66047210881d130384f Setup.exe
+ET 5cc104767ecdf0feb3a36210adf46a8e WolfET.exe
+
+
+Example:
+
+// NOTE: Set absolute path to WISEINSTALL.hexpat
+#include </home/user/code/WISESCRIPT.hexpat>
+
+struct MainStruct {+ Header header;
+ InstStruct1 install_struct_1;
+
+ // HL:GOTY 6536
+ // RTCW 403
+ // HL:CS 29701
+ // CS15 6253
+ // ET 403
+ Container items[390];
+};
+
+MainStruct mainStruct @0x0;
+*/
--- /dev/null
+++ b/src/crc32.c
@@ -1,0 +1,145 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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 <stdio.h>
+
+#include "crc32.h"
+
+
+// CRC32 lookup table
+static uint32_t CRC32_LOOKUP[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
+};
+
+
+uint32_t crc32Finalize(uint32_t value) {+ return ~value;
+}
+
+
+uint32_t crc32UpdateByte(uint32_t value, unsigned char byte) {+ return CRC32_LOOKUP[(value ^ byte) & 255] ^ (value >> 8);
+}
+
+
+uint32_t crc32Update(uint32_t value, unsigned char * buff, uint32_t size) {+ if (size == 0) {+ return CRC32_NEW;
+ }
+ for (uint32_t i=0; i < size; i++) {+ value = crc32UpdateByte(value, buff[i]);
+ }
+ return value;
+}
+
+
+// Used to generate the lookup table
+#ifdef REWISE_DEV_DEBUG
+void crc32BuildTable(void) {+ for (uint32_t i=0; i < 256; i++) {+ uint32_t value = i << 1;
+ for (int x=8; x>=0; x--) {+ if ((value & 1) == 1) {+ value = (value >> 1) ^ CRC32_SEED;
+ }
+ else {+ value = (value >> 1);
+ }
+ }
+ CRC32_LOOKUP[i] = value;
+ }
+}
+
+void crc32PrintTable(void) {+ uint8_t colCount = 4;
+ for (uint32_t i=0; i<256; i++) {+ printf("0x%08X", CRC32_LOOKUP[i]);+ if ((i+1) % colCount) {+ printf(", ");+ }
+ else
+ if (i == 255) {+ printf("\n");+ }
+ else {+ printf(",\n");+ }
+ }
+}
+#endif
--- /dev/null
+++ b/src/crc32.h
@@ -1,0 +1,39 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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/>.
+ */
+
+#ifndef H_REWISE_CRC32
+#define H_REWISE_CRC32
+
+#include <stdint.h>
+
+// https://wiki.osdev.org/CRC32
+
+#define CRC32_SEED 0xedb88320
+#define CRC32_NEW 0xffffffff
+
+
+uint32_t crc32Finalize(uint32_t value);
+uint32_t crc32UpdateByte(uint32_t value, unsigned char byte);
+uint32_t crc32Update(uint32_t value, unsigned char * buff, uint32_t size);
+
+#ifdef REWISE_DEV_DEBUG
+// Don't use these, they where used to generate the lookup table.
+// Preserve to give insight on how the 'CRC32_LOOKUP' array is build.
+void crc32BuildTable(void);
+void crc32PrintTable(void);
+#endif
+
+#endif
--- /dev/null
+++ b/src/errors.h
@@ -1,0 +1,28 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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/>.
+ */
+
+#ifndef H_REWISE_ERRORS
+#define H_REWISE_ERRORS
+
+typedef enum {+ REWERR_OK = 0, // Success
+ REWERR_EOF = 1, // End of file
+ REWERR_NOOPT = 2, // Invalid operation (WiseScript parse)
+ REWERR_INVALDAT = 3, // Invalid data (WiseScript parse)
+ REWERR_ERRNO = 4 // Check errno
+} REWError;
+
+#endif
--- /dev/null
+++ b/src/inflate.c
@@ -1,0 +1,1189 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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 <errno.h>
+
+#include "inflate.h"
+#include "errors.h"
+#include "print.h"
+
+
+static HuffmanNode * FixedLiteralTree = NULL;
+static HuffmanNode * FixedDistanceTree = NULL;
+
+// Constant defined in the PKZIP APPNOTE (5.5.3 Expanding Huffman Codes)
+// Also here https://www.rfc-editor.org/rfc/rfc1951#page-13
+static unsigned char CODE_LENGTH_ORDER[19] = {+ 0x10, 0x11, 0x12, 0x00, 0x08, 0x07, 0x09, 0x06, 0x0A, 0x05, 0x0B, 0x04,
+ 0x0C, 0x03, 0x0D, 0x02, 0x0E, 0x01, 0x0F
+};
+
+// DEFLATE static dictionary (Bit reduction)
+static int LENGTH_CODE_VALUE_OFFSET[286] = {+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000003, 0x00000004, 0x00000005,
+ 0x00000006, 0x00000007, 0x00000008, 0x00000009,
+ 0x0000000A, 0x0000000B, 0x0000000D, 0x0000000F,
+ 0x00000011, 0x00000013, 0x00000017, 0x0000001B,
+ 0x0000001F, 0x00000023, 0x0000002B, 0x00000033,
+ 0x0000003B, 0x00000043, 0x00000053, 0x00000063,
+ 0x00000073, 0x00000083, 0x000000A3, 0x000000C3,
+ 0x000000E3, 0x00000102
+
+};
+
+static unsigned char LENGTH_CODE_VALUE_EXTRA_BITS[286] = {+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
+ 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04,
+ 0x04, 0x05, 0x05, 0x05, 0x05, 0x00
+};
+
+static int DISTANCE_CODE_VALUE_OFFSET[30] = {+ 0x00000001, 0x00000002, 0x00000003, 0x00000004,
+ 0x00000005, 0x00000007, 0x00000009, 0x0000000D,
+ 0x00000011, 0x00000019, 0x00000021, 0x00000031,
+ 0x00000041, 0x00000061, 0x00000081, 0x000000C1,
+ 0x00000101, 0x00000181, 0x00000201, 0x00000301,
+ 0x00000401, 0x00000601, 0x00000801, 0x00000C01,
+ 0x00001001, 0x00001801, 0x00002001, 0x00003001,
+ 0x00004001, 0x00006001
+};
+
+static unsigned char DISTANCE_CODE_VALUE_EXTRA_BITS[30] = {+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02,
+ 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06,
+ 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0A, 0x0A,
+ 0x0B, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D
+};
+
+
+/* newHuffmanNode() - Create new Huffman node. This allocates memory and inits
+ * the new node.
+ *
+ * @returns: Pointer to the new Huffman node or NULL on error. */
+HuffmanNode * newHuffmanNode(void) {+ HuffmanNode * newNode = NULL;
+ newNode = malloc(sizeof(HuffmanNode));
+
+ if (newNode == NULL) {+ printError("newHuffmanNode errno: %s\n", strerror(errno));+ return NULL;
+ }
+
+ newNode->value = 0xffff;
+ newNode->childeren[HUFFMAN_LEFT] = NULL;
+ newNode->childeren[HUFFMAN_RIGHT] = NULL;
+ newNode->leafes[HUFFMAN_LEFT] = false;
+ newNode->leafes[HUFFMAN_RIGHT] = false;
+
+ return newNode;
+}
+
+
+/* HuffmanAddCode() - Adds new code to a Huffman tree.
+ *
+ * @param node : Node to add the code to.
+ * @param codeLength: The traverse count to the node we want to set the value
+ * to (count from given 'node').
+ * @param codeValue : The value to set.
+ *
+ * @returns: 'true' on success, 'false' on error. */
+bool HuffmanAddCode(HuffmanNode * node, unsigned char codeLength, int codeValue) {+ bool result = false;
+
+ if (codeLength == 0) {+ printError("HuffmanAddCode codeLength == 0\n");+ return result;
+ }
+
+ // Left child of current node is not a end node
+ if (node->leafes[HUFFMAN_LEFT] == false) {+ // Add new left node
+ if (node->childeren[HUFFMAN_LEFT] == NULL) {+ HuffmanNode * newNode = newHuffmanNode();
+ if (newNode == NULL) {+ return false;
+ }
+ node->childeren[HUFFMAN_LEFT] = newNode;
+ }
+
+ // Left (new) child is a end node
+ if (codeLength == 1) {+ node->leafes[HUFFMAN_LEFT] = true;
+ node->childeren[HUFFMAN_LEFT]->value = codeValue;
+ result = true;
+ }
+ else {+ result = HuffmanAddCode(node->childeren[HUFFMAN_LEFT], codeLength - 1, codeValue);
+ }
+ }
+
+ if (result == false) {+ // Right child of current node is not a end node
+ if (node->leafes[HUFFMAN_RIGHT] == false) {+ // Set left child as a end node ???
+ node->leafes[HUFFMAN_LEFT] = true;
+
+ // Add new right node
+ if (node->childeren[HUFFMAN_RIGHT] == NULL) {+ HuffmanNode * newNode = newHuffmanNode();
+ if (newNode == NULL) {+ return false;
+ }
+ node->childeren[HUFFMAN_RIGHT] = newNode;
+ }
+
+ // The new right child is a end node
+ if (codeLength == 1) {+ node->leafes[HUFFMAN_RIGHT] = true;
+ node->childeren[HUFFMAN_RIGHT]->value = codeValue;
+ result = true;
+ }
+ else {+ result = HuffmanAddCode(node->childeren[HUFFMAN_RIGHT], codeLength - 1, codeValue);
+ }
+ }
+ else {+ node->leafes[HUFFMAN_RIGHT] = true;
+ }
+ }
+
+ return result;
+}
+
+
+/* huffmanFreeTree() - Free a Huffman tree.
+ *
+ * @param node: Root node of the Huffman tree. */
+void huffmanFreeTree(HuffmanNode * node) {+ if (node->childeren[HUFFMAN_LEFT] != NULL) {+ huffmanFreeTree(node->childeren[HUFFMAN_LEFT]);
+ node->childeren[HUFFMAN_LEFT] = NULL;
+ }
+
+ if (node->childeren[HUFFMAN_RIGHT] != NULL) {+ huffmanFreeTree(node->childeren[HUFFMAN_RIGHT]);
+ node->childeren[HUFFMAN_RIGHT] = NULL;
+ }
+
+ free(node);
+}
+
+
+/* huffmanInitFixedTrees() - Build fixed DEFLATE Huffman tree.
+ * NOTE: huffmanFreeFixedTrees() should be called when this has returned true!
+ **/
+bool huffmanInitFixedTrees(void) {+ HuffmanNode * literalTree = newHuffmanNode();
+ if (literalTree == NULL) {+ return false;
+ }
+
+ // 256 - 279
+ for (uint16_t value=256; value < 280; value++) {+ if (HuffmanAddCode(literalTree, 7, value) == false) {+ huffmanFreeTree(literalTree);
+ return false;
+ }
+ }
+
+ // 0 - 143
+ for (uint16_t value=0; value < 144; value++) {+ if (HuffmanAddCode(literalTree, 8, value) == false) {+ huffmanFreeTree(literalTree);
+ return false;
+ }
+ }
+
+ // 280 - 287
+ for (uint16_t value=280; value < 288; value++) {+ if (HuffmanAddCode(literalTree, 8, value) == false) {+ huffmanFreeTree(literalTree);
+ return false;
+ }
+ }
+
+ // 144 - 255
+ for (uint16_t value=144; value < 256; value++) {+ if (HuffmanAddCode(literalTree, 9, value) == false) {+ huffmanFreeTree(literalTree);
+ return false;
+ }
+ }
+
+ HuffmanNode * distanceTree = newHuffmanNode();
+ if (distanceTree == NULL) {+ huffmanFreeTree(literalTree);
+ return false;
+ }
+
+ // 0 - 31
+ for (unsigned char value=0; value < 32; value++) {+ if (HuffmanAddCode(distanceTree, 5, value) == false) {+ huffmanFreeTree(literalTree);
+ huffmanFreeTree(distanceTree);
+ return false;
+ }
+ }
+
+ FixedLiteralTree = literalTree;
+ FixedDistanceTree = distanceTree;
+
+ return true;
+}
+
+
+void huffmanFreeFixedTrees(void) {+ huffmanFreeTree(FixedLiteralTree);
+ huffmanFreeTree(FixedDistanceTree);
+ FixedLiteralTree = NULL;
+ FixedDistanceTree = NULL;
+}
+
+
+// forward deceleration
+int inflateReadBits(InflateObject * inflateObj, uint8_t bitCount);
+
+
+// return value larger then 0x11e is error
+/* huffmanReadValue() - Keep reading 1 bit until we arrive at a leaf of the
+ * given 'tree' and return that value.
+ *
+ * @param inflateObj: Inflate descriptor.
+ * @param tree : The Huffman tree for resolving the value.
+ *
+ * @returns: The real value, or 0xffffffff on error. */
+int huffmanReadValue(InflateObject * inflateObj, HuffmanNode * tree) {+ HuffmanNode * node = tree;
+ // loop until a leaf node has reached
+ while (node->value == 0xffff) {+ int direction = inflateReadBits(inflateObj, 1);
+ if (direction > 1) {+ printError("huffmanReadValue inflateReadBits failed\n");+ return 0xffffffff; // error
+ }
+ if (node->childeren[direction] == NULL) {+ printError("huffmanReadValue the tree is incomplete or invalid bit path "+ "requested.\n");
+ return 0xffffffff; // error
+ }
+ node = node->childeren[direction];
+ }
+ return node->value;
+}
+
+
+/* inflateInit() - This should be called on every new InflateObject to init it.
+ *
+ * @param inflateObj: Inflate descriptor.
+ * @param inputFile : Valid and open FILE to the input PE file (installer exe),
+ * this may NOT be NULL!
+ **/
+void inflateInit(InflateObject * inflateObj, FILE * inputFile) {+ memset(inflateObj->window , 0x00, sizeof(unsigned char) * WINDOW_SIZE);
+ memset(inflateObj->chunkBuff, 0x00, sizeof(unsigned char) * CHUNK_SIZE);
+ inflateObj->bitOffset = 8; // trigger read new byte
+ inflateObj->windowPosition = 0;
+ inflateObj->chunkBuffPosition = CHUNK_SIZE; // set to max, to trigger new read
+ inflateObj->chunkBuffSize = CHUNK_SIZE;
+ inflateObj->inputFile = inputFile;
+ inflateObj->outputFile = NULL;
+ inflateObj->outputSize = 0;
+ inflateObj->crc32 = CRC32_NEW;
+
+ long inputFileOffset = ftell(inputFile); // backup input positions
+ fseek(inputFile, 0L, SEEK_END); // seek to end
+ inflateObj->inputFileSize = ftell(inputFile); // store file size
+ fseek(inputFile, inputFileOffset, SEEK_SET); // restore input position
+}
+
+
+/* inflateNew() - Prepare the 'InflateObject' for a new file to extract. This
+ * should be called before trying to extract a file.
+ *
+ * @param inflateObj: Inflate descriptor.
+ * @param outputFile: Should be a valid and open FILE to actually inflate to a
+ * output file. This may be NULL to not write the inflated
+ * data to a file. */
+void inflateNew(InflateObject * inflateObj, FILE * outputFile) {+ inflateObj->bitOffset = 8; // trigger read new byte
+ inflateObj->windowPosition = 0;
+ inflateObj->chunkBuffPosition = CHUNK_SIZE; // set to max, to trigger new read
+ inflateObj->chunkBuffSize = CHUNK_SIZE;
+ inflateObj->outputFile = outputFile;
+ inflateObj->outputSize = 0;
+ inflateObj->crc32 = CRC32_NEW;
+}
+
+
+/* inflateRead() - Read a byte from the inflate chunk buffer.
+ *
+ * @returns: A value greater then 0xff on error, else it will return the value.
+ **/
+uint16_t inflateRead(InflateObject * inflateObj) {+ // Read new chunk
+ if (inflateObj->chunkBuffPosition >= inflateObj->chunkBuffSize) {+ // End Of File
+ if (ftell(inflateObj->inputFile) == inflateObj->inputFileSize) {+ printError("inflateRead EOF.\n");+ return 0xffff;
+ }
+
+ // Last chunk of the inputFile is smaller then 16k, adjust the
+ // inputBuffSize
+ if (inflateObj->chunkBuffPosition > (inflateObj->inputFileSize - ftell(inflateObj->inputFile))) {+ inflateObj->chunkBuffSize = inflateObj->inputFileSize - ftell(inflateObj->inputFile);
+ }
+
+ // Read next chunk
+ REWError status = readBytesInto(inflateObj->inputFile, inflateObj->chunkBuff, inflateObj->chunkBuffSize);
+ if (status != REWERR_OK) {+ // failed to read next chunk
+ printError("inflateRead failed to read next chunk.\n");+ return 0xffff;
+ }
+ inflateObj->chunkBuffPosition = 0;
+ }
+
+ uint16_t result = (uint16_t)inflateObj->chunkBuff[inflateObj->chunkBuffPosition];
+ inflateObj->chunkBuffPosition++;
+ return result;
+}
+
+
+/* inflateReadBits() - Reads 'bitCount' of bits from the chunk buffer.
+ *
+ * @param inflateObj: Inflate descriptor.
+ * @param bitCount : The amount of bits to read. (TODO define maximum bitCount)
+ *
+ * @returns: The value on success, 0xffffffff on error. */
+int inflateReadBits(InflateObject * inflateObj, uint8_t bitCount) {+ int result = 0;
+ int resultOffset = 0;
+
+ while (bitCount > 0) {+ // Read new byte into buffer
+ if (inflateObj->bitOffset == 8) {+ uint16_t readResult = inflateRead(inflateObj);
+ if (readResult > 0xff) {+ // inflateRead error
+ return 0xffffffff;
+ }
+ inflateObj->bitBuff = (unsigned char)readResult;
+ inflateObj->bitOffset = 0;
+ }
+
+ int mask = 1 << inflateObj->bitOffset;
+ if ((inflateObj->bitBuff & mask)) {+ result |= 1 << resultOffset;
+ }
+
+ inflateObj->bitOffset++;
+ bitCount--;
+ resultOffset++;
+ }
+
+ return result;
+}
+
+
+/* huffmanReadDynamicTrees() - Read and build dynamic Huffman trees.
+ *
+ * @param inflateObj: Inflate descriptor.
+ * @param hlit : Literal/Length codes
+ * @param hdist : Distance codes
+ * @param hclen : Code Length codes
+ * @param literalTree : Literal tree destination on success, this should be
+ * freed with huffmanFreeTree() on success.
+ * @param distanceTree: Distance tree destination on success, this should be
+ * freed with huffmanFreeTree() on success.
+ *
+ * @returns: 'true' on success, 'false' on error. */
+bool huffmanReadDynamicTrees(InflateObject * inflateObj, int hlit, int hdist,
+ int hclen, HuffmanNode ** literalTree,
+ HuffmanNode ** distanceTree) {+ bool result;
+ int repeatAmount;
+ unsigned char repeatValue;
+ unsigned char maxCodeLength, maxCodeLength2;
+ unsigned char codeLengths[19];
+ unsigned char codeLengthAlphabet[318];
+ unsigned char codeLength;
+ int codeValue;
+ int readBitsResult;
+
+ memset(codeLengths, 0x00, sizeof(unsigned char) * 19);
+ memset(codeLengthAlphabet, 0x00, sizeof(unsigned char) * 318);
+ maxCodeLength = 0;
+
+ // Read codeLengths
+ for (uint16_t i=0; i < hclen; i++) {+ readBitsResult = inflateReadBits(inflateObj, 3);
+ if (readBitsResult == 0xffffffff) {+ // inflateReadBits error
+ printError("Failed to read dynamic trees.\n");+ return false;
+ }
+ codeLength = (unsigned char)readBitsResult;
+
+ if (codeLength > maxCodeLength) {+ maxCodeLength = codeLength;
+ }
+
+ codeLengths[CODE_LENGTH_ORDER[i]] = codeLength;
+ }
+
+ // Build codeLengthTree
+ HuffmanNode * codeLengthTree = newHuffmanNode();
+ if (codeLengthTree == NULL) {+ printError("huffmanReadDynamicTrees failed to build dynamic code length "+ "tree.\n");
+ printError("huffmanReadDynamicTrees errno: %s\n", strerror(errno));+ return false;
+ }
+ result = false;
+ // 1 - 16
+ for (codeLength = 0x01; codeLength < 0x10; codeLength++) {+ // 0 - 18
+ for (codeValue = 0x00; codeValue < 0x13; codeValue++) {+ if (codeLength == codeLengths[codeValue]) {+ result = HuffmanAddCode(codeLengthTree, codeLength, codeValue);
+ }
+ }
+ }
+
+ if (result == false) {+ huffmanFreeTree(codeLengthTree);
+ printError("huffmanReadDynamicTrees failed to build dynamic code length "+ "tree.\n");
+ return false;
+ }
+
+ if (maxCodeLength == 0) {+ maxCodeLength++;
+ }
+
+ result = !HuffmanAddCode(codeLengthTree, maxCodeLength, 0);
+ if (result == false) {+ huffmanFreeTree(codeLengthTree);
+ printError("huffmanReadDynamicTrees ailed to build dynamic code length "+ "tree. It has nodes without end-node.\n");
+ return false;
+ }
+
+ // Build alphabet
+ repeatAmount = 0;
+ repeatValue = 0;
+ maxCodeLength = 0;
+ maxCodeLength2 = 0;
+ for (codeValue=0; codeValue < (int)(hlit + hdist); codeValue++) {+ if (repeatAmount == 0) {+ // read and decode codeLength
+ codeLength = (unsigned char)huffmanReadValue(inflateObj, codeLengthTree);
+
+ if (codeLength < 0x10) {+ codeLengthAlphabet[codeValue] = codeLength;
+
+ // Update maxBits/maxBits2
+ if (codeValue < hlit) {+ if (codeLength > maxCodeLength) {+ maxCodeLength = codeLength;
+ }
+ }
+ else {+ if (codeLength > maxCodeLength2) {+ maxCodeLength2 = codeLength;
+ }
+ }
+ }
+ else if (codeLength == 0x10) {+ readBitsResult = inflateReadBits(inflateObj, 2);
+ if (readBitsResult == 0xffffffff) {+ printError("Failed to read dynamic trees.\n");+ return false;
+ }
+ repeatAmount = 0x02 + readBitsResult;
+ repeatValue = codeLengthAlphabet[codeValue - 0x01];
+ codeLengthAlphabet[codeValue] = repeatValue;
+ }
+ else if (codeLength == 0x11) {+ readBitsResult = inflateReadBits(inflateObj, 3);
+ if (readBitsResult == 0xffffffff) {+ printError("Failed to read dynamic trees.\n");+ return false;
+ }
+ repeatAmount = 0x02 + readBitsResult;
+ repeatValue = 0;
+ codeLengthAlphabet[codeValue] = repeatValue;
+ }
+ else if (codeLength == 0x12) {+ readBitsResult = inflateReadBits(inflateObj, 7);
+ if (readBitsResult == 0xffffffff) {+ printError("Failed to read dynamic trees.\n");+ return false;
+ }
+ repeatAmount = 0x0A + readBitsResult;
+ repeatValue = 0;
+ codeLengthAlphabet[codeValue] = repeatValue;
+ }
+ }
+ else {+ codeLengthAlphabet[codeValue] = repeatValue;
+ repeatAmount--;
+ }
+ }
+
+ // free the codeLengthTree, we don't need it anymore.
+ huffmanFreeTree(codeLengthTree);
+
+ // build literal tree
+ HuffmanNode * litTree = newHuffmanNode();
+ if (litTree == NULL) {+ printError("huffmanReadDynamicTrees failed to allocate literalTree.\n");+ printError("huffmanReadDynamicTrees errno: %s\n", strerror(errno));+ return false;
+ }
+ for (codeLength=0x01; codeLength < 0x10; codeLength++) {+ for (codeValue=0; codeValue < hlit; codeValue++) {+ if (codeLength == codeLengthAlphabet[codeValue]) {+ result = HuffmanAddCode(litTree, codeLength, codeValue);
+ }
+ }
+ }
+
+ if (result == true) {+ if (maxCodeLength == 0) {+ maxCodeLength++;
+ }
+ result = !HuffmanAddCode(litTree, maxCodeLength, 0);
+ if (result == false) {+ huffmanFreeTree(litTree);
+ printError("huffmanReadDynamicTrees failed to build dynamic literal "+ "tree (1). It has a open branch without leaf?\n");
+ return false;
+ }
+ }
+ else {+ huffmanFreeTree(litTree);
+ printError("huffmanReadDynamicTrees failed to build dynamic literal tree "+ "(2).\n");
+ return false;
+ }
+
+ // build distance tree
+ HuffmanNode * distTree = newHuffmanNode();
+ if (distTree == NULL) {+ printError("huffmanReadDynamicTrees failed to allocate distanceTree.\n");+ printError("huffmanReadDynamicTrees errno: %s\n", strerror(errno));+ return false;
+ }
+ for (codeLength=0x01; codeLength < 0x10; codeLength++) {+ for (codeValue=0; codeValue < hdist; codeValue++) {+ if (codeLength == codeLengthAlphabet[codeValue + hlit]) {+ result = HuffmanAddCode(distTree, codeLength, codeValue);
+ }
+ }
+ }
+
+ if (result == true) {+ if (maxCodeLength2 == 0) {+ maxCodeLength2++;
+ }
+ result = !HuffmanAddCode(distTree, maxCodeLength2, 0);
+ if (result == false) {+ huffmanFreeTree(litTree);
+ huffmanFreeTree(distTree);
+ printError("huffmanReadDynamicTrees failed to build dynamic distance "+ "tree (1).\n");
+ return false;
+ }
+ }
+ else {+ huffmanFreeTree(litTree);
+ huffmanFreeTree(distTree);
+ printError("huffmanReadDynamicTrees failed to build dynamic distance tree "+ "(2).\n");
+ return false;
+ }
+
+ *literalTree = litTree;
+ *distanceTree = distTree;
+
+ return true;
+}
+
+
+#ifdef REWISE_DEV_DEBUG
+void inflateInitStaticTables(void) {+ int lengthOffset = 3;
+ unsigned char lengthExtraBits = 0;
+ int distanceOffset = 1;
+ unsigned char distanceExtraBits = 0;
+
+ LENGTH_CODE_VALUE_OFFSET[285] = 258; // 258 = 0x102
+ LENGTH_CODE_VALUE_EXTRA_BITS[285] = 0;
+
+ for (unsigned char pos=0; pos < 30; pos++) {+ // increment number of extra bits for length code table every 4th value
+ if (pos >= 0x08 && !(pos & 0x03)) {+ lengthExtraBits++;
+ }
+
+ // increment number of extra bits for distance code table every 2nd value
+ if (pos >= 0x04 && !(pos & 0x01)) {+ distanceExtraBits++;
+ }
+
+ // for pos<0x1c put value entry into length code table
+ if (pos < 0x1c) {+ LENGTH_CODE_VALUE_OFFSET[pos + 0x101] = lengthOffset;
+ LENGTH_CODE_VALUE_EXTRA_BITS[pos + 0x101] = lengthExtraBits;
+ }
+
+ // put value entry into distance code table
+ DISTANCE_CODE_VALUE_OFFSET[pos] = distanceOffset;
+ DISTANCE_CODE_VALUE_EXTRA_BITS[pos] = distanceExtraBits;
+
+ // increment length and distance code values
+ lengthOffset += (1 << lengthExtraBits);
+ distanceOffset += (1 << distanceExtraBits);
+ }
+}
+
+
+void inflatePrintStaticTables(void) {+ uint8_t colCount;
+
+ colCount = 4;
+ printf("LENGTH_CODE_VALUE_OFFSET\n");+ for (uint32_t i=0; i<286; i++) {+ printf("0x%08X", LENGTH_CODE_VALUE_OFFSET[i]);+ uint8_t colMod = (i+1) % colCount;
+ if (i == 285) { printf("\n"); }+ else if (colMod) { printf(", "); }+ else { printf(",\n"); }+ }
+
+ colCount = 8;
+ printf("\n");+ printf("LENGTH_CODE_VALUE_EXTRA_BITS\n");+ for (uint32_t i=0; i<286; i++) {+ printf("0x%02X", LENGTH_CODE_VALUE_EXTRA_BITS[i]);+ if (i == 285) { printf("\n"); }+ else if ((i+1) % colCount) { printf(", "); }+ else { printf(",\n"); }+ }
+
+ colCount = 4;
+ printf("\n");+ printf("DISTANCE_CODE_VALUE_OFFSET\n");+ for (uint32_t i=0; i<30; i++) {+ printf("0x%08X", DISTANCE_CODE_VALUE_OFFSET[i]);+ if (i == 29) { printf("\n"); }+ else if ((i+1) % colCount) { printf(", "); }+ else { printf(",\n"); }+ }
+
+ colCount = 8;
+ printf("\n");+ printf("DISTANCE_CODE_VALUE_EXTRA_BITS\n");+ for (uint32_t i=0; i<30; i++) {+ printf("0x%02X", DISTANCE_CODE_VALUE_EXTRA_BITS[i]);+ if (i == 29) { printf("\n"); }+ else if ((i+1) % colCount) { printf(", "); }+ else { printf(",\n"); }+ }
+}
+#endif
+
+
+/* inflateWriteOutput() - Writes the content of the sliding window to the
+ * 'InflateObject->outputFile' when it is not 'NULL'.
+ *
+ * It will also keep track of the total number of bytes
+ * written to this file and update the CRC32.
+ *
+ * This does not reset the sliding window position, the
+ * caller is responsible for that when this returns
+ * 'true'.
+ *
+ * @param inflateObj: Inflate descriptor.
+ * @param size : Amount of bytes in the sliding window to write.
+ *
+ * @returns: 'true' on success, 'false' on error. */
+bool inflateWriteOutput(InflateObject * inflateObj, uint32_t size) {+ // Write to output file
+ if (inflateObj->outputFile != NULL) {+ size_t noWritten = fwrite(inflateObj->window, sizeof(unsigned char),
+ (size_t)size, inflateObj->outputFile);
+ if (noWritten != (size_t)size) {+ // Failed to write to file, out of disk space or something got corrupted.
+ printError("Failed to write to file, only wrote %ld of %ld bytes. "+ "Out of disk space?\n", noWritten, size);
+ return false;
+ }
+ }
+
+ inflateObj->outputSize += (long)size;
+
+ // Update CRC32
+ inflateObj->crc32 = crc32Update(inflateObj->crc32, inflateObj->window, size);
+ return true;
+}
+
+
+/* inflateOutputByte() - Appends the given 'byte' to the sliding window, when
+ * the sliding window is full it will output the sliding
+ * window to 'inflateObj->outputFile' (when it isn't
+ * 'NULL') and resets the sliding window.
+ *
+ * @param inflateObj: Inflate descriptor.
+ * @param byte : The 'byte' to output.
+ *
+ * @returns: 'true' on success, 'false' on error. */
+bool inflateOutputByte(InflateObject * inflateObj, unsigned char byte) {+ inflateObj->window[inflateObj->windowPosition] = byte;
+ inflateObj->windowPosition++;
+
+ if (inflateObj->windowPosition == WINDOW_SIZE) {+ if (inflateWriteOutput(inflateObj, WINDOW_SIZE) == false) {+ return false;
+ }
+ inflateObj->windowPosition = 0;
+ }
+
+ return true;
+}
+
+
+/* inflateCopyBytes() - Copy 'codeLength' amount of bytes (with 'codeDistance'
+ * offset) from the sliding window to the end of the
+ * sliding window.
+ * @param inflateObj : Inflate descriptor.
+ * @param codeDistance: Offset from the end of the current sliding window.
+ * @param codeLength : Amount of bytes to copy and append to the end of the
+ * current sliding window.
+ *
+ * @returns: 'true' on success, 'false' on error. */
+bool inflateCopyBytes(InflateObject * inflateObj, int codeDistance,
+ int codeLength)
+{+ while (codeLength > 0) {+ unsigned char byte = inflateObj->window[
+ (inflateObj->windowPosition + WINDOW_SIZE - codeDistance) & 0x7fff];
+ if (inflateOutputByte(inflateObj, byte) == false) {+ return false;
+ }
+ codeLength--;
+ }
+ return true;
+}
+
+
+/* inflateNext() - Inflate next file.
+ *
+ * @returns: 'true' on success, 'false' on error. */
+bool inflateNext(InflateObject * inflateObj) {+ unsigned char lastBlock;
+ unsigned char blockType;
+ int hclen;
+ int hdist;
+ int hlit;
+
+ // TODO just for DEBUG
+ inflateObj->deflatedStart = ftell(inflateObj->inputFile);
+ printDebug("deflatedStart: %08lX\n", inflateObj->deflatedStart);+
+ lastBlock = 0;
+ while (lastBlock == 0) {+ // read lastBlock
+ int readBitsResult = inflateReadBits(inflateObj, 1);
+ if (readBitsResult == 0xffffffff) {+ return false;
+ }
+ lastBlock = (unsigned char)readBitsResult;
+
+ // read blockType
+ readBitsResult = inflateReadBits(inflateObj, 2);
+ if (readBitsResult == 0xffffffff) {+ return false;
+ }
+ blockType = (unsigned char)readBitsResult;
+
+ if (blockType == BTYPE_UNCOMPRESSED) {+ // Make sure we start on a fresh byte (aligned)
+ inflateObj->bitOffset = 8;
+
+ hclen = inflateRead(inflateObj) + (inflateRead(inflateObj) << 8);
+ hdist = inflateRead(inflateObj) + (inflateRead(inflateObj) << 8);
+
+ if ((hclen ^ hdist) != 0xffff) {+ printError("Code-length or code-distance invalid! 0x%04X hclen: 0x%02X hdist: 0x%02X\n", (hclen ^ hdist), hclen, hdist);+ return false;
+ }
+
+ while (hclen > 0) {+ // read hlit
+ uint16_t readResultHlit = inflateRead(inflateObj);
+ if (readResultHlit > 255) {+ return false;
+ }
+ //hlit = (unsigned char)readResult;
+ if (inflateOutputByte(inflateObj, (unsigned char)readResultHlit) == false) {+ return false;
+ }
+ hclen--;
+ }
+ }
+ else
+ if (blockType == BTYPE_FIXED) {+ hlit = 0;
+ while (hlit != 0x100) {+ hlit = huffmanReadValue(inflateObj, FixedLiteralTree);
+
+ if (hlit < 0x100) {+ if (inflateOutputByte(inflateObj, (unsigned char)hlit) == false) {+ return false;
+ }
+ }
+ else
+ if (hlit == 0x100) {+ break;
+ }
+ else
+ if (hlit < 0x11e) {+ // Read code length extra bits
+ readBitsResult = inflateReadBits(inflateObj,
+ LENGTH_CODE_VALUE_EXTRA_BITS[hlit]);
+ if (readBitsResult == 0xffffffff) {+ // inflateReadBits error
+ printError("Failed to read fixed length extra bits.\n");+ return false;
+ }
+ int codeLength = LENGTH_CODE_VALUE_OFFSET[hlit] + readBitsResult;
+ int codeDistance = huffmanReadValue(inflateObj, FixedDistanceTree);
+
+ if (codeDistance > 0x1d) {+ printError("Unexpected code distance 0x%08X\n", codeDistance);+ return false;
+ }
+
+ // Read code distance extra bits
+ readBitsResult = inflateReadBits(inflateObj,
+ DISTANCE_CODE_VALUE_EXTRA_BITS[codeDistance]);
+ if (readBitsResult == 0xffffffff) {+ printError("Failed to read fixed distance extra bits.\n");+ return false;
+ }
+ codeDistance = DISTANCE_CODE_VALUE_OFFSET[codeDistance] + readBitsResult;
+ if (inflateCopyBytes(inflateObj, codeDistance, codeLength) == false) {+ return false;
+ }
+ }
+ else {+ printError("Unexpected literal 0x%08X\n", hlit);+ return false;
+ }
+ }
+ }
+ else
+ if (blockType == BTYPE_DYNAMIC) {+ // Read hlit
+ readBitsResult = inflateReadBits(inflateObj, 5);
+ if (readBitsResult == 0xffffffff) {+ printError("Failed to read dynamic hlit bits.\n");+ return false;
+ }
+ hlit = readBitsResult + 0x101;
+
+ // Read hdist
+ readBitsResult = inflateReadBits(inflateObj, 5);
+ if (readBitsResult == 0xffffffff) {+ printError("Failed to read dynamic hdist bits.\n");+ return false;
+ }
+ hdist = readBitsResult + 0x01;
+
+ // Read hclen
+ readBitsResult = inflateReadBits(inflateObj, 4);
+ if (readBitsResult == 0xffffffff) {+ printError("Failed to read dynamic hclen bits.\n");+ return false;
+ }
+ hclen = readBitsResult + 0x04;
+
+ HuffmanNode * literalTree = NULL;
+ HuffmanNode * distanceTree = NULL;
+
+ if (huffmanReadDynamicTrees(inflateObj, hlit, hdist, hclen, &literalTree,
+ &distanceTree) == false) {+ printError("Failed to build dynamic trees\n");+ return false;
+ }
+
+ while (hlit != 0x100) {+ hlit = huffmanReadValue(inflateObj, literalTree);
+
+ if (hlit < 0x100) {+ if (inflateOutputByte(inflateObj, (unsigned char)hlit) == false) {+ huffmanFreeTree(literalTree);
+ huffmanFreeTree(distanceTree);
+ return false;
+ }
+ }
+ else
+ if (hlit == 0x100) {+ break;
+ }
+ else
+ if (hlit < 0x11e) {+ // Read code value extra bits
+ readBitsResult = inflateReadBits(inflateObj, LENGTH_CODE_VALUE_EXTRA_BITS[hlit]);
+ if (readBitsResult == 0xffffffff) {+ huffmanFreeTree(literalTree);
+ huffmanFreeTree(distanceTree);
+ printError("Failed to read dynamic code value extra bits.\n");+ return false;
+ }
+ int codeLength = LENGTH_CODE_VALUE_OFFSET[hlit] + readBitsResult;
+ int codeDistance = huffmanReadValue(inflateObj, distanceTree);
+
+ // Read distance value extra bits
+ readBitsResult = inflateReadBits(inflateObj, DISTANCE_CODE_VALUE_EXTRA_BITS[codeDistance]);
+ if (readBitsResult == 0xffffffff) {+ huffmanFreeTree(literalTree);
+ huffmanFreeTree(distanceTree);
+ printError("Failed to read dynamic distance value extra bits.\n");+ return false;
+ }
+
+ codeDistance = DISTANCE_CODE_VALUE_OFFSET[codeDistance] + readBitsResult;
+ if (inflateCopyBytes(inflateObj, codeDistance, codeLength) == false) {+ huffmanFreeTree(literalTree);
+ huffmanFreeTree(distanceTree);
+ return false;
+ }
+ }
+ else {+ huffmanFreeTree(literalTree);
+ huffmanFreeTree(distanceTree);
+ printError("Unexpected literal 0x%08X\n", hlit);+ return false;
+ }
+ }
+
+ // free dynamic trees
+ huffmanFreeTree(literalTree);
+ huffmanFreeTree(distanceTree);
+ }
+ else {+ printError("Unknown block type! '%X'\n", blockType);+ return false;
+ }
+ }
+
+ // Write leftover (smaller then WINDOW_SIZE)
+ if (inflateObj->windowPosition > 0) {+ if (inflateWriteOutput(inflateObj, inflateObj->windowPosition) == false) {+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/* inflateExtractNextFile() - Inflate helper that opens/creates the output file
+ * when 'outputFilePath' is not 'NULL'. This also
+ * reads and checks the CRC32. The input file offset
+ * should be EOF or the beginning of the next
+ * deflated file data after this returns 'true'.
+ *
+ * @param inflateObj : Inflate descriptor.
+ * @param outputFilePath: File path to extract the data to, this may be 'NULL'
+ * to not extract to a file (for just verify crc32 or to
+ * skip a file).
+ *
+ * @returns: 'true' on success, 'false' on error. */
+bool inflateExtractNextFile(InflateObject * inflateObj,
+ const char * outputFilePath) {+ FILE * outputFp;
+ bool result;
+ uint32_t newCrc;
+
+ if (outputFilePath != NULL) {+ outputFp = fopen(outputFilePath, "wb");
+ if (outputFp == NULL) {+ printError("Failed to open output file '%s'\n", outputFilePath);+ return false;
+ }
+ }
+ else {+ outputFp = NULL;
+ }
+
+ inflateNew(inflateObj, outputFp);
+ result = inflateNext(inflateObj);
+
+ // close output file when there is any
+ if (outputFp != NULL) {+ fclose(outputFp);
+ }
+
+ if (result == false) {+ return false;
+ }
+ inflateObj->crc32 = crc32Finalize(inflateObj->crc32);
+
+ // Seek to the end of the deflate data since we probably overshot due to the
+ // chunk buffer.
+ if (fseek(inflateObj->inputFile, ftell(inflateObj->inputFile) -
+ inflateObj->chunkBuffSize +
+ inflateObj->chunkBuffPosition, SEEK_SET) != 0)
+ {+ printError("Failed to seek back to deflate end.\n");+ return false;
+ }
+
+ if (readUInt32(inflateObj->inputFile, &newCrc) != REWERR_OK) {+ printError("Failed to read CRC32\n");+ return false;
+ }
+
+ if (inflateObj->crc32 != newCrc) {+ // deflated data should be 8 byte aligned?
+ // NOTE: largest offset I have seen is 1. Probably because of the bits read.
+ uint8_t attempt;
+ for (attempt=0; attempt<8; attempt++) {+ if (fseek(inflateObj->inputFile, -3l, SEEK_CUR) != 0) {+ printError("Failed to seek to next crc attempt.\n");+ return false;
+ }
+ if (readUInt32(inflateObj->inputFile, &newCrc) != REWERR_OK) {+ printError("Failed to read CRC32 attempt\n");+ return false;
+ }
+ if (inflateObj->crc32 == newCrc) {+ break;
+ }
+ }
+
+ if (inflateObj->crc32 != newCrc) {+ printError("CRC32 mismatch\n");+ return false;
+ }
+
+ printDebug("crc32 attempt %d\n", attempt);+ }
+
+ return true;
+}
--- /dev/null
+++ b/src/inflate.h
@@ -1,0 +1,83 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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/>.
+ */
+
+#ifndef H_REWISE_INFLATE
+#define H_REWISE_INFLATE
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "crc32.h"
+#include "reader.h"
+
+#define HUFFMAN_LEFT 0
+#define HUFFMAN_RIGHT 1
+
+// DEFLATE block types
+#define BTYPE_UNCOMPRESSED 0x00 // https://www.rfc-editor.org/rfc/rfc1951#page-11
+#define BTYPE_FIXED 0x01 // https://www.rfc-editor.org/rfc/rfc1951#page-12
+#define BTYPE_DYNAMIC 0x02 // https://www.rfc-editor.org/rfc/rfc1951#page-13
+
+// DELFATE sliding window size and chunk size
+#define WINDOW_SIZE 0x8000 // 32K https://www.rfc-editor.org/rfc/rfc1951#page-4
+#define CHUNK_SIZE 0x4000 // 16K
+
+
+typedef struct __HuffmanNode HuffmanNode;
+
+struct __HuffmanNode {+ int value;
+ HuffmanNode * childeren[2];
+ bool leafes[2];
+};
+
+
+typedef struct {+ unsigned char bitBuff;
+ unsigned char window[WINDOW_SIZE];
+ unsigned char chunkBuff[CHUNK_SIZE];
+ uint8_t bitOffset;
+ uint32_t windowPosition;
+ uint32_t chunkBuffPosition;
+ uint32_t chunkBuffSize;
+ FILE * inputFile;
+ FILE * outputFile;
+ long inputFileSize;
+ long outputSize;
+ uint32_t crc32;
+ long deflatedStart; // TODO tmp for debugging
+} InflateObject;
+
+
+bool huffmanInitFixedTrees(void);
+void huffmanFreeFixedTrees(void);
+void inflateInit(InflateObject * inflateObj, FILE * inputFile);
+void inflateNew(InflateObject * inflateObj, FILE * outputFile);
+
+bool inflateNext(InflateObject * inflateObj);
+bool inflateExtractNextFile(InflateObject * inflateObj,
+ const char * outputFilePath);
+
+// only used to generate the lookup table
+#ifdef REWISE_DEV_DEBUG
+void inflateInitStaticTables(void);
+void inflatePrintStaticTables(void);
+#endif
+
+#endif
--- /dev/null
+++ b/src/pefile.c
@@ -1,0 +1,162 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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 <stdio.h>
+#include <errno.h>
+
+#include "reader.h"
+#include "pefile.h"
+#include "print.h"
+
+
+/* pefileGetOverlayOffset() - Get the overlay-offset of a PE file.
+ *
+ * @returns: The offset to overlay-data on success, <0 on error. */
+long pefileGetOverlayOffset(const char * filePath) {+ long fileSize;
+ FILE * fp;
+ size_t noRead;
+ long overlayOffset;
+
+ // Open file
+ fp = fopen(filePath, "rb");
+
+ // Failed to open file
+ if (fp == NULL) {+ printError("pefileGetOverlayOffset failed to open '%s'\n", filePath);+ return -1;
+ }
+
+ // Determine file size
+ if (fseek(fp, 0l, SEEK_END) != 0) {+ printError("pefileGetOverlayOffset failed to seek to end of file '%s'\n",+ filePath);
+ fclose(fp);
+ return -1;
+ }
+ if ((fileSize = ftell(fp)) < 1) {+ printError("pefileGetOverlayOffset failed to determine file size of file "+ "'%s'\n", filePath);
+ fclose(fp);
+ return -1;
+ }
+ // Set cursor back to start of the file
+ if (fseek(fp, 0l, SEEK_SET) != 0) {+ printError("pefileGetOverlayOffset failed to seek to start of file '%s'\n",+ filePath);
+ fclose(fp);
+ return -1;
+ }
+
+ // Read MsDosHeader
+ size_t readSize = sizeof(MsDosHeader);
+ if (readSize > fileSize) {+ printError("pefileGetOverlayOffset file is to small to contain a MS Dos "+ "header. File: '%s'\n", filePath);
+ fclose(fp);
+ return -1;
+ }
+ MsDosHeader msDosHeader;
+ noRead = fread(&msDosHeader, readSize, 1, fp);
+ if (noRead != 1) {+ printError("pefileGetOverlayOffset failed to read the MS Dos header. "+ "noRead: %ld readSize: %ld\n", noRead, readSize);
+ fclose(fp);
+ return -1;
+ }
+
+ if (msDosHeader.signature != 0x5A4D) {+ printError("pefileGetOverlayOffset this is not a PE file for sure. The "+ "MS-DOS header signature doesn't match (not MZ).\n");
+ fclose(fp);
+ return -1;
+ }
+
+ // Read e_lfanew (offset to PE file header)
+ uint32_t e_lfanew;
+ if (fseek(fp, 0x3C, SEEK_SET) != 0) {+ printError("pefileGetOverlayOffset failed to seek to 0x3C.\n");+ printError("pefileGetOverlayOffset errno: %s\n", strerror(errno));+ fclose(fp);
+ return -1;
+ }
+ if (fread(&e_lfanew, 4, 1, fp) != 1) {+ printError("pefileGetOverlayOffset failed to read e_lfanew.\n");+ fclose(fp);
+ return -1;
+ }
+ if (e_lfanew >= fileSize) {+ printError("pefileGetOverlayOffset PE file offset is larger then file "+ "size.\n");
+ fclose(fp);
+ return -1;
+ }
+
+ // Read PE File Header
+ if (fseek(fp, (long)e_lfanew, SEEK_SET) != 0) {+ printError("pefileGetOverlayOffset failed to seek to e_lfanew.\n");+ printError("pefileGetOverlayOffset errno: %s\n", strerror(errno));+ fclose(fp);
+ return -1;
+ }
+ PeFileHeader peFileHeader;
+ if (fread(&peFileHeader, sizeof(PeFileHeader), 1, fp) != 1) {+ printError("pefileGetOverlayOffset failed to read PE File Header\n");+ fclose(fp);
+ return -1;
+ }
+ if ((peFileHeader.signature << 16) != 0x45500000) {+ printError("pefileGetOverlayOffset this is not a PE file for sure (2).\n");+ fclose(fp);
+ return -1;
+ }
+
+ // Skip optional header
+ if (peFileHeader.optionalHeaderSize > 0) {+ if (fseek(fp, peFileHeader.optionalHeaderSize, SEEK_CUR) != 0) {+ printError("pefileGetOverlayOffset failed to skip over the optional "+ "header.\n");
+ printError("pefileGetOverlayOffset errno: %s\n", strerror(errno));+ fclose(fp);
+ return -1;
+ }
+ }
+
+ // Read sections
+ overlayOffset = 0l;
+ for (uint32_t i=0; i< peFileHeader.numberOfSections; i++) {+ PeImageSectionHeader sectionHeader;
+ if (fread(§ionHeader, sizeof(PeImageSectionHeader), 1, fp) != 1) {+ printError("pefileGetOverlayOffset failed to read section header.\n");+ fclose(fp);
+ return -1;
+ }
+
+ if ((sectionHeader.rawDataLocation + sectionHeader.rawDataSize) > overlayOffset) {+ overlayOffset = sectionHeader.rawDataLocation + sectionHeader.rawDataSize;
+ }
+ }
+
+ fclose(fp);
+
+ if (overlayOffset > fileSize) {+ printError("pefileGetOverlayOffset no overlay offset larger then file.\n");+ return -1;
+ }
+
+ return overlayOffset;
+}
+
--- /dev/null
+++ b/src/pefile.h
@@ -1,0 +1,75 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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/>.
+ */
+
+#ifndef H_REWISE_PEFILE
+#define H_REWISE_PEFILE
+
+#include <stdint.h>
+
+// https://github.com/lumbytyci/PExplorer/blob/master/src/pefile.h
+// https://chuongdong.com/reverse%20engineering/2020/08/15/PE-Parser/
+// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
+// https://wiki.osdev.org/MZ
+// https://wiki.osdev.org/PE
+
+typedef struct {+ uint16_t signature; // Should be 'MZ'
+ uint16_t extra;
+ uint16_t pages;
+ uint16_t relocationItems;
+ uint16_t headerSize;
+ uint16_t minimumAllocation;
+ uint16_t maximumAllocation;
+ uint16_t initialSs;
+ uint16_t initialSp;
+ uint16_t checksum;
+ uint16_t initialIp;
+ uint16_t initialCs;
+ uint16_t relocationTable;
+ uint16_t overlay;
+ uint16_t overlayInformation;
+} MsDosHeader;
+
+
+typedef struct {+ uint32_t signature;
+ uint16_t machine;
+ uint16_t numberOfSections;
+ uint32_t timeDateStamp;
+ uint32_t pointerToSymbolTable;
+ uint32_t numberOfSymbols;
+ uint16_t optionalHeaderSize;
+ uint16_t characteristics;
+} PeFileHeader;
+
+
+typedef struct {+ char name[8];
+ uint32_t virtualSize;
+ uint32_t virtualAddress;
+ uint32_t rawDataSize;
+ uint32_t rawDataLocation;
+ uint32_t relocationsLocation;
+ uint32_t lineNumbersLocation;
+ uint16_t numberOfRelocations;
+ uint16_t numberOfLineNumbers;
+ uint32_t characteristics;
+} PeImageSectionHeader;
+
+
+long pefileGetOverlayOffset(const char * filePath);
+
+#endif
--- /dev/null
+++ b/src/print.c
@@ -1,0 +1,80 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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 <stdio.h>
+#include <stdarg.h>
+
+#include "print.h"
+
+
+static enum __PrintFlags PrintFlags = (PRINT_INFO | PRINT_WARNING | PRINT_ERROR);
+
+
+void setPrintFlags(enum __PrintFlags flags) {+ PrintFlags = flags;
+}
+
+void setPrintFlag(enum __PrintFlags flag) {+ PrintFlags |= flag;
+}
+
+void unsetPrintFlag(enum __PrintFlags flag) {+ PrintFlags &= ~flag;
+}
+
+
+void printInfo(const char * fmt, ...) {+ if (PrintFlags & PRINT_INFO) {+ va_list args;
+ va_start (args, fmt);
+ fprintf(stdout, "INFO: ");
+ vfprintf(stdout, fmt, args);
+ va_end(args);
+ }
+}
+
+
+void printWarning(const char * fmt, ...) {+ if (PrintFlags & PRINT_WARNING) {+ va_list args;
+ va_start (args, fmt);
+ fprintf(stderr, "WARNING: ");
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ }
+}
+
+
+void printError(const char * fmt, ...) {+ if (PrintFlags & PRINT_ERROR) {+ va_list args;
+ va_start (args, fmt);
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ }
+}
+
+
+void printDebug(const char * fmt, ...) {+ if (PrintFlags & PRINT_DEBUG) {+ fprintf(stderr, "DEBUG: ");
+ va_list args;
+ va_start (args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ }
+}
--- /dev/null
+++ b/src/print.h
@@ -1,0 +1,40 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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/>.
+ */
+
+#ifndef H_REWISE_PRINT
+#define H_REWISE_PRINT
+
+
+enum __PrintFlags {+ PRINT_SILENT = 0,
+ PRINT_INFO = 1,
+ PRINT_WARNING = 2,
+ PRINT_ERROR = 4,
+ PRINT_DEBUG = 8
+};
+
+
+void setPrintFlags(enum __PrintFlags flags);
+void setPrintFlag(enum __PrintFlags flag);
+void unsetPrintFlag(enum __PrintFlags flag);
+
+void printInfo(const char * fmt, ...);
+void printWarning(const char * fmt, ...);
+void printError(const char * fmt, ...);
+void printDebug(const char * fmt, ...);
+
+
+#endif
--- /dev/null
+++ b/src/reader.c
@@ -1,0 +1,135 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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 <errno.h>
+
+#include "reader.h"
+
+
+REWError readBytesInto(FILE * fp, unsigned char * dest, uint32_t size) {+ int ch;
+ uint32_t chNo = 0;
+
+ do {+ ch = fgetc(fp);
+ dest[chNo] = (unsigned char)ch;
+ chNo++;
+ } while (ch != EOF && size != chNo);
+
+ if (ch == EOF) {+ return REWERR_EOF;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readInt32(FILE * fp, int * dest) {+ unsigned char buff[4];
+ REWError status;
+
+ // read 4 bytes into the buffer
+ status = readBytesInto(fp, buff, 4);
+
+ // failed to read 4 bytes into the buffer
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ *dest = *(int*)buff;
+ return REWERR_OK;
+}
+
+
+REWError readUInt32(FILE * fp, unsigned int * dest) {+ unsigned char buff[4];
+ REWError status;
+
+ // read 4 bytes into the buffer
+ status = readBytesInto(fp, buff, 4);
+
+ // failed to read 4 bytes into the buffer
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ *dest = *(unsigned int*)buff;
+ return REWERR_OK;
+}
+
+
+REWError readUInt16(FILE * fp, uint16_t * dest) {+ REWError status;
+ unsigned char buff[2];
+
+ // read 2 bytes into the buffer
+ status = readBytesInto(fp, buff, 2);
+
+ // failed to read 2 bytes into the buffer
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ *dest = buff[0] + (buff[1] << 8);
+ return REWERR_OK;
+}
+
+
+// This will allocate a new string which need to be freed REWERR_OK is returned
+REWError readString(FILE * fp, char ** dest) {+ char * string = NULL;
+ uint32_t stringLength = 0;
+ int ch;
+ uint32_t chNo;
+ long startOffset = ftell(fp);
+
+ // determine string length
+ do {+ ch = fgetc(fp);
+ if (ch == EOF) {+ return REWERR_EOF;
+ }
+ stringLength++;
+ } while (ch != 0x00);
+
+ if (ch == EOF) {+ return REWERR_EOF;
+ }
+
+ // empty string
+ if (stringLength <= 1) {+ return REWERR_OK;
+ }
+
+ // allocate string
+ string = malloc(sizeof(char) * stringLength + 1);
+ if (string == NULL) {+ return REWERR_ERRNO; // failed to allocate mem
+ }
+ string[stringLength] = 0x00;
+
+ // read the string to the new allocated space
+ fseek(fp, startOffset, SEEK_SET);
+ chNo = 0;
+ do {+ ch = fgetc(fp);
+ string[chNo] = (char)ch;
+ chNo++;
+ } while (ch != 0x00);
+
+ *dest = string;
+ return REWERR_OK;
+}
--- /dev/null
+++ b/src/reader.h
@@ -1,0 +1,37 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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/>.
+ */
+
+#ifndef H_REWISE_READER
+#define H_REWISE_READER
+
+#include <stdio.h>
+#include <linux/limits.h> // PATH_MAX
+#include <string.h>
+#include <unistd.h> // access, F_OK
+#include <stdint.h> // uint32_t
+#include <stdlib.h> // malloc
+
+#include "errors.h"
+
+
+REWError readBytesInto(FILE * fp, unsigned char * dest, uint32_t size);
+REWError readInt32(FILE * fp, int * dest);
+REWError readUInt32(FILE * fp, unsigned int * dest);
+REWError readUInt16(FILE * fp, uint16_t * dest);
+REWError readString(FILE * fp, char ** dest);
+
+
+#endif
--- /dev/null
+++ b/src/rewise.c
@@ -1,0 +1,729 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/limits.h> // PATH_MAX, NAME_MAX
+#include <stdlib.h>
+#include <getopt.h>
+#include <time.h>
+#include <libgen.h> // dirname
+#include <errno.h>
+#include <sys/stat.h> // mkdir
+#include <utime.h>
+#include <sys/statvfs.h>
+
+#include "print.h"
+#include "reader.h"
+#include "inflate.h"
+#include "pefile.h"
+#include "errors.h"
+#include "wiseoverlay.h"
+#include "wisescript.h"
+#include "version.h"
+
+
+#ifndef REWISE_DEFAULT_TMP_PATH
+#define REWISE_DEFAULT_TMP_PATH "/tmp/"
+#endif
+
+
+#define SIZE_KiB 1024
+#define SIZE_MiB 1048576 // 1024^2
+#define SIZE_GiB 1073741824 // 1024^3
+
+
+enum Operation {+ OP_NONE = 0,
+ OP_EXTRACT = 1,
+ OP_EXTRACT_RAW = 2,
+ OP_LIST = 3,
+ OP_VERIFY = 4,
+ OP_HELP = 5,
+ OP_SCRIPT_DEBUG = 6
+};
+
+
+void printPrettySize(size_t size) {+ if (size > SIZE_GiB) {+ printf("%.2f GiB", (float)size / SIZE_GiB);+ }
+ else
+ if (size > SIZE_MiB) {+ printf("%.2f MiB", (float)size / SIZE_MiB);+ }
+ else
+ if (size > SIZE_KiB) {+ printf("%.2f KiB", (float)size / SIZE_KiB);+ }
+ else {+ printf("%zu bytes", size);+ }
+}
+
+
+unsigned long getFreeDiskSpace(char * path) {+ struct statvfs fsStats;
+ if (statvfs((const char *)path, &fsStats) != 0) {+ printError("Failed to determine free disk space for '%s'. Errno: %s\n",+ strerror(errno));
+ return 0;
+ }
+ return fsStats.f_bsize * fsStats.f_bavail;
+}
+
+
+void convertMsDosTime(struct tm * destTime, uint16_t date, uint16_t time) {+ destTime->tm_year = (int)((date >> 9) + 80);
+ destTime->tm_mon = (int)((date >> 5) & 0b0000000000001111);
+ destTime->tm_mday = (int)(date & 0b0000000000011111);
+ destTime->tm_hour = (int)(time >> 11);
+ destTime->tm_min = (int)((time >> 5) & 0b0000000000111111);
+ destTime->tm_sec = (int)(time & 0b0000000000011111) * 2;
+}
+
+static InflateObject * InflateObjPtr;
+static long ScriptDeflateOffset;
+
+#define MAX_OUTPUT_PATH (PATH_MAX - WIN_PATH_MAX) - 2
+static char OutputPath[MAX_OUTPUT_PATH]; // should be absolute and end with a '/'
+static char TempPath[MAX_OUTPUT_PATH] = REWISE_DEFAULT_TMP_PATH;
+static char PreserveTmp = 0;
+static char NoExtract = 0;
+
+
+void printHelp(void) {+ printf("==============================================================\n");+ printf(" Welcome to REWise version %s\n", REWISE_VERSION_STR);+ printf("==============================================================\n\n");+ printf(" Usage: rewise [OPERATION] [OPTIONS] INPUT_FILE\n\n");+ printf(" OPERATIONS\n");+ printf(" -x --extract OUTPUT_PATH Extract files.\n");+ printf(" -r --raw OUTPUT_PATH Extract all files in the overlay "+ "data. This does not move/rename "
+ "files!\n");
+ printf(" -l --list List files.\n");+ printf(" -V --verify Run extract without actually "+ "outputting files, crc32s will be "
+ "checked.\n");
+ printf(" -z --script-debug Print parsed WiseScript.bin\n");+ printf(" -v --version Print version and exit.\n");+ printf(" -h --help Display this HELP.\n");+ printf("\n");+ printf(" OPTIONS\n");+ printf(" -p --preserve Don't delete TMP files.\n");+ printf(" -t --tmp-path TMP_PATH Set temporary path, default: /tmp/\n");+ printf(" -d --debug Print debug info.\n");+ printf(" -s --silent Be silent, don't print anything.\n");+ printf(" -n --no-extract Don't extract anything. This will "+ "be ignored with -x or -r. It also "
+ "will not try to remove TMP files, "
+ "so -p won't do anything.\n");
+ printf("\n");+ printf(" NOTES\n");+ printf(" - Path to directory OUTPUT_PATH and TMP_PATH should exist and "+ "be writable.\n");
+}
+
+
+void printFile(WiseScriptFileHeader * data) {+ struct tm fileDatetime;
+ convertMsDosTime(&fileDatetime, data->date, data->time);
+ printf("% 12u %02d-%02d-%04d %02d:%02d:%02d '%s'\n", data->inflatedSize,+ fileDatetime.tm_mday, fileDatetime.tm_mon, fileDatetime.tm_year + 1900,
+ fileDatetime.tm_hour, fileDatetime.tm_min, fileDatetime.tm_sec,
+ data->destFile);
+}
+
+
+/* preparePath() - Joins the two given paths to dest and tries to create the
+ * directories that don't exist yet.
+ * param subPath: Rest of the filepath (including file) from WiseScript.bin
+ * Should not be larger then (WIN_PATH_MAX + 1)
+ * param dest : A pre-allocated char buffer with size PATH_MAX */
+bool preparePath(char * basePath, char * subPath, char * dest) {+ // Join paths
+ if ((strlen(basePath) + strlen(subPath) + 1) > PATH_MAX) {+ printError("Overflow of final path > PATH_MAX\n");+ return false;
+ }
+ strcpy(dest, basePath);
+ strcat(dest, subPath);
+
+ // Try to create directories as needed
+ char * outputFilePath;
+ char * currentSubPath;
+ char * separator;
+
+ // make a copy which strchr may manipulate.
+ outputFilePath = strdup(dest);
+
+ if (outputFilePath == NULL) {+ printError("Errno: %s\n", strerror(errno));+ return false;
+ }
+
+ // get the path without filename
+ currentSubPath = dirname(outputFilePath);
+
+ // get the path its root (string until first '/')
+ separator = strchr(currentSubPath, '/');
+
+ // This should not happen because the given path by the user should exist.
+ if (separator == NULL) {+ printError("This should not happen, please report if it does! (1)\n");+ return false;
+ }
+
+ // iterate through all sub-directories from root
+ while (separator != NULL) {+ // terminate the dirName string on next occurance of '/'
+ separator[0] = 0x00;
+
+ // do not create root
+ if (currentSubPath[0] != 0x00) {+ // stat currentSubPath
+ if (access(currentSubPath, F_OK) != 0) {+ // currentSubPath exists but is not a directory
+ if (errno == ENOTDIR) {+ printError("Extract subpath '%s' exists but is not a directory!\n",+ currentSubPath);
+ free(outputFilePath);
+ return false;
+ }
+
+ // currentSubPath does not exist, try to create a new directory
+ if (errno == ENOENT) {+ errno = 0;
+ if (mkdir(currentSubPath, 0777) != 0) {+ printError("Failed to create subpath (1): '%s'\n", currentSubPath);+ printError("Errno: %s\n", strerror(errno));+ free(outputFilePath);
+ return false;
+ }
+ }
+ }
+ }
+
+ // reset the previous set terminator
+ separator[0] = '/';
+
+ // set separator to next occurrence of '/' (will be set to NULL when
+ // there are no more occurrences of '/'.
+ separator = strchr(separator + 1, '/');
+ }
+
+ // last subdir
+ if (access(currentSubPath, F_OK) != 0) {+ // currentSubPath exists but is not a directory
+ if (errno == ENOTDIR) {+ printError("Extract path '%s' exists but is not a directory!\n",+ currentSubPath);
+ free(outputFilePath);
+ return false;
+ }
+
+ // currentSubPath does not exist, try to create a new directory
+ if (errno == ENOENT) {+ if (mkdir(currentSubPath, 0777) != 0) {+ printError("Failed to create subpath (2): '%s'\n", currentSubPath);+ printError("Errno: %s\n", strerror(errno));+ free(outputFilePath);
+ return false;
+ }
+ }
+ }
+
+ // cleanup
+ free(outputFilePath);
+
+ return true;
+}
+
+
+void extractFile(WiseScriptFileHeader * data) {+ bool result;
+ char outputFilePath[PATH_MAX];
+
+ // Create the final absolute filepath and make sure the path exists (will be
+ // created when it doesn't exist).
+ if (preparePath(OutputPath, data->destFile, outputFilePath) == false) {+ printError("preparePath failed.\n");+ stopWiseScriptParse();
+ return;
+ }
+
+ // Seek to deflated file start
+ if (fseek(InflateObjPtr->inputFile, ((long)data->deflateStart) + ScriptDeflateOffset, SEEK_SET) != 0) {+ printError("Failed seek to file offset 0x%08X\n", data->deflateStart);+ printError("Errno: %s\n", strerror(errno));+ stopWiseScriptParse();
+ return;
+ }
+
+ // Inflate/extract the file
+ result = inflateExtractNextFile(InflateObjPtr, outputFilePath);
+ if (result == false) {+ printError("Failed to extract '%s'\n", outputFilePath);+ stopWiseScriptParse();
+ return;
+ }
+
+ // Set file access/modification datetime
+ struct tm fileCreation;
+ time_t creationSeconds;
+ convertMsDosTime(&fileCreation, data->date, data->time);
+ creationSeconds = mktime(&fileCreation);
+ const struct utimbuf times = {+ .actime = creationSeconds,
+ .modtime = creationSeconds
+ };
+ if (utime(outputFilePath, ×) != 0) {+ printWarning("Failed to set access and modification datetime for file "+ "'%s'\n", outputFilePath);
+ }
+
+ printInfo("Extracted %s\n", data->destFile);+}
+
+
+void noExtractFile(WiseScriptFileHeader * data) {+ // Inflate/extract the file
+ bool result = inflateExtractNextFile(InflateObjPtr, NULL);
+ if (result == false) {+ printError("Failed to no-extract '%s'\n", data->destFile);+ stopWiseScriptParse();
+ return;
+ }
+ printInfo("CRC32 success for '%s'\n", data->destFile);+}
+
+
+bool setPath(const char * optarg, char * dest) {+ // Resolve absolute path
+ char * outputPath = realpath(optarg, dest);
+ if (outputPath == NULL) {+ printError("Invalid PATH given, could not resolve absolute path for "+ "'%s'. Errno: %s\n", optarg, strerror(errno));
+ return false;
+ }
+
+ size_t outputPathLen = strlen(outputPath);
+ // -2 for the potential '/' we may add
+ if (outputPathLen >= (MAX_OUTPUT_PATH - 1)) {+ printError("Absolute path of PATH is to large.\n");+ return false;
+ }
+
+ // Make sure the path ends with a '/'
+ if (dest[outputPathLen - 1] != '/') {+ strcat(dest, "/");
+ }
+
+ // Make sure the path exists
+ if (access(dest, F_OK) != 0) {+ // dest exists but is not a directory
+ if (errno == ENOTDIR) {+ printError("'%s' is not a directory.\n", dest);+ return false;
+ }
+ // NOTE: realpath would have failed when the directory does not exist.
+ // dest does not exist
+ /*if (errno == ENOENT) {+ printError("'%s' does not exist.\n", dest);+ return false;
+ }*/
+ }
+
+ return true;
+}
+
+
+int main(int argc, char *argv[]) {+ char inputFile[PATH_MAX];
+ long overlayOffset;
+ FILE * fp;
+ REWError status;
+ enum Operation operation = OP_NONE;
+ inputFile[0] = 0x00;
+
+ // https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Options.html
+ // https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html
+ struct option long_options[] = {+ // OPERATIONS
+ {"extract" , required_argument, NULL, 'x'},+ {"raw" , required_argument, NULL, 'r'},+ {"list" , no_argument , NULL, 'l'},+ {"verify" , no_argument , NULL, 'V'},+ {"script-debug", no_argument , NULL, 'z'},+ {"version" , no_argument , NULL, 'v'},+ {"help" , no_argument , NULL, 'h'},+ // OPTIONS
+ {"temp" , required_argument, NULL, 't'},+ {"debug" , no_argument , NULL, 'd'},+ {"preserve" , no_argument , NULL, 'p'},+ {"silent" , no_argument , NULL, 's'},+ {"no-extract" , no_argument , NULL, 'n'},+ {NULL , 0 , NULL, 0}+ };
+
+ int option_index = 0;
+ for (;;) {+ int opt = getopt_long(argc, argv, "x:r:t:lhdspznVv",
+ long_options, &option_index);
+
+ if (opt == -1) {+ break;
+ }
+
+ switch (opt) {+ // OPERATIONS
+ case 'x':
+ {+ if (operation != OP_NONE) {+ printError("More then one operation is set! Do set only one.\n");+ return 1;
+ }
+ operation = OP_EXTRACT;
+ if (setPath(optarg, OutputPath) == false) {+ return 1;
+ }
+ }
+ break;
+
+ case 'r':
+ if (operation != OP_NONE) {+ printError("More then one operation is set! Do set only one.\n");+ return 1;
+ }
+ operation = OP_EXTRACT_RAW;
+ if (setPath(optarg, OutputPath) == false) {+ return 1;
+ }
+ break;
+
+ case 'l':
+ if (operation != OP_NONE) {+ printError("More then one operation is set! Do set only one.\n");+ return 1;
+ }
+ operation = OP_LIST;
+ break;
+
+ case 'V':
+ if (operation != OP_NONE) {+ printError("More then one operation is set! Do set only one.\n");+ return 1;
+ }
+ operation = OP_VERIFY;
+ break;
+
+ case 'z':
+ if (operation != OP_NONE) {+ printError("More then one operation is set! Do set only one.\n");+ return 1;
+ }
+ operation = OP_SCRIPT_DEBUG;
+ break;
+
+ case 'v':
+ printf("REWise v%s\n", REWISE_VERSION_STR);+ return 0;
+
+ case 'h':
+ printHelp();
+ return 0;
+
+ // OPTIONS
+ case 'd':
+ setPrintFlag(PRINT_DEBUG);
+ break;
+
+ case 's':
+ setPrintFlags(PRINT_SILENT);
+ break;
+
+ case 't':
+ if (setPath(optarg, TempPath) == false) {+ printError("Invalid TMP_PATH given.\n");+ return 1;
+ }
+ break;
+
+ case 'p':
+ PreserveTmp = 1;
+ break;
+
+ case 'n':
+ NoExtract = 1;
+ break;
+
+ case '?':
+ // invalid option
+ printError("Invalid operation or option\n");+ return 1;
+
+ default:
+ printError("default\n");+ break;
+ }
+ }
+
+ if ((argc - 1 ) < optind) {+ printError("Please supply a input file\n");+ return 1;
+ }
+ if ((argc - 1 ) > optind) {+ printError("Please supply only one input file\n");+ return 1;
+ }
+
+ if (strlen(argv[optind]) > (PATH_MAX - 1)) {+ printError("What are you trying to do? INPUT_FILE is larger then PATH_MAX\n");+ return 1;
+ }
+ strcpy(inputFile, argv[optind]);
+
+ if (operation == OP_NONE) {+ printError("Please specify a operation.\n");+ return 1;
+ }
+
+ /* Check if input file exists */
+ if (access(inputFile, F_OK) != 0) {+ printError("InputFile '%s' not found. Errno: %s\n", inputFile,+ strerror(errno));
+ return 1;
+ }
+
+ // Get offset to overlay data
+ overlayOffset = pefileGetOverlayOffset(inputFile);
+ if (overlayOffset == -1) {+ printError("Failed to find overlay offset.\n", inputFile);+ return 1;
+ }
+
+ printDebug("InputFile: %s\n", inputFile);+ printDebug("OverlayOffset: %ld\n", overlayOffset);+
+ /* Open inputFile */
+ fp = fopen(inputFile, "rb");
+
+ if (fp == NULL) {+ printError("Failed to open inputFile '%s'\n", inputFile);+ printError("Errno: %s\n", strerror(errno));+ return 1;
+ };
+
+ // Seek to overlayData
+ if (fseek(fp, overlayOffset, SEEK_SET) != 0) {+ printError("Failed to seek to overlayData. Offset: 0x%08X\n", overlayOffset);+ printError("Errno: %s\n", strerror(errno));+ fclose(fp);
+ return 1;
+ }
+
+ // Read Wise overlay header
+ WiseOverlayHeader overlayHeader;
+ if ((status = readWiseOverlayHeader(fp, &overlayHeader)) != REWERR_OK) {+ printError("Failed to read WiseOverlayHeader.\n");+ fclose(fp);
+ return 1;
+ }
+ freeWiseOverlayHeader(&overlayHeader);
+
+ // Here we arrived at the delated data, each entry followed by a CRC32
+ // https://en.wikipedia.org/wiki/DEFLATE
+ if (huffmanInitFixedTrees() == false) {+ printError("Failed to huffmanInitFixedTrees, out of mem?\n");+ fclose(fp);
+ return 1;
+ }
+
+ // Initial check on free disk space (TMP_PATH)
+ unsigned long tmpFreeDiskSpace = getFreeDiskSpace(TempPath);
+ if (tmpFreeDiskSpace == 0) { // failed to determine free disk space+ fclose(fp);
+ return 1;
+ }
+ // make sure at-least 1 MiB is available at the TMP path
+ if (tmpFreeDiskSpace < SIZE_MiB) {+ printError("At-least 1 MiB of free space is required in the TMP_PATH.\n");+ fclose(fp);
+ return 1;
+ }
+ printInfo("Free tmp disk space: ");+ printPrettySize(tmpFreeDiskSpace);
+ printf("\n");+
+ bool result;
+ InflateObject inflateObj;
+ inflateInit(&inflateObj, fp);
+ InflateObjPtr = &inflateObj;
+
+ // Raw extract
+ if (operation == OP_EXTRACT_RAW) {+ uint32_t extractCount = 0;
+ char extractFilePath[PATH_MAX];
+
+ // Start inflating and outputting files
+ while (ftell(fp) < inflateObj.inputFileSize) {+ char fileName[21];
+ if (snprintf(fileName, 20, "EXTRACTED_%09u", extractCount) > 20) {+ // truncated
+ printError("Failed to format filename, it truncated.\n");+ fclose(fp);
+ huffmanFreeFixedTrees();
+ return 1;
+ }
+ if (preparePath(OutputPath, fileName, extractFilePath) == false) {+ printError("Failed to create directories for '%s'.\n", fileName);+ fclose(fp);
+ huffmanFreeFixedTrees();
+ return 1;
+ }
+
+ result = inflateExtractNextFile(&inflateObj, (const char *)extractFilePath);
+ if (result == false) {+ printError("Failed to extract '%s'.\n", extractFilePath);+ fclose(fp);
+ huffmanFreeFixedTrees();
+ return 1;
+ }
+
+ printInfo("Extracted '%s'\n", extractFilePath);+ extractCount++;
+ }
+
+ printInfo("Extracted %d files.\n", extractCount);+ }
+
+ else {+ char tmpFileScript[PATH_MAX];
+
+ // Skip WiseColors.dib
+ if (NoExtract == 0) {+ result = inflateExtractNextFile(&inflateObj, NULL);
+ if (result == false) {+ printError("Failed to extract 'WiseColors.dib'.\n");+ fclose(fp);
+ huffmanFreeFixedTrees();
+ return 1;
+ }
+ }
+
+ // Create filepath for WiseScript.bin
+ if (preparePath(TempPath, "WiseScript.bin", tmpFileScript) == false) {+ fclose(fp);
+ huffmanFreeFixedTrees();
+ printf("Failed to create filepath for WiseScript.bin.\n");+ return 1;
+ }
+ // Extract WiseScript.bin
+ if (NoExtract == 0) {+ result = inflateExtractNextFile(&inflateObj, tmpFileScript);
+ if (result == false) {+ printError("Failed to extract '%s'.\n", tmpFileScript);+ fclose(fp);
+ huffmanFreeFixedTrees();
+ return 1;
+ }
+ }
+
+ // Determine the inflate data offset inside WiseScript.bin (this needs to
+ // be added to the inflateStart we got for files from WiseScript to get to
+ // the real inflateStart offset in the PE file.)
+ WiseScriptParsedInfo * parsedInfo = wiseScriptGetParsedInfo(tmpFileScript);
+ ScriptDeflateOffset = inflateObj.inputFileSize - parsedInfo->inflateStartOffset;
+ printDebug("scriptDeflateOffset: %ld (0x%08X).\n", parsedInfo->inflateStartOffset);+
+ WiseScriptCallbacks callbacks;
+ initWiseScriptCallbacks(&callbacks);
+
+ // LIST
+ if (operation == OP_LIST) {+ callbacks.cb_0x00 = &printFile;
+ printf(" FILESIZE FILEDATE FILETIME FILEPATH\n");+ printf("------------ ---------- -------- ----------------------------\n");+ status = parseWiseScript(tmpFileScript, &callbacks);
+ if (status != REWERR_OK) {+ printError("Parsing WiseScript failed.\n");+ }
+ printf("------------ ---------- -------- ----------------------------\n");+ printf("Total size: ");+ printPrettySize(parsedInfo->inflatedSize0x00);
+ printf(" (%zu bytes)\n", parsedInfo->inflatedSize0x00);+ }
+ // EXTRACT
+ else
+ if (operation == OP_EXTRACT) {+ // Check if there is enough free disk space
+ unsigned long outputFreeDiskSpace = getFreeDiskSpace(OutputPath);
+ if (outputFreeDiskSpace == 0) { // failed to determine free disk space+ fclose(fp);
+ return 1;
+ }
+ if (outputFreeDiskSpace <= parsedInfo->inflatedSize0x00) {+ printError("Not enough free disk space at '%s'. Required: %ld Left: "+ "%ld\n", OutputPath, parsedInfo->inflatedSize0x00,
+ outputFreeDiskSpace);
+ fclose(fp);
+ return 1;
+ }
+
+ // Start inflating and outputting files
+ callbacks.cb_0x00 = &extractFile;
+ status = parseWiseScript(tmpFileScript, &callbacks);
+
+ // Something went wrong
+ if (status != REWERR_OK) {+ printError("Parsing WiseScript failed.\n");+ }
+ }
+ // SCRIPT_DEBUG
+ else
+ if (operation == OP_SCRIPT_DEBUG) {+ status = wiseScriptDebugPrint(tmpFileScript);
+ if (status != REWERR_OK) {+ printError("Debug print WiseScript failed.\n");+ }
+ }
+ else
+ if (operation == OP_VERIFY) {+ callbacks.cb_0x00 = &noExtractFile;
+ status = parseWiseScript(tmpFileScript, &callbacks);
+ if (status != REWERR_OK) {+ printError("Parsing WiseScript failed.\n");+ }
+ printInfo("All looks good!\n");+ }
+
+ // remove tmp files
+ if (PreserveTmp == 0 && NoExtract == 0) {+ if (remove(tmpFileScript) != 0) {+ printError("Failed to remove '%s'. Errno: %s\n", tmpFileScript,+ strerror(errno));
+ }
+ }
+ }
+
+ // Cleanup
+ huffmanFreeFixedTrees();
+ fclose(fp);
+
+ return status;
+}
--- /dev/null
+++ b/src/version.h
@@ -1,0 +1,30 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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/>.
+ */
+
+#ifndef H_REWISE_VERSION
+#define H_REWISE_VERSION
+
+#define REWISE_VERSION_MAJOR 0
+#define REWISE_VERSION_MINOR 1
+#define REWISE_VERSION_PATCH 0
+
+#define _STRINGIFY(x) #x
+#define STRINGIFY(x) _STRINGIFY(x)
+
+#define REWISE_VERSION_STR STRINGIFY(REWISE_VERSION_MAJOR) "." \
+ STRINGIFY(REWISE_VERSION_MINOR) "." \
+ STRINGIFY(REWISE_VERSION_PATCH)
+#endif
--- /dev/null
+++ b/src/wiseoverlay.c
@@ -1,0 +1,95 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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 <stdio.h>
+#include <stdlib.h>
+
+#include "wiseoverlay.h"
+#include "reader.h"
+#include "print.h"
+
+
+REWError readWiseOverlayHeader(FILE * fp, WiseOverlayHeader * dest) {+ REWError status;
+ int ch;
+
+ // Init structure
+ //dest->dllName = NULL; // We skip this
+ dest->initTextLen = 0;
+ dest->initTexts = NULL;
+
+ // Read dllNameLen
+ ch = fgetc(fp);
+ if (ch == EOF) {+ printError("Failed to read dllNameLen (EOF).\n");+ return REWERR_EOF;
+ }
+ dest->dllNameLen = (unsigned char)ch;
+ printDebug("dllNameLen: %d\n", dest->dllNameLen);+
+ // Skip dllName (string) and dllSize (int)
+ if (dest->dllNameLen > 0) {+ if (fseek(fp, (long)(dest->dllNameLen + 4), SEEK_CUR) != 0) {+ printError("Failed to skip dllName.\n");+ return REWERR_ERRNO;
+ }
+ }
+
+ // Read crcFlags (int32)
+ if ((status = readInt32(fp, &dest->crcFlags)) != REWERR_OK) {+ printError("Failed to read crcFlags. %d\n", status);+ return status;
+ }
+
+ // Read 86 unknown bytes
+ if ((status = readBytesInto(fp, dest->unknown_86, 86)) != REWERR_OK) {+ printError("Failed to read 86 unknown bytes\n");+ return status;
+ }
+
+ // Read initTextLen
+ ch = fgetc(fp);
+ if (ch == EOF) {+ printError("Failed to read initTextLen (EOF).\n");+ return REWERR_EOF;
+ }
+ dest->initTextLen = (unsigned char)ch;
+
+ // Init texts
+ printDebug("Read init texts, len: %d\n", dest->initTextLen);+ unsigned char * initTexts = malloc(sizeof(unsigned char) * dest->initTextLen);
+ if (initTexts == NULL) {+ printError("Failed allocate memory for initTexts. Out of memory!\n");+ return REWERR_ERRNO;
+ }
+
+ if ((status = readBytesInto(fp, initTexts, dest->initTextLen)) != REWERR_OK) {+ printError("Failed to read initTexts. %d\n", status);+ free(initTexts);
+ return status;
+ }
+ dest->initTexts = initTexts;
+
+ return REWERR_OK;
+}
+
+
+void freeWiseOverlayHeader(WiseOverlayHeader * data) {+ if (data->initTexts != NULL) {+ free(data->initTexts);
+ data->initTexts = NULL;
+ }
+}
--- /dev/null
+++ b/src/wiseoverlay.h
@@ -1,0 +1,36 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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/>.
+ */
+
+#ifndef H_WISEOVERLAY
+#define H_WISEOVERLAY
+
+#include "errors.h"
+
+typedef struct {+ unsigned char dllNameLen;
+ //unsigned char * dllName; // We SKIP this
+ //uint32_t dllSize; // We SKIP this
+
+ int crcFlags;
+ unsigned char unknown_86[86];
+ unsigned char initTextLen;
+ unsigned char * initTexts;
+} WiseOverlayHeader;
+
+REWError readWiseOverlayHeader(FILE * fp, WiseOverlayHeader * dest);
+void freeWiseOverlayHeader(WiseOverlayHeader * data);
+
+#endif
--- /dev/null
+++ b/src/wisescript.c
@@ -1,0 +1,1738 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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 <stddef.h>
+#include <linux/limits.h>
+#include <string.h>
+#include <errno.h>
+
+
+#include "wisescript.h"
+#include "reader.h"
+#include "print.h"
+
+
+static int WiseScriptSTOP = 0;
+
+
+REWError readWiseScriptHeader(FILE * fp, WiseScriptHeader * header) {+ // init struct
+ header->logPath = NULL;
+ header->font = NULL;
+
+ REWError status;
+
+ status = readBytesInto(fp, header->unknown_44, 44);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &header->logPath);
+ if (status != REWERR_OK) {+ printError("Failed to read WiseScriptHeader logpath: %d\n", status);+ return status;
+ }
+
+ status = readString(fp, &header->font);
+ if (status != REWERR_OK) {+ printError("Failed to read WiseScriptHeader font: %d\n", status);+ freeWiseScriptHeader(header);
+ return status;
+ }
+
+ status = readBytesInto(fp, header->unknown_14, 14);
+ if (status != REWERR_OK) {+ freeWiseScriptHeader(header);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptTexts(FILE * fp, WiseScriptTexts * texts) {+ REWError status;
+ uint32_t i;
+
+ // Init text pointers
+ for (i = 0; i < 56; i++) {+ texts->installTexts[i] = NULL;
+ }
+
+ // Read the text strings
+ for (i = 0; i < 56; i++) {+ status = readString(fp, &texts->installTexts[i]);
+ if (status != REWERR_OK) {+ freeWiseScriptTexts(texts);
+ return status;
+ }
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptFileHeader(FILE * fp, WiseScriptFileHeader * data) {+ REWError status;
+
+ // init struct
+ data->destFile = NULL;
+ data->fileText = NULL;
+
+ status = readBytesInto(fp, data->unknown_2, 2);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readUInt32(fp, &data->deflateStart);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readUInt32(fp, &data->deflateEnd);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readUInt16(fp, &data->date);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readUInt16(fp, &data->time);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readUInt32(fp, &data->inflatedSize);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readBytesInto(fp, data->unknown_40, 20);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readUInt32(fp, &data->crc32);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &data->destFile);
+ if (status != REWERR_OK) {+ printError("readWiseScriptFileHeader failed to read destFile: %d\n",+ status);
+ return status;
+ }
+
+ // data->destFile is just 0x00
+ if (data->destFile == NULL) {+ printError("readWiseScriptFileHeader destFile is a empty string\n");+ return REWERR_INVALDAT;
+ }
+
+ // parse filepath
+ char * filePath = wiseScriptParsePath(data->destFile);
+ if (filePath == NULL) {+ printError("readWiseScriptFileHeader invalid destFile\n");+ freeWiseScriptFileHeader(data);
+ return REWERR_INVALDAT;
+ }
+ free(data->destFile);
+ data->destFile = filePath;
+
+ status = readString(fp, &data->fileText);
+ if (status != REWERR_OK) {+ printError("readWiseScriptFileHeader failed to read fileText: "+ "%d\n", status);
+ freeWiseScriptFileHeader(data);
+ return status;
+ }
+
+ status = readBytesInto(fp, &data->terminator, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x03(FILE * fp, WiseScriptUnknown0x03 * data) {+ REWError status;
+
+ // init struct
+ data->unknownString_1 = NULL;
+ data->unknownString_2 = NULL;
+
+ status = readBytesInto(fp, &data->unknown_1, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_1);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x03 failed to read unknownString_1: "+ "%d\n", status);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_2);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x03 failed to read unknownString_2: "+ "%d\n", status);
+ freeWiseScriptUnknown0x03(data);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x04(FILE * fp, WiseScriptUnknown0x04 * data) {+ REWError status;
+
+ // init struct
+ data->dataString = NULL;
+
+ status = readBytesInto(fp, &data->no, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &data->dataString);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x04 failed to read dataString: "+ "%d\n", status);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x05(FILE * fp, WiseScriptUnknown0x05 * data) {+ REWError status;
+
+ // init struct
+ data->file = NULL;
+ data->section = NULL;
+ data->values = NULL;
+
+ status = readString(fp, &data->file);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x05 failed to read file: "+ "%d\n", status);
+ return status;
+ }
+
+ status = readString(fp, &data->section);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x05 failed to read section: "+ "%d\n", status);
+ return status;
+ }
+
+ status = readString(fp, &data->values);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x05 failed to read values: "+ "%d\n", status);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x06(FILE * fp, WiseScriptUnknown0x06 * data) {+ REWError status;
+
+ status = readBytesInto(fp, data->unknown, 6);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readUInt32(fp, &data->deflateStart);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readUInt32(fp, &data->deflateEnd);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readUInt32(fp, &data->inflatedSize);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readBytesInto(fp, &data->unknown1, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x07(FILE * fp, WiseScriptUnknown0x07 * data) {+ REWError status;
+
+ // init struct
+ data->unknownString_1 = NULL;
+ data->unknownString_2 = NULL;
+ data->unknownString_3 = NULL;
+
+ status = readBytesInto(fp, &data->unknown_1, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_1);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x07 failed to read unknownString_1: "+ "%d\n", status);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_2);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x07 failed to read unknownString_2: "+ "%d\n", status);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_3);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x07 failed to read unknownString_3: "+ "%d\n", status);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x08(FILE * fp, WiseScriptUnknown0x08 * data) {+ REWError status;
+ status = readBytesInto(fp, &data->unknown_1, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x09(FILE * fp, WiseScriptUnknown0x09 * data) {+ REWError status;
+
+ // init struct
+ data->unknownString_1 = NULL;
+ data->unknownString_2 = NULL;
+ data->unknownString_3 = NULL;
+ data->unknownString_4 = NULL;
+ data->unknownString_5 = NULL;
+ data->unknown_2 = 0;
+
+ status = readBytesInto(fp, &data->unknown_1, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ if (data->unknown_1 != 0x01 && data->unknown_1 != 0x20) {+ status = readBytesInto(fp, &data->unknown_2, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+ }
+
+ status = readString(fp, &data->unknownString_1);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x09 failed to read unknownString_1: "+ "%d\n", status);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_2);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x09 failed to read unknownString_2: "+ "%d\n", status);
+ freeWiseScriptUnknown0x09(data);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_3);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x09 failed to read unknownString_3: "+ "%d\n", status);
+ freeWiseScriptUnknown0x09(data);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_4);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x09 failed to read unknownString_4: "+ "%d\n", status);
+ freeWiseScriptUnknown0x09(data);
+ return status;
+ }
+
+ if (data->unknown_1 == 0x01 || data->unknown_1 == 0x20) {+ status = readString(fp, &data->unknownString_5);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x09 failed to read unknownString_5: "+ "%d\n", status);
+ freeWiseScriptUnknown0x09(data);
+ return status;
+ }
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x0A(FILE * fp, WiseScriptUnknown0x0A * data) {+ REWError status;
+
+ // init struct
+ data->unknownString_1 = NULL;
+ data->unknownString_2 = NULL;
+ data->unknownString_3 = NULL;
+
+ status = readBytesInto(fp, data->unknown_2, 2);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_1);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x0A failed to read unknownString_1: "+ "%d\n", status);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_2);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x0A failed to read unknownString_2: "+ "%d\n", status);
+ freeWiseScriptUnknown0x0A(data);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_3);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x0A failed to read unknownString_3: "+ "%d\n", status);
+ freeWiseScriptUnknown0x0A(data);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x0B(FILE * fp, WiseScriptUnknown0x0B * data) {+ REWError status;
+
+ // init struct
+ data->unknownString_1 = NULL;
+
+ status = readBytesInto(fp, &data->unknown_1, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_1);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x0B failed to read unknownString_1: "+ "%d\n", status);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x0C(FILE * fp, WiseScriptUnknown0x0C * data) {+ REWError status;
+
+ // init struct
+ data->unknownString_1 = NULL;
+ data->unknownString_2 = NULL;
+
+ status = readBytesInto(fp, &data->unknown_1, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_1);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x0C failed to read unknownString_1: "+ "%d\n", status);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_2);
+ if (status != REWERR_OK) {+ freeWiseScriptUnknown0x0C(data);
+ printError("readWiseScriptUnknown0x0C failed to read unknownString_2: "+ "%d\n", status);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x11(FILE * fp, WiseScriptUnknown0x11 * data) {+ REWError status;
+
+ // init struct
+ data->unknownString_1 = NULL;
+
+ status = readString(fp, &data->unknownString_1);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x11 failed to read unknownString_1: "+ "%d\n", status);
+ freeWiseScriptUnknown0x11(data);
+ return status;
+ }
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x12(FILE * fp, WiseScriptUnknown0x12 * data) {+ REWError status;
+
+ // init struct
+ data->sourceFile = NULL;
+ data->unknownString_1 = NULL;
+ data->unknownString_2 = NULL;
+ data->destFile = NULL;
+
+ status = readBytesInto(fp, &data->unknown_1, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readBytesInto(fp, data->unknown_41, 41);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &data->sourceFile);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x12 failed to read sourceFile: %d\n",+ status);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_1);
+ if (status != REWERR_OK) {+ freeWiseScriptUnknown0x12(data);
+ printError("readWiseScriptUnknown0x12 failed to read unknownString_1: "+ "%d\n", status);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_2);
+ if (status != REWERR_OK) {+ freeWiseScriptUnknown0x12(data);
+ printError("readWiseScriptUnknown0x12 failed to read unknownString_2: "+ "%d\n", status);
+ return status;
+ }
+
+ status = readString(fp, &data->destFile);
+ if (status != REWERR_OK) {+ freeWiseScriptUnknown0x12(data);
+ printError("readWiseScriptUnknown0x12 failed to read destFile: %d\n",+ status);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x14(FILE * fp, WiseScriptUnknown0x14 * data) {+ REWError status;
+
+ // init struct
+ data->name = NULL;
+ data->message = NULL;
+
+ status = readUInt32(fp, &data->deflateStart);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readUInt32(fp, &data->deflateEnd);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readUInt32(fp, &data->inflatedSize);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &data->name);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x14 failed to read name: %d\n", status);+ return status;
+ }
+
+ status = readString(fp, &data->message);
+ if (status != REWERR_OK) {+ freeWiseScriptUnknown0x14(data);
+ printError("readWiseScriptUnknown0x14 failed to read message: %d\n",+ status);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x15(FILE * fp, WiseScriptUnknown0x15 * data) {+ REWError status;
+
+ // init struct
+ data->unknownString_1 = NULL;
+ data->unknownString_2 = NULL;
+
+ status = readBytesInto(fp, &data->unknown_1, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_1);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x15 failed to read unknownString_1: "+ "%d\n", status);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_2);
+ if (status != REWERR_OK) {+ freeWiseScriptUnknown0x15(data);
+ printError("readWiseScriptUnknown0x15 failed to read unknownString_2: "+ "%d\n", status);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x16(FILE * fp, WiseScriptUnknown0x16 * data) {+ REWError status;
+
+ // init struct
+ data->name = NULL;
+
+ status = readString(fp, &data->name);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x16 failed to read name: "+ "%d\n", status);
+ return status;
+ }
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x17(FILE * fp, WiseScriptUnknown0x17 * data) {+ REWError status;
+
+ // init struct
+ data->unknownString_1 = NULL;
+
+ status = readBytesInto(fp, &data->unknown_1, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readBytesInto(fp, data->unknown_4, 4);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_1);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x17 failed to read unknownString_1: "+ "%d\n", status);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x1C(FILE * fp, WiseScriptUnknown0x1C * data) {+ REWError status;
+
+ // init struct
+ data->unknownString_1 = NULL;
+
+ status = readString(fp, &data->unknownString_1);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x1C failed to read unknownString_1: "+ "%d\n", status);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x1E(FILE * fp, WiseScriptUnknown0x1E * data) {+ REWError status;
+ status = readBytesInto(fp, data->unknown_2, 2);
+ if (status != REWERR_OK) {+ return status;
+ }
+ return REWERR_OK;
+}
+
+
+REWError readWiseScriptUnknown0x23(FILE * fp, WiseScriptUnknown0x23 * data) {+ REWError status;
+
+ // init struct
+ data->unknownString_1 = NULL;
+ data->unknownString_2 = NULL;
+
+ status = readBytesInto(fp, &data->unknown_1, 1);
+ if (status != REWERR_OK) {+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_1);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x23 failed to read unknownString_1: "+ "%d\n", status);
+ return status;
+ }
+
+ status = readString(fp, &data->unknownString_2);
+ if (status != REWERR_OK) {+ printError("readWiseScriptUnknown0x23 failed to read unknownString_2: "+ "%d\n", status);
+ freeWiseScriptUnknown0x23(data);
+ return status;
+ }
+
+ return REWERR_OK;
+}
+
+
+
+
+void freeWiseScriptHeader(WiseScriptHeader * header) {+ if (header->logPath != NULL) {+ free(header->logPath);
+ header->logPath = NULL;
+ }
+ if (header->font != NULL) {+ free(header->font);
+ header->font = NULL;
+ }
+}
+
+
+void freeWiseScriptTexts(WiseScriptTexts * texts) {+ for (uint32_t i = 0; i < 56; i++) {+ if (texts->installTexts[i] != NULL) {+ free(texts->installTexts[i]);
+ texts->installTexts[i] = NULL;
+ }
+ }
+}
+
+
+void freeWiseScriptFileHeader(WiseScriptFileHeader * data) {+ if (data->destFile != NULL) {+ free(data->destFile);
+ data->destFile = NULL;
+ }
+ if (data->fileText != NULL) {+ free(data->fileText);
+ data->fileText = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x03(WiseScriptUnknown0x03 * data) {+ if (data->unknownString_1 != NULL) {+ free(data->unknownString_1);
+ data->unknownString_1 = NULL;
+ }
+ if (data->unknownString_2 != NULL) {+ free(data->unknownString_2);
+ data->unknownString_2 = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x04(WiseScriptUnknown0x04 * data) {+ if (data->dataString != NULL) {+ free(data->dataString);
+ data->dataString = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x05(WiseScriptUnknown0x05 * data) {+ if (data->file != NULL) {+ free(data->file);
+ data->file = NULL;
+ }
+ if (data->section != NULL) {+ free(data->section);
+ data->section = NULL;
+ }
+ if (data->values != NULL) {+ free(data->values);
+ data->values = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x07(WiseScriptUnknown0x07 * data) {+ if (data->unknownString_1 != NULL) {+ free(data->unknownString_1);
+ data->unknownString_1 = NULL;
+ }
+ if (data->unknownString_2 != NULL) {+ free(data->unknownString_2);
+ data->unknownString_2 = NULL;
+ }
+ if (data->unknownString_3 != NULL) {+ free(data->unknownString_3);
+ data->unknownString_3 = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x09(WiseScriptUnknown0x09 * data) {+ if (data->unknownString_1 != NULL) {+ free(data->unknownString_1);
+ data->unknownString_1 = NULL;
+ }
+ if (data->unknownString_2 != NULL) {+ free(data->unknownString_2);
+ data->unknownString_2 = NULL;
+ }
+ if (data->unknownString_3 != NULL) {+ free(data->unknownString_3);
+ data->unknownString_3 = NULL;
+ }
+ if (data->unknownString_4 != NULL) {+ free(data->unknownString_4);
+ data->unknownString_4 = NULL;
+ }
+ if (data->unknownString_5 != NULL) {+ free(data->unknownString_5);
+ data->unknownString_5 = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x0A(WiseScriptUnknown0x0A * data) {+ if (data->unknownString_1 != NULL) {+ free(data->unknownString_1);
+ data->unknownString_1 = NULL;
+ }
+ if (data->unknownString_2 != NULL) {+ free(data->unknownString_2);
+ data->unknownString_2 = NULL;
+ }
+ if (data->unknownString_3 != NULL) {+ free(data->unknownString_3);
+ data->unknownString_3 = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x0B(WiseScriptUnknown0x0B * data) {+ if (data->unknownString_1 != NULL) {+ free(data->unknownString_1);
+ data->unknownString_1 = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x0C(WiseScriptUnknown0x0C * data) {+ if (data->unknownString_1 != NULL) {+ free(data->unknownString_1);
+ data->unknownString_1 = NULL;
+ }
+ if (data->unknownString_2 != NULL) {+ free(data->unknownString_2);
+ data->unknownString_2 = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x11(WiseScriptUnknown0x11 * data) {+ if (data->unknownString_1 != NULL) {+ free(data->unknownString_1);
+ data->unknownString_1 = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x12(WiseScriptUnknown0x12 * data) {+ if (data->sourceFile != NULL) {+ free(data->sourceFile);
+ data->sourceFile = NULL;
+ }
+ if (data->unknownString_1 != NULL) {+ free(data->unknownString_1);
+ data->unknownString_1 = NULL;
+ }
+ if (data->unknownString_2 != NULL) {+ free(data->unknownString_2);
+ data->unknownString_2 = NULL;
+ }
+ if (data->destFile != NULL) {+ free(data->destFile);
+ data->destFile = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x14(WiseScriptUnknown0x14 * data) {+ if (data->name != NULL) {+ free(data->name);
+ data->name = NULL;
+ }
+ if (data->message != NULL) {+ free(data->message);
+ data->message = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x15(WiseScriptUnknown0x15 * data) {+ if (data->unknownString_1 != NULL) {+ free(data->unknownString_1);
+ data->unknownString_1 = NULL;
+ }
+ if (data->unknownString_2 != NULL) {+ free(data->unknownString_2);
+ data->unknownString_2 = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x16(WiseScriptUnknown0x16 * data) {+ if (data->name != NULL) {+ free(data->name);
+ data->name = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x17(WiseScriptUnknown0x17 * data) {+ if (data->unknownString_1 != NULL) {+ free(data->unknownString_1);
+ data->unknownString_1 = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x1C(WiseScriptUnknown0x1C * data) {+ if (data->unknownString_1 != NULL) {+ free(data->unknownString_1);
+ data->unknownString_1 = NULL;
+ }
+}
+
+
+void freeWiseScriptUnknown0x23(WiseScriptUnknown0x23 * data) {+ if (data->unknownString_1 != NULL) {+ free(data->unknownString_1);
+ data->unknownString_1 = NULL;
+ }
+ if (data->unknownString_2 != NULL) {+ free(data->unknownString_2);
+ data->unknownString_2 = NULL;
+ }
+}
+
+
+
+
+// Debug prints //
+
+// https://www.doubleblak.com/m/blogPosts.php?id=7
+// MS-Dos FileTime
+// DATE
+// ----
+// bit 0 - 6 Year
+// bit 7 - 10 Month
+// bit 11 - 15 Day
+//
+// TIME
+// ----
+// bit 0 - 4 Hour
+// bit 5 - 10 Minutes
+// bit 11 - 15 * 2 Seconds
+void printDatetime(uint16_t date, uint16_t time) {+ printf("%04d-%02d-%02d %02d:%02d:%02d",+ (date >> 9) + 1980,
+ (date >> 5) & 0b0000000000001111,
+ date & 0b0000000000011111,
+ (time >> 11),
+ (time >> 5) & 0b0000000000111111,
+ (time & 0b0000000000011111) * 2);
+}
+
+void printHex(unsigned char * value, uint32_t size) {+ for (uint32_t i=0; i < size; i++) {+ printf("%02X", value[i]);+ }
+}
+
+void printWiseScriptHeader(WiseScriptHeader * header) {+ printf("WiseScript Header\n-----------------\n");+ for (int i = 0; i < 44; i++) {+ printf("%02X ", header->unknown_44[i]);+ }
+ printf("\n");+ printf("'%s': '%s'\n", header->font, header->logPath);+ printf("-----------------\n");+
+}
+
+void printWiseScriptTexts(WiseScriptTexts * texts) {+ printf("WiseScript Texts\n");+ printf("----------------\n");+ for (int i = 0; i < 56; i++) {+ printf("Text: \"%s\"\n", texts->installTexts[i]);+ }
+ printf("----------------\n");+}
+
+void printWiseScriptFileHeader(WiseScriptFileHeader * data) {+ printf("0x00 0x%08X 0x%08X ", data->deflateStart, data->deflateEnd);+ printDatetime(data->date, data->time);
+ printf(" % 11u ", data->inflatedSize);+ printHex(data->unknown_40, 20);
+ printf(" %08X '%s' '%s' %d\n", data->crc32, data->destFile, data->fileText,+ data->terminator);
+}
+
+void printWiseScriptUnknown0x03(WiseScriptUnknown0x03 * data) {+ printf("0x03 0x%02X '%s' '%s'\n", data->unknown_1, data->unknownString_1,+ data->unknownString_2);
+}
+
+void printWiseScriptUnknown0x04(WiseScriptUnknown0x04 * data) {+ printf("0x04 0x%02X '%s'\n", data->no, data->dataString);+}
+
+void printWiseScriptUnknown0x05(WiseScriptUnknown0x05 * data) {+ printf("0x05 '%s' '%s' '%s'\n", data->file, data->section, data->values);+}
+
+void printWiseScriptUnknown0x06(WiseScriptUnknown0x06 * data) {+ printf("0x06 ");+ printHex(data->unknown, 6);
+ printf(" 0x%08X 0x%08X % 11u %02X\n", data->deflateStart, data->deflateEnd,+ data->inflatedSize, data->unknown1);
+}
+
+void printWiseScriptUnknown0x07(WiseScriptUnknown0x07 * data) {+ printf("0x07 %02X '%s' '%s' '%s'\n", data->unknown_1, data->unknownString_1,+ data->unknownString_2, data->unknownString_3);
+}
+
+void printWiseScriptUnknown0x08(WiseScriptUnknown0x08 * data) {+ printf("0x08 %02X\n", data->unknown_1);+}
+
+void printWiseScriptUnknown0x09(WiseScriptUnknown0x09 * data) {+ printf("0x09 %02X ", data->unknown_1);+ if (data->unknown_1 != 0x01 && data->unknown_1 != 0x20) {+ printf("%02X ", data->unknown_2);+ }
+ printf("'%s' '%s' '%s' '%s'", data->unknownString_1, data->unknownString_2,+ data->unknownString_3, data->unknownString_4);
+ if (data->unknown_1 == 0x01 || data->unknown_1 == 0x20) {+ printf(" '%s'", data->unknownString_5);+ }
+ printf("\n");+}
+
+void printWiseScriptUnknown0x0A(WiseScriptUnknown0x0A * data) {+ printf("0x0A ");+ printHex(data->unknown_2, 2);
+ printf(" '%s' '%s' '%s'\n", data->unknownString_1, data->unknownString_2,+ data->unknownString_3);
+}
+
+void printWiseScriptUnknown0x0B(WiseScriptUnknown0x0B * data) {+ printf("0x0B %02X '%s'\n", data->unknown_1, data->unknownString_1);+}
+
+void printWiseScriptUnknown0x0C(WiseScriptUnknown0x0C * data) {+ printf("0x0B %02X '%s' '%s'\n", data->unknown_1, data->unknownString_1,+ data->unknownString_2);
+}
+
+void printWiseScriptUnknown0x11(WiseScriptUnknown0x11 * data) {+ printf("0x11 '%s'\n", data->unknownString_1);+}
+
+void printWiseScriptUnknown0x12(WiseScriptUnknown0x12 * data) {+ printf("0x12 %02X ", data->unknown_1);+ printHex(data->unknown_41, 41);
+ printf(" '%s' '%s' '%s' '%s'\n", data->sourceFile, data->unknownString_1,+ data->unknownString_2, data->destFile);
+}
+
+void printWiseScriptUnknown0x14(WiseScriptUnknown0x14 * data) {+ printf("0x14 0x%08X 0x%08X % 11u '%s' '%s'\n", data->deflateStart,+ data->deflateEnd, data->inflatedSize, data->name, data->message);
+}
+
+void printWiseScriptUnknown0x15(WiseScriptUnknown0x15 * data) {+ printf("0x15 %02X '%s' '%s'\n", data->unknown_1, data->unknownString_1,+ data->unknownString_2);
+}
+
+void printWiseScriptUnknown0x16(WiseScriptUnknown0x16 * data) {+ printf("0x16 '%s'\n", data->name);+}
+
+void printWiseScriptUnknown0x17(WiseScriptUnknown0x17 * data) {+ printf("0x17 %02X ", data->unknown_1);+ printHex(data->unknown_4, 4);
+ printf(" '%s'\n", data->unknownString_1);+}
+
+void printWiseScriptUnknown0x1C(WiseScriptUnknown0x1C * data) {+ printf("0x1C '%s'\n", data->unknownString_1);+}
+
+void printWiseScriptUnknown0x1E(WiseScriptUnknown0x1E * data) {+ printf("0x1E ");+ printHex(data->unknown_2, 2);
+ printf("\n");+}
+
+void printWiseScriptUnknown0x23(WiseScriptUnknown0x23 * data) {+ printf("0x1C %02X '%s' '%s'\n", data->unknown_1, data->unknownString_1,+ data->unknownString_2);
+}
+
+
+
+
+void initWiseScriptCallbacks(WiseScriptCallbacks * callbacks) {+ callbacks->cb_header = NULL;
+ callbacks->cb_texts = NULL;
+ callbacks->cb_0x00 = NULL;
+ callbacks->cb_0x03 = NULL;
+ callbacks->cb_0x04 = NULL;
+ callbacks->cb_0x05 = NULL;
+ callbacks->cb_0x06 = NULL;
+ callbacks->cb_0x07 = NULL;
+ callbacks->cb_0x08 = NULL;
+ callbacks->cb_0x09 = NULL;
+ callbacks->cb_0x0A = NULL;
+ callbacks->cb_0x0B = NULL;
+ callbacks->cb_0x0C = NULL;
+ callbacks->cb_0x0F = NULL;
+ callbacks->cb_0x10 = NULL;
+ callbacks->cb_0x11 = NULL;
+ callbacks->cb_0x12 = NULL;
+ callbacks->cb_0x14 = NULL;
+ callbacks->cb_0x15 = NULL;
+ callbacks->cb_0x16 = NULL;
+ callbacks->cb_0x17 = NULL;
+ callbacks->cb_0x1C = NULL;
+ callbacks->cb_0x1E = NULL;
+ callbacks->cb_0x23 = NULL;
+}
+
+
+REWError parseWiseScript(const char * filepath, WiseScriptCallbacks * callbacks) {+ FILE * fp;
+ REWError status;
+ unsigned char op;
+
+ // check if file exists
+ if (access(filepath, F_OK) != 0) {+ printError("parseWiseScript input file '%s' not found\n", filepath);+ printError("parseWiseScript errno: %s\n", strerror(errno));+ return REWERR_ERRNO;
+ }
+
+ // open the file
+ fp = fopen(filepath, "rb");
+
+ // failed to open the file
+ if (fp == NULL) {+ printError("parseWiseScript failed to open file '%s'\n", filepath);+ printError("parseWiseScript errno: %s\n", strerror(errno));+ return REWERR_ERRNO;
+ }
+
+ // Read the header
+ {+ WiseScriptHeader header;
+ status = readWiseScriptHeader(fp, &header);
+ if (status != REWERR_OK) {+ printError("parseWiseScript failed to read header. %d\n", status);+ fclose(fp);
+ return status;
+ }
+
+ // callback
+ if (callbacks->cb_header != NULL) {+ (*callbacks->cb_header)(&header);
+ }
+
+ // cleanup
+ freeWiseScriptHeader(&header);
+ }
+
+ // Read the texts
+ {+ WiseScriptTexts texts;
+ status = readWiseScriptTexts(fp, &texts);
+ if (status != REWERR_OK) {+ printError("parseWiseScript failed to read texts. %d\n", status);+ fclose(fp);
+ return status;
+ }
+
+ // callback
+ if (callbacks->cb_texts != NULL) {+ (*callbacks->cb_texts)(&texts);
+ }
+
+ // cleanup
+ freeWiseScriptTexts(&texts);
+ }
+
+ // Read operation and struct
+ WiseScriptSTOP = 0;
+ while (status == REWERR_OK && WiseScriptSTOP == 0) {+ int ch = fgetc(fp);
+ op = (unsigned char)ch;
+
+ if (ch == EOF) {+ break;
+ }
+
+ switch (op) {+ case 0x00:
+ {+ WiseScriptFileHeader data;
+ status = readWiseScriptFileHeader(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x00 != NULL) {+ (*callbacks->cb_0x00)(&data);
+ }
+ freeWiseScriptFileHeader(&data);
+ }
+ }
+ break;
+
+ case 0x03:
+ {+ WiseScriptUnknown0x03 data;
+ status = readWiseScriptUnknown0x03(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x03 != NULL) {+ (*callbacks->cb_0x03)(&data);
+ }
+ freeWiseScriptUnknown0x03(&data);
+ }
+ }
+ break;
+
+ case 0x04:
+ {+ WiseScriptUnknown0x04 data;
+ status = readWiseScriptUnknown0x04(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x04 != NULL) {+ (*callbacks->cb_0x04)(&data);
+ }
+ freeWiseScriptUnknown0x04(&data);
+ }
+ }
+ break;
+
+ case 0x05:
+ {+ WiseScriptUnknown0x05 data;
+ status = readWiseScriptUnknown0x05(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x05 != NULL) {+ (*callbacks->cb_0x05)(&data);
+ }
+ freeWiseScriptUnknown0x05(&data);
+ }
+ }
+ break;
+
+ case 0x06:
+ {+ WiseScriptUnknown0x06 data;
+ status = readWiseScriptUnknown0x06(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x06 != NULL) {+ (*callbacks->cb_0x06)(&data);
+ }
+ }
+ }
+ break;
+
+ case 0x07:
+ {+ WiseScriptUnknown0x07 data;
+ status = readWiseScriptUnknown0x07(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x07 != NULL) {+ (*callbacks->cb_0x07)(&data);
+ }
+ freeWiseScriptUnknown0x07(&data);
+ }
+ }
+ break;
+
+ case 0x08:
+ {+ WiseScriptUnknown0x08 data;
+ status = readWiseScriptUnknown0x08(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x08 != NULL) {+ (*callbacks->cb_0x08)(&data);
+ }
+ }
+ }
+ break;
+
+ case 0x09:
+ {+ WiseScriptUnknown0x09 data;
+ status = readWiseScriptUnknown0x09(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x09 != NULL) {+ (*callbacks->cb_0x09)(&data);
+ }
+ freeWiseScriptUnknown0x09(&data);
+ }
+ }
+ break;
+
+ case 0x0A:
+ {+ WiseScriptUnknown0x0A data;
+ status = readWiseScriptUnknown0x0A(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x0A != NULL) {+ (*callbacks->cb_0x0A)(&data);
+ }
+ freeWiseScriptUnknown0x0A(&data);
+ }
+ }
+ break;
+
+ case 0x0B:
+ {+ WiseScriptUnknown0x0B data;
+ status = readWiseScriptUnknown0x0B(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x0B != NULL) {+ (*callbacks->cb_0x0B)(&data);
+ }
+ freeWiseScriptUnknown0x0B(&data);
+ }
+ }
+ break;
+
+ case 0x0C:
+ {+ WiseScriptUnknown0x0C data;
+ status = readWiseScriptUnknown0x0C(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x0C != NULL) {+ (*callbacks->cb_0x0C)(&data);
+ }
+ freeWiseScriptUnknown0x0C(&data);
+ }
+ }
+ break;
+
+ case 0x0F:
+ // Start form data?
+ if (callbacks->cb_0x0F != NULL) {+ (*callbacks->cb_0x0F)();
+ }
+ break;
+
+ case 0x10:
+ // end form data?
+ if (callbacks->cb_0x10 != NULL) {+ (*callbacks->cb_0x10)();
+ }
+ break;
+
+ case 0x11:
+ {+ WiseScriptUnknown0x11 data;
+ status = readWiseScriptUnknown0x11(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x11 != NULL) {+ (*callbacks->cb_0x11)(&data);
+ }
+ freeWiseScriptUnknown0x11(&data);
+ }
+ }
+ break;
+
+ case 0x12:
+ {+ WiseScriptUnknown0x12 data;
+ status = readWiseScriptUnknown0x12(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x12 != NULL) {+ (*callbacks->cb_0x12)(&data);
+ }
+ freeWiseScriptUnknown0x12(&data);
+ }
+ }
+ break;
+
+ case 0x14:
+ {+ WiseScriptUnknown0x14 data;
+ status = readWiseScriptUnknown0x14(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x14 != NULL) {+ (*callbacks->cb_0x14)(&data);
+ }
+ freeWiseScriptUnknown0x14(&data);
+ }
+ }
+ break;
+
+ case 0x15:
+ {+ WiseScriptUnknown0x15 data;
+ status = readWiseScriptUnknown0x15(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x15 != NULL) {+ (*callbacks->cb_0x15)(&data);
+ }
+ freeWiseScriptUnknown0x15(&data);
+ }
+ }
+ break;
+
+ case 0x16:
+ {+ WiseScriptUnknown0x16 data;
+ status = readWiseScriptUnknown0x16(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x16 != NULL) {+ (*callbacks->cb_0x16)(&data);
+ }
+ freeWiseScriptUnknown0x16(&data);
+ }
+ }
+ break;
+
+ case 0x17:
+ {+ WiseScriptUnknown0x17 data;
+ status = readWiseScriptUnknown0x17(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x17 != NULL) {+ (*callbacks->cb_0x17)(&data);
+ }
+ freeWiseScriptUnknown0x17(&data);
+ }
+ }
+ break;
+
+ // skip tailing zeros
+ case 0x18:
+ ch = 0x00;
+ while (ch != EOF && ch == 0x00) {+ ch = fgetc(fp);
+ }
+ if (ch != EOF) {+ fseek(fp, -1, SEEK_CUR);
+ }
+ break;
+
+ case 0x1B:
+ case 0x0D:
+ case 0x24: // TODO Skip? Only seen in RTCW
+ case 0x25: // TODO Skip? Only seen in RTCW
+ // Skip this byte
+ break;
+
+ case 0x1C:
+ {+ WiseScriptUnknown0x1C data;
+ status = readWiseScriptUnknown0x1C(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x1C != NULL) {+ (*callbacks->cb_0x1C)(&data);
+ }
+ freeWiseScriptUnknown0x1C(&data);
+ }
+ }
+ break;
+
+ case 0x1E:
+ {+ WiseScriptUnknown0x1E data;
+ status = readWiseScriptUnknown0x1E(fp, &data);
+ if (status == REWERR_OK && callbacks->cb_0x1E != NULL) {+ (*callbacks->cb_0x1E)(&data);
+ }
+ }
+ break;
+
+ case 0x23:
+ {+ WiseScriptUnknown0x23 data;
+ status = readWiseScriptUnknown0x23(fp, &data);
+ if (status == REWERR_OK) {+ if (callbacks->cb_0x23 != NULL) {+ (*callbacks->cb_0x23)(&data);
+ }
+ freeWiseScriptUnknown0x23(&data);
+ }
+ }
+ break;
+
+ default:
+ printError("parseWiseScript unknown OP: %02X at 0x%08X\n", ch,+ ftell(fp));
+ status = REWERR_NOOPT;
+ break;
+ }
+ }
+
+ fclose(fp);
+
+ if (status != REWERR_OK) {+ printError("parseWiseScript OP 0x%02X failed\n", op);+ }
+
+ return status;
+}
+
+
+void stopWiseScriptParse(void) {+ WiseScriptSTOP = 1;
+}
+
+
+static WiseScriptParsedInfo WISESCRIPT_PARSED_INFO = {+ .totalInflatedSize = 0,
+ .inflatedSize0x00 = 0,
+ .inflatedSize0x06 = 0,
+ .inflatedSize0x14 = 0,
+ .inflateStartOffset = 0
+};
+
+void updateParsedInfo0x00(WiseScriptFileHeader * data) {+ if (data->deflateEnd > WISESCRIPT_PARSED_INFO.inflateStartOffset ) {+ WISESCRIPT_PARSED_INFO.inflateStartOffset = data->deflateEnd;
+ }
+ WISESCRIPT_PARSED_INFO.totalInflatedSize += data->inflatedSize;
+ WISESCRIPT_PARSED_INFO.inflatedSize0x00 += data->inflatedSize;
+}
+
+void updateParsedInfo0x06(WiseScriptUnknown0x06 * data) {+ if (data->deflateEnd > WISESCRIPT_PARSED_INFO.inflateStartOffset ) {+ WISESCRIPT_PARSED_INFO.inflateStartOffset = data->deflateEnd;
+ }
+ WISESCRIPT_PARSED_INFO.totalInflatedSize += data->inflatedSize;
+ WISESCRIPT_PARSED_INFO.inflatedSize0x06 += data->inflatedSize;
+}
+
+void updateParsedInfo0x14(WiseScriptUnknown0x14 * data) {+ if (data->deflateEnd > WISESCRIPT_PARSED_INFO.inflateStartOffset ) {+ WISESCRIPT_PARSED_INFO.inflateStartOffset = data->deflateEnd;
+ }
+ WISESCRIPT_PARSED_INFO.totalInflatedSize += data->inflatedSize;
+ WISESCRIPT_PARSED_INFO.inflatedSize0x14 += data->inflatedSize;
+}
+
+WiseScriptParsedInfo * wiseScriptGetParsedInfo(const char * filepath) {+ WiseScriptCallbacks callbacks;
+ REWError status;
+
+ initWiseScriptCallbacks(&callbacks);
+ callbacks.cb_0x00 = &updateParsedInfo0x00;
+ callbacks.cb_0x06 = &updateParsedInfo0x06;
+ callbacks.cb_0x14 = &updateParsedInfo0x14;
+
+ WISESCRIPT_PARSED_INFO.totalInflatedSize = 0;
+ WISESCRIPT_PARSED_INFO.inflateStartOffset = 0;
+
+ status = parseWiseScript(filepath, &callbacks);
+ if (status != REWERR_OK) {+ printError("wiseScriptGetDeflateEnd parsing failed\n");+ return NULL;
+ }
+
+ return &WISESCRIPT_PARSED_INFO;
+}
+
+
+REWError wiseScriptDebugPrint(const char * filepath) {+ WiseScriptCallbacks callbacks;
+
+ initWiseScriptCallbacks(&callbacks);
+ callbacks.cb_header = &printWiseScriptHeader;
+ callbacks.cb_texts = &printWiseScriptTexts;
+ callbacks.cb_0x00 = &printWiseScriptFileHeader;
+ callbacks.cb_0x03 = &printWiseScriptUnknown0x03;
+ callbacks.cb_0x04 = &printWiseScriptUnknown0x04;
+ callbacks.cb_0x05 = &printWiseScriptUnknown0x05;
+ callbacks.cb_0x06 = &printWiseScriptUnknown0x06;
+ callbacks.cb_0x07 = &printWiseScriptUnknown0x07;
+ callbacks.cb_0x08 = &printWiseScriptUnknown0x08;
+ callbacks.cb_0x09 = &printWiseScriptUnknown0x09;
+ callbacks.cb_0x0A = &printWiseScriptUnknown0x0A;
+ callbacks.cb_0x0B = &printWiseScriptUnknown0x0B;
+ callbacks.cb_0x0C = &printWiseScriptUnknown0x0C;
+ callbacks.cb_0x11 = &printWiseScriptUnknown0x11;
+ callbacks.cb_0x12 = &printWiseScriptUnknown0x12;
+ callbacks.cb_0x14 = &printWiseScriptUnknown0x14;
+ callbacks.cb_0x15 = &printWiseScriptUnknown0x15;
+ callbacks.cb_0x16 = &printWiseScriptUnknown0x16;
+ callbacks.cb_0x17 = &printWiseScriptUnknown0x17;
+ callbacks.cb_0x1C = &printWiseScriptUnknown0x1C;
+ callbacks.cb_0x1E = &printWiseScriptUnknown0x1E;
+ callbacks.cb_0x23 = &printWiseScriptUnknown0x23;
+
+ return parseWiseScript(filepath, &callbacks);
+}
+
+
+// Must be a valid pointer to a \0 terminated string.
+char * wiseScriptParsePath(char * path) {+ char * pathCopy;
+ char * section;
+ char newPath[PATH_MAX];
+ char * returnPath;
+ uint32_t strSize = 0;
+
+ // Basic verification that this string may be a valid path
+ do {+ unsigned char ch = path[strSize];
+
+ if (ch == 0x00) {+ break;
+ }
+
+ // It contains a illegal character
+ if (ch < 0x20 || ch > 0x7E || ch == '/') {+ printError("wiseScriptParsePath path contains an illegal character "+ "0x%02X\n", ch);
+ return NULL;
+ }
+
+ strSize++;
+
+ // Path is to long
+ if (strSize > WIN_PATH_MAX) {+ printError("wiseScriptParsePath path is larger then WIN_PATH_MAX\n");+ return NULL;
+ }
+
+ } while (1);
+
+ // Check that the path starts with '%'
+ if (path[0] != 0x25) {+ printError("wiseScriptParsePath path does not start with '%'\n");+ return NULL;
+ }
+
+ // Duplicate the path for the use with strtok
+ pathCopy = strdup(path);
+ if (pathCopy == NULL) {+ printError("wiseScriptParsePath errno: %s\n", strerror(errno));+ return NULL;
+ }
+
+ newPath[0] = 0x00;
+ section = strtok(pathCopy, "\\");
+ do {+ size_t sectionLen = strlen(section);
+
+ // Strip '%' from variables
+ if (section[0] == 0x25 && section[sectionLen - 1] == 0x25) {+ section[sectionLen - 1] = 0x00;
+ section++;
+
+ if (section[0] == 0x00) {+ printError("wiseScriptParsePath empty path component.\n");+ free(pathCopy);
+ return NULL;
+ }
+ }
+
+ if (sectionLen > NAME_MAX) {+ printError("wiseScriptParsePath path component name exceeds NAME_MAX\n");+ free(pathCopy);
+ return NULL;
+ }
+
+ // Don't allow a path section to start with '..'
+ if (sectionLen >= 2) {+ if (section[0] == '.' && section[1] == '.') {+ printError("wiseScriptParsePath path component starts with '..'."+ " Symbolic paths are not allowed! Path: '%s'\n", path);
+ free(pathCopy);
+ return NULL;
+ }
+ }
+
+ strcat(newPath, "/");
+ strcat(newPath, section);
+ } while ((section = strtok(NULL, "\\")));
+
+
+ free(pathCopy);
+
+ returnPath = strdup(newPath + 1); // +1 to remove the first character '/'
+
+ if (returnPath == NULL) {+ printError("wiseScriptParsePath errno: %s\n", strerror(errno));+ return NULL;
+ }
+
+ return returnPath;
+}
--- /dev/null
+++ b/src/wisescript.h
@@ -1,0 +1,386 @@
+/* This file is part of REWise.
+ *
+ * REWise 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.
+ *
+ * REWise 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/>.
+ */
+
+/*
+DISCLAIMER
+----------
+The way the data is interpreted may be very wrong! It is the result of many
+hours of puzzling with ImHex and this is what made the most sense to me with
+the data (different installer executables) available at this time. Also what
+most values represent inside a operation struct is still a mystery.
+
+Another source of getting insight on how the Wise installer handles this would
+be to disassemble wise0132.dll, I have done this with Cutter and its Ghidra
+plugin but it was very hard since I couldn't rename about half of the variables,
+which is a known issue of Cutter + the Ghidra plugin (at this time), but it
+gave some hints.
+
+
+INSEPCTED INSTALLERS
+--------------------
+
+NAME MD5 FILE
+---- --- ----
+HL:CS (CD) 43cd9aff74e04fa1b59321b898d9a6bd counter-strike.exe
+HLGOTY (CD) f5b8b35ca02beeeb146e62a63a0273a6 SETUP.EXE
+CS15 eedcfcf6545cef92f26fb9a7fdd77c42 csv15full.exe
+RTCW (CD) f2d9e3e1eaaed66047210881d130384f Setup.exe
+ET 5cc104767ecdf0feb3a36210adf46a8e WolfET.exe
+
+The way this program currently interprets the binary Wise script file is as
+follows:
+
+ 1. Read 'struct WiseScriptHeader'
+ 2. Read 'struct WiseScriptTexts'
+ 3. Read one byte as operation code, we use this value to determine what struct
+ to read, after that struct is read, continue this step (read one operation
+ byte again etc..)
+
+Operation codes:
+
+ 0x00 // Custom deflate file header
+ 0x03 // ?
+ 0x04 // Form data?
+ 0x05 // .ini file, section-name and values for that section
+ 0x06 // Deflated file just used by the installer? (No filename)
+ 0x07
+ 0x08
+ 0x09
+ 0x0A
+ 0x0B
+ 0x0C
+ 0x0D // Skip this byte?
+ 0x0F // Start form data?
+ 0x10 // End form data?
+ 0x11
+ 0x12 // File on install medium (CD/DVD), to copy?
+ 0x14 // Deflated file just used by the installer? (No filename)
+ 0x15
+ 0x16 // Temp filename?
+ 0x17
+ 0x18 // Skip this byte? ET suggests to skip all tailing zeros
+ 0x23
+ 0x24 // Skip this byte? Only seen in RTCW
+ 0x25 // Skip this byte? Only seen in RTCW
+ 0x1B // Skip this byte?
+ 0x1C
+ 0x1E
+*/
+
+#ifndef H_WISESCRIPT
+#define H_WISESCRIPT
+
+#include <stdio.h>
+#include <stdint.h>
+
+
+#include "errors.h"
+
+#define WIN_PATH_MAX 260
+
+
+/* WiseScriptHeader */
+typedef struct {+ unsigned char unknown_44[44];
+ char * logPath; // \0 terminated string
+ char * font; // \0 terminated string
+ unsigned char unknown_14[14];
+} WiseScriptHeader;
+
+/* WiseScriptTexts */
+typedef struct {+ // 56 \0 terminated strings
+ char * installTexts[56];
+} WiseScriptTexts;
+
+/* 0x00 WiseScriptFileHeader */
+typedef struct {+ unsigned char unknown_2[2]; // seen: 0x8000, 0x8100, 0x0000, 0x9800 0xA100
+ uint32_t deflateStart;
+ uint32_t deflateEnd;
+ uint16_t date;
+ uint16_t time;
+ uint32_t inflatedSize;
+ unsigned char unknown_40[20];
+ uint32_t crc32;
+ char * destFile; // \0 terminated string
+ char * fileText; // \0 terminated string
+ unsigned char terminator; // always \0? terminator?
+} WiseScriptFileHeader;
+
+/* WiseScriptUnknown0x03 */
+typedef struct {+ unsigned char unknown_1; // unknown
+ char * unknownString_1; // \0 terminated string
+ char * unknownString_2; // \0 terminated string
+} WiseScriptUnknown0x03;
+
+/* WiseScriptUnknown0x04 Form data? */
+typedef struct {+ unsigned char no; // read this struct again until 'no' == 0 ?
+ char * dataString; // \0 terminated string
+} WiseScriptUnknown0x04;
+
+/* WiseScriptUnknown0x05 Something with .ini file */
+typedef struct {+ // write .ini file?
+ char * file; // open for writing in append mode
+ char * section; // ini section text
+ char * values; // multiline string containing values.
+} WiseScriptUnknown0x05;
+
+/* WiseScriptUnknown0x06 deflated Wise file? */
+typedef struct {+ unsigned char unknown[6];
+ uint32_t deflateStart;
+ uint32_t deflateEnd;
+ uint32_t inflatedSize;
+ unsigned char unknown1; // terminator?
+} WiseScriptUnknown0x06;
+
+/* WiseScriptUnknown0x07 */
+typedef struct {+ unsigned char unknown_1;
+ char * unknownString_1;
+ char * unknownString_2;
+ char * unknownString_3;
+} WiseScriptUnknown0x07;
+
+/* WiseScriptUnknown0x08 */
+typedef struct {+ unsigned char unknown_1;
+} WiseScriptUnknown0x08;
+
+/* WiseScriptUnknown0x09 */
+typedef struct {+ unsigned char unknown_1;
+ unsigned char unknown_2; // only when NOT 0x0901 or 0x0920
+ char * unknownString_1;
+ char * unknownString_2;
+ char * unknownString_3;
+ char * unknownString_4;
+ char * unknownString_5; // only when 0x0901 or 0x0920
+} WiseScriptUnknown0x09;
+
+/* WiseScriptUnknown0x0A */
+typedef struct {+ unsigned char unknown_2[2]; // 0x0200
+ char * unknownString_1;
+ char * unknownString_2;
+ char * unknownString_3;
+} WiseScriptUnknown0x0A;
+
+/* WiseScriptUnknown0x0B */
+typedef struct {+ unsigned char unknown_1;
+ char * unknownString_1;
+} WiseScriptUnknown0x0B;
+
+/* WiseScriptUnknown0x0C */
+typedef struct {+ unsigned char unknown_1;
+ char * unknownString_1;
+ char * unknownString_2;
+} WiseScriptUnknown0x0C;
+
+/* WiseScriptUnknown0x11 */
+typedef struct {+ char * unknownString_1;
+} WiseScriptUnknown0x11;
+
+/* WiseScriptUnknown0x12 File on install medium (CD/DVD) */
+typedef struct {+ unsigned char unknown_1; // 0C
+ unsigned char unknown_41[41];
+ char * sourceFile;
+ char * unknownString_1;
+ char * unknownString_2;
+ char * destFile;
+} WiseScriptUnknown0x12;
+
+/* WiseScriptUnknown0x14 Wise script file? */
+typedef struct {+ uint32_t deflateStart;
+ uint32_t deflateEnd;
+ uint32_t inflatedSize;
+ char * name;
+ char * message;
+} WiseScriptUnknown0x14;
+
+/* WiseScriptUnknown0x15 */
+typedef struct {+ unsigned char unknown_1;
+ char * unknownString_1;
+ char * unknownString_2;
+} WiseScriptUnknown0x15;
+
+/* WiseScriptUnknown0x16 (TempFileName) */
+typedef struct {+ char * name;
+} WiseScriptUnknown0x16;
+
+/* WiseScriptUnknown0x17 */
+typedef struct {+ unsigned char unknown_1;
+ unsigned char unknown_4[4];
+ char * unknownString_1;
+} WiseScriptUnknown0x17;
+
+/* WiseScriptUnknown0x1C */
+typedef struct {+ char * unknownString_1;
+} WiseScriptUnknown0x1C;
+
+/* WiseScriptUnknown0x1E */
+typedef struct {+ unsigned char unknown_2[2];
+} WiseScriptUnknown0x1E;
+
+/* WiseScriptUnknown0x23 */
+typedef struct {+ unsigned char unknown_1;
+ char * unknownString_1;
+ char * unknownString_2;
+} WiseScriptUnknown0x23;
+
+
+REWError readWiseScriptHeader(FILE * fp, WiseScriptHeader * header);
+REWError readWiseScriptTexts(FILE * fp, WiseScriptTexts * texts);
+REWError readWiseScriptFileHeader(FILE * fp, WiseScriptFileHeader * data);
+REWError readWiseScriptUnknown0x03(FILE * fp, WiseScriptUnknown0x03 * data);
+REWError readWiseScriptUnknown0x04(FILE * fp, WiseScriptUnknown0x04 * data);
+REWError readWiseScriptUnknown0x05(FILE * fp, WiseScriptUnknown0x05 * data);
+REWError readWiseScriptUnknown0x06(FILE * fp, WiseScriptUnknown0x06 * data); // no-free (no strings)
+REWError readWiseScriptUnknown0x07(FILE * fp, WiseScriptUnknown0x07 * data);
+REWError readWiseScriptUnknown0x08(FILE * fp, WiseScriptUnknown0x08 * data); // no-free (no strings)
+REWError readWiseScriptUnknown0x09(FILE * fp, WiseScriptUnknown0x09 * data);
+REWError readWiseScriptUnknown0x0A(FILE * fp, WiseScriptUnknown0x0A * data);
+REWError readWiseScriptUnknown0x0B(FILE * fp, WiseScriptUnknown0x0B * data);
+REWError readWiseScriptUnknown0x0C(FILE * fp, WiseScriptUnknown0x0C * data);
+REWError readWiseScriptUnknown0x11(FILE * fp, WiseScriptUnknown0x11 * data);
+REWError readWiseScriptUnknown0x12(FILE * fp, WiseScriptUnknown0x12 * data);
+REWError readWiseScriptUnknown0x14(FILE * fp, WiseScriptUnknown0x14 * data);
+REWError readWiseScriptUnknown0x15(FILE * fp, WiseScriptUnknown0x15 * data);
+REWError readWiseScriptUnknown0x16(FILE * fp, WiseScriptUnknown0x16 * data);
+REWError readWiseScriptUnknown0x17(FILE * fp, WiseScriptUnknown0x17 * data);
+REWError readWiseScriptUnknown0x1C(FILE * fp, WiseScriptUnknown0x1C * data);
+REWError readWiseScriptUnknown0x1E(FILE * fp, WiseScriptUnknown0x1E * data); // no-free (no strings)
+REWError readWiseScriptUnknown0x23(FILE * fp, WiseScriptUnknown0x23 * data);
+
+void freeWiseScriptHeader(WiseScriptHeader * header);
+void freeWiseScriptTexts(WiseScriptTexts * texts);
+void freeWiseScriptFileHeader(WiseScriptFileHeader * data);
+void freeWiseScriptUnknown0x03(WiseScriptUnknown0x03 * data);
+void freeWiseScriptUnknown0x04(WiseScriptUnknown0x04 * data);
+void freeWiseScriptUnknown0x05(WiseScriptUnknown0x05 * data);
+void freeWiseScriptUnknown0x07(WiseScriptUnknown0x07 * data);
+void freeWiseScriptUnknown0x09(WiseScriptUnknown0x09 * data);
+void freeWiseScriptUnknown0x0A(WiseScriptUnknown0x0A * data);
+void freeWiseScriptUnknown0x0B(WiseScriptUnknown0x0B * data);
+void freeWiseScriptUnknown0x0C(WiseScriptUnknown0x0C * data);
+void freeWiseScriptUnknown0x11(WiseScriptUnknown0x11 * data);
+void freeWiseScriptUnknown0x12(WiseScriptUnknown0x12 * data);
+void freeWiseScriptUnknown0x14(WiseScriptUnknown0x14 * data);
+void freeWiseScriptUnknown0x15(WiseScriptUnknown0x15 * data);
+void freeWiseScriptUnknown0x16(WiseScriptUnknown0x16 * data);
+void freeWiseScriptUnknown0x17(WiseScriptUnknown0x17 * data);
+void freeWiseScriptUnknown0x1C(WiseScriptUnknown0x1C * data);
+void freeWiseScriptUnknown0x23(WiseScriptUnknown0x23 * data);
+
+
+// For debugging
+void printWiseScriptHeader(WiseScriptHeader * header);
+void printWiseScriptTexts(WiseScriptTexts * texts);
+void printWiseScriptFileHeader(WiseScriptFileHeader * data);
+void printWiseScriptUnknown0x03(WiseScriptUnknown0x03 * data);
+void printWiseScriptUnknown0x04(WiseScriptUnknown0x04 * data);
+void printWiseScriptUnknown0x05(WiseScriptUnknown0x05 * data);
+void printWiseScriptUnknown0x06(WiseScriptUnknown0x06 * data);
+void printWiseScriptUnknown0x07(WiseScriptUnknown0x07 * data);
+void printWiseScriptUnknown0x08(WiseScriptUnknown0x08 * data);
+void printWiseScriptUnknown0x09(WiseScriptUnknown0x09 * data);
+void printWiseScriptUnknown0x0A(WiseScriptUnknown0x0A * data);
+void printWiseScriptUnknown0x0B(WiseScriptUnknown0x0B * data);
+void printWiseScriptUnknown0x0C(WiseScriptUnknown0x0C * data);
+void printWiseScriptUnknown0x11(WiseScriptUnknown0x11 * data);
+void printWiseScriptUnknown0x12(WiseScriptUnknown0x12 * data);
+void printWiseScriptUnknown0x14(WiseScriptUnknown0x14 * data);
+void printWiseScriptUnknown0x15(WiseScriptUnknown0x15 * data);
+void printWiseScriptUnknown0x16(WiseScriptUnknown0x16 * data);
+void printWiseScriptUnknown0x17(WiseScriptUnknown0x17 * data);
+void printWiseScriptUnknown0x1C(WiseScriptUnknown0x1C * data);
+void printWiseScriptUnknown0x1E(WiseScriptUnknown0x1E * data);
+void printWiseScriptUnknown0x23(WiseScriptUnknown0x23 * data);
+
+
+typedef struct {+ void (*cb_header)(WiseScriptHeader*);
+ void (*cb_texts)(WiseScriptTexts*);
+ void (*cb_0x00)(WiseScriptFileHeader*);
+ void (*cb_0x03)(WiseScriptUnknown0x03*);
+ void (*cb_0x04)(WiseScriptUnknown0x04*);
+ void (*cb_0x05)(WiseScriptUnknown0x05*);
+ void (*cb_0x06)(WiseScriptUnknown0x06*);
+ void (*cb_0x07)(WiseScriptUnknown0x07*);
+ void (*cb_0x08)(WiseScriptUnknown0x08*);
+ void (*cb_0x09)(WiseScriptUnknown0x09*);
+ void (*cb_0x0A)(WiseScriptUnknown0x0A*);
+ void (*cb_0x0B)(WiseScriptUnknown0x0B*);
+ void (*cb_0x0C)(WiseScriptUnknown0x0C*);
+ void (*cb_0x0F)(void); // start form data?
+ void (*cb_0x10)(void); // end form data?
+ void (*cb_0x11)(WiseScriptUnknown0x11*);
+ void (*cb_0x12)(WiseScriptUnknown0x12*);
+ void (*cb_0x14)(WiseScriptUnknown0x14*);
+ void (*cb_0x15)(WiseScriptUnknown0x15*);
+ void (*cb_0x16)(WiseScriptUnknown0x16*);
+ void (*cb_0x17)(WiseScriptUnknown0x17*);
+ void (*cb_0x1C)(WiseScriptUnknown0x1C*);
+ void (*cb_0x1E)(WiseScriptUnknown0x1E*);
+ void (*cb_0x23)(WiseScriptUnknown0x23*);
+} WiseScriptCallbacks;
+
+
+typedef struct {+ size_t totalInflatedSize;
+ size_t inflatedSize0x00;
+ size_t inflatedSize0x06;
+ size_t inflatedSize0x14;
+ // Deflated files described in the WiseScript have a offset to the deflated
+ // data, but this offset also has a offset which we can be found by iterating
+ // through all file structs and note the largest end-deflate offset, use that
+ // to subtract from the filesize.
+ //
+ // PE_filesize - largestEndDeflate
+ //
+ // This offset needs to be added to the offset described in the WiseScript to
+ // get to the real offset of the deflated data.
+ uint32_t inflateStartOffset;
+} WiseScriptParsedInfo;
+
+
+void initWiseScriptCallbacks(WiseScriptCallbacks * callbacks);
+REWError parseWiseScript(const char * filepath, WiseScriptCallbacks * callbacks);
+void stopWiseScriptParse(void);
+
+WiseScriptParsedInfo * wiseScriptGetParsedInfo(const char * filepath);
+
+// Debug print the parsed WiseScript structures
+REWError wiseScriptDebugPrint(const char * filepath);
+
+char * wiseScriptParsePath(char * path);
+
+#endif
--
⑨