shithub: rewise

Download patch

ref: ce1852b585349f8244fb132b199004f22d2a99fb
author: CYBERDEViL <[email protected]>
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(&sectionHeader, 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, &times) != 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