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