From ce1852b585349f8244fb132b199004f22d2a99fb Mon Sep 17 00:00:00 2001 From: CYBERDEViL Date: Wed, 3 May 2023 18:22:39 +0200 Subject: [PATCH] Initial commit --- COPYING | 674 +++++++++++++++ Makefile | 22 + README.md | 69 ++ imhex/WISESCRIPT.hexpat | 224 +++++ src/crc32.c | 145 ++++ src/crc32.h | 39 + src/errors.h | 28 + src/inflate.c | 1189 ++++++++++++++++++++++++++ src/inflate.h | 83 ++ src/pefile.c | 162 ++++ src/pefile.h | 75 ++ src/print.c | 80 ++ src/print.h | 40 + src/reader.c | 135 +++ src/reader.h | 37 + src/rewise.c | 729 ++++++++++++++++ src/version.h | 30 + src/wiseoverlay.c | 95 +++ src/wiseoverlay.h | 36 + src/wisescript.c | 1738 +++++++++++++++++++++++++++++++++++++++ src/wisescript.h | 386 +++++++++ 21 files changed, 6016 insertions(+) create mode 100644 COPYING create mode 100644 Makefile create mode 100644 README.md create mode 100644 imhex/WISESCRIPT.hexpat create mode 100644 src/crc32.c create mode 100644 src/crc32.h create mode 100644 src/errors.h create mode 100644 src/inflate.c create mode 100644 src/inflate.h create mode 100644 src/pefile.c create mode 100644 src/pefile.h create mode 100644 src/print.c create mode 100644 src/print.h create mode 100644 src/reader.c create mode 100644 src/reader.h create mode 100644 src/rewise.c create mode 100644 src/version.h create mode 100644 src/wiseoverlay.c create mode 100644 src/wiseoverlay.h create mode 100644 src/wisescript.c create mode 100644 src/wisescript.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..369db23 --- /dev/null +++ b/Makefile @@ -0,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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..10bc862 --- /dev/null +++ b/README.md @@ -0,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 diff --git a/imhex/WISESCRIPT.hexpat b/imhex/WISESCRIPT.hexpat new file mode 100644 index 0000000..aa95e3e --- /dev/null +++ b/imhex/WISESCRIPT.hexpat @@ -0,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 + +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; +*/ diff --git a/src/crc32.c b/src/crc32.c new file mode 100644 index 0000000..d454c2d --- /dev/null +++ b/src/crc32.c @@ -0,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 . + */ + +#include + +#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 diff --git a/src/crc32.h b/src/crc32.h new file mode 100644 index 0000000..3189f5c --- /dev/null +++ b/src/crc32.h @@ -0,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 . + */ + +#ifndef H_REWISE_CRC32 +#define H_REWISE_CRC32 + +#include + +// 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 diff --git a/src/errors.h b/src/errors.h new file mode 100644 index 0000000..7b7bd41 --- /dev/null +++ b/src/errors.h @@ -0,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 . + */ + +#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 diff --git a/src/inflate.c b/src/inflate.c new file mode 100644 index 0000000..8641e8d --- /dev/null +++ b/src/inflate.c @@ -0,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 . + */ + +#include + +#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; +} diff --git a/src/inflate.h b/src/inflate.h new file mode 100644 index 0000000..084a3d5 --- /dev/null +++ b/src/inflate.h @@ -0,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 . + */ + +#ifndef H_REWISE_INFLATE +#define H_REWISE_INFLATE + +#include +#include +#include +#include +#include + +#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 diff --git a/src/pefile.c b/src/pefile.c new file mode 100644 index 0000000..c4d83e3 --- /dev/null +++ b/src/pefile.c @@ -0,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 . + */ + +#include +#include + +#include "reader.h" +#include "pefile.h" +#include "print.h" + + +/* pefileGetOverlayOffset() - Get the overlay-offset of a PE file. + * + * @returns: The offset to overlay-data on success, <0 on error. */ +long pefileGetOverlayOffset(const char * filePath) { + long fileSize; + FILE * fp; + size_t noRead; + long overlayOffset; + + // Open file + fp = fopen(filePath, "rb"); + + // Failed to open file + if (fp == NULL) { + printError("pefileGetOverlayOffset failed to open '%s'\n", filePath); + return -1; + } + + // Determine file size + if (fseek(fp, 0l, SEEK_END) != 0) { + printError("pefileGetOverlayOffset failed to seek to end of file '%s'\n", + filePath); + fclose(fp); + return -1; + } + if ((fileSize = ftell(fp)) < 1) { + printError("pefileGetOverlayOffset failed to determine file size of file " + "'%s'\n", filePath); + fclose(fp); + return -1; + } + // Set cursor back to start of the file + if (fseek(fp, 0l, SEEK_SET) != 0) { + printError("pefileGetOverlayOffset failed to seek to start of file '%s'\n", + filePath); + fclose(fp); + return -1; + } + + // Read MsDosHeader + size_t readSize = sizeof(MsDosHeader); + if (readSize > fileSize) { + printError("pefileGetOverlayOffset file is to small to contain a MS Dos " + "header. File: '%s'\n", filePath); + fclose(fp); + return -1; + } + MsDosHeader msDosHeader; + noRead = fread(&msDosHeader, readSize, 1, fp); + if (noRead != 1) { + printError("pefileGetOverlayOffset failed to read the MS Dos header. " + "noRead: %ld readSize: %ld\n", noRead, readSize); + fclose(fp); + return -1; + } + + if (msDosHeader.signature != 0x5A4D) { + printError("pefileGetOverlayOffset this is not a PE file for sure. The " + "MS-DOS header signature doesn't match (not MZ).\n"); + fclose(fp); + return -1; + } + + // Read e_lfanew (offset to PE file header) + uint32_t e_lfanew; + if (fseek(fp, 0x3C, SEEK_SET) != 0) { + printError("pefileGetOverlayOffset failed to seek to 0x3C.\n"); + printError("pefileGetOverlayOffset errno: %s\n", strerror(errno)); + fclose(fp); + return -1; + } + if (fread(&e_lfanew, 4, 1, fp) != 1) { + printError("pefileGetOverlayOffset failed to read e_lfanew.\n"); + fclose(fp); + return -1; + } + if (e_lfanew >= fileSize) { + printError("pefileGetOverlayOffset PE file offset is larger then file " + "size.\n"); + fclose(fp); + return -1; + } + + // Read PE File Header + if (fseek(fp, (long)e_lfanew, SEEK_SET) != 0) { + printError("pefileGetOverlayOffset failed to seek to e_lfanew.\n"); + printError("pefileGetOverlayOffset errno: %s\n", strerror(errno)); + fclose(fp); + return -1; + } + PeFileHeader peFileHeader; + if (fread(&peFileHeader, sizeof(PeFileHeader), 1, fp) != 1) { + printError("pefileGetOverlayOffset failed to read PE File Header\n"); + fclose(fp); + return -1; + } + if ((peFileHeader.signature << 16) != 0x45500000) { + printError("pefileGetOverlayOffset this is not a PE file for sure (2).\n"); + fclose(fp); + return -1; + } + + // Skip optional header + if (peFileHeader.optionalHeaderSize > 0) { + if (fseek(fp, peFileHeader.optionalHeaderSize, SEEK_CUR) != 0) { + printError("pefileGetOverlayOffset failed to skip over the optional " + "header.\n"); + printError("pefileGetOverlayOffset errno: %s\n", strerror(errno)); + fclose(fp); + return -1; + } + } + + // Read sections + overlayOffset = 0l; + for (uint32_t i=0; i< peFileHeader.numberOfSections; i++) { + PeImageSectionHeader sectionHeader; + if (fread(§ionHeader, sizeof(PeImageSectionHeader), 1, fp) != 1) { + printError("pefileGetOverlayOffset failed to read section header.\n"); + fclose(fp); + return -1; + } + + if ((sectionHeader.rawDataLocation + sectionHeader.rawDataSize) > overlayOffset) { + overlayOffset = sectionHeader.rawDataLocation + sectionHeader.rawDataSize; + } + } + + fclose(fp); + + if (overlayOffset > fileSize) { + printError("pefileGetOverlayOffset no overlay offset larger then file.\n"); + return -1; + } + + return overlayOffset; +} + diff --git a/src/pefile.h b/src/pefile.h new file mode 100644 index 0000000..f22657f --- /dev/null +++ b/src/pefile.h @@ -0,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 . + */ + +#ifndef H_REWISE_PEFILE +#define H_REWISE_PEFILE + +#include + +// 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 diff --git a/src/print.c b/src/print.c new file mode 100644 index 0000000..906aef7 --- /dev/null +++ b/src/print.c @@ -0,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 . + */ + +#include +#include + +#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); + } +} diff --git a/src/print.h b/src/print.h new file mode 100644 index 0000000..22c615f --- /dev/null +++ b/src/print.h @@ -0,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 . + */ + +#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 diff --git a/src/reader.c b/src/reader.c new file mode 100644 index 0000000..c87f1e0 --- /dev/null +++ b/src/reader.c @@ -0,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 . + */ + +#include + +#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; +} diff --git a/src/reader.h b/src/reader.h new file mode 100644 index 0000000..f334672 --- /dev/null +++ b/src/reader.h @@ -0,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 . + */ + +#ifndef H_REWISE_READER +#define H_REWISE_READER + +#include +#include // PATH_MAX +#include +#include // access, F_OK +#include // uint32_t +#include // 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 diff --git a/src/rewise.c b/src/rewise.c new file mode 100644 index 0000000..3e1e63c --- /dev/null +++ b/src/rewise.c @@ -0,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 . + */ + +#include +#include +#include +#include // PATH_MAX, NAME_MAX +#include +#include +#include +#include // dirname +#include +#include // mkdir +#include +#include + +#include "print.h" +#include "reader.h" +#include "inflate.h" +#include "pefile.h" +#include "errors.h" +#include "wiseoverlay.h" +#include "wisescript.h" +#include "version.h" + + +#ifndef REWISE_DEFAULT_TMP_PATH +#define REWISE_DEFAULT_TMP_PATH "/tmp/" +#endif + + +#define SIZE_KiB 1024 +#define SIZE_MiB 1048576 // 1024^2 +#define SIZE_GiB 1073741824 // 1024^3 + + +enum Operation { + OP_NONE = 0, + OP_EXTRACT = 1, + OP_EXTRACT_RAW = 2, + OP_LIST = 3, + OP_VERIFY = 4, + OP_HELP = 5, + OP_SCRIPT_DEBUG = 6 +}; + + +void printPrettySize(size_t size) { + if (size > SIZE_GiB) { + printf("%.2f GiB", (float)size / SIZE_GiB); + } + else + if (size > SIZE_MiB) { + printf("%.2f MiB", (float)size / SIZE_MiB); + } + else + if (size > SIZE_KiB) { + printf("%.2f KiB", (float)size / SIZE_KiB); + } + else { + printf("%zu bytes", size); + } +} + + +unsigned long getFreeDiskSpace(char * path) { + struct statvfs fsStats; + if (statvfs((const char *)path, &fsStats) != 0) { + printError("Failed to determine free disk space for '%s'. Errno: %s\n", + strerror(errno)); + return 0; + } + return fsStats.f_bsize * fsStats.f_bavail; +} + + +void convertMsDosTime(struct tm * destTime, uint16_t date, uint16_t time) { + destTime->tm_year = (int)((date >> 9) + 80); + destTime->tm_mon = (int)((date >> 5) & 0b0000000000001111); + destTime->tm_mday = (int)(date & 0b0000000000011111); + destTime->tm_hour = (int)(time >> 11); + destTime->tm_min = (int)((time >> 5) & 0b0000000000111111); + destTime->tm_sec = (int)(time & 0b0000000000011111) * 2; +} + +static InflateObject * InflateObjPtr; +static long ScriptDeflateOffset; + +#define MAX_OUTPUT_PATH (PATH_MAX - WIN_PATH_MAX) - 2 +static char OutputPath[MAX_OUTPUT_PATH]; // should be absolute and end with a '/' +static char TempPath[MAX_OUTPUT_PATH] = REWISE_DEFAULT_TMP_PATH; +static char PreserveTmp = 0; +static char NoExtract = 0; + + +void printHelp(void) { + printf("==============================================================\n"); + printf(" Welcome to REWise version %s\n", REWISE_VERSION_STR); + printf("==============================================================\n\n"); + printf(" Usage: rewise [OPERATION] [OPTIONS] INPUT_FILE\n\n"); + printf(" OPERATIONS\n"); + printf(" -x --extract OUTPUT_PATH Extract files.\n"); + printf(" -r --raw OUTPUT_PATH Extract all files in the overlay " + "data. This does not move/rename " + "files!\n"); + printf(" -l --list List files.\n"); + printf(" -V --verify Run extract without actually " + "outputting files, crc32s will be " + "checked.\n"); + printf(" -z --script-debug Print parsed WiseScript.bin\n"); + printf(" -v --version Print version and exit.\n"); + printf(" -h --help Display this HELP.\n"); + printf("\n"); + printf(" OPTIONS\n"); + printf(" -p --preserve Don't delete TMP files.\n"); + printf(" -t --tmp-path TMP_PATH Set temporary path, default: /tmp/\n"); + printf(" -d --debug Print debug info.\n"); + printf(" -s --silent Be silent, don't print anything.\n"); + printf(" -n --no-extract Don't extract anything. This will " + "be ignored with -x or -r. It also " + "will not try to remove TMP files, " + "so -p won't do anything.\n"); + printf("\n"); + printf(" NOTES\n"); + printf(" - Path to directory OUTPUT_PATH and TMP_PATH should exist and " + "be writable.\n"); +} + + +void printFile(WiseScriptFileHeader * data) { + struct tm fileDatetime; + convertMsDosTime(&fileDatetime, data->date, data->time); + printf("% 12u %02d-%02d-%04d %02d:%02d:%02d '%s'\n", data->inflatedSize, + fileDatetime.tm_mday, fileDatetime.tm_mon, fileDatetime.tm_year + 1900, + fileDatetime.tm_hour, fileDatetime.tm_min, fileDatetime.tm_sec, + data->destFile); +} + + +/* preparePath() - Joins the two given paths to dest and tries to create the + * directories that don't exist yet. + * param subPath: Rest of the filepath (including file) from WiseScript.bin + * Should not be larger then (WIN_PATH_MAX + 1) + * param dest : A pre-allocated char buffer with size PATH_MAX */ +bool preparePath(char * basePath, char * subPath, char * dest) { + // Join paths + if ((strlen(basePath) + strlen(subPath) + 1) > PATH_MAX) { + printError("Overflow of final path > PATH_MAX\n"); + return false; + } + strcpy(dest, basePath); + strcat(dest, subPath); + + // Try to create directories as needed + char * outputFilePath; + char * currentSubPath; + char * separator; + + // make a copy which strchr may manipulate. + outputFilePath = strdup(dest); + + if (outputFilePath == NULL) { + printError("Errno: %s\n", strerror(errno)); + return false; + } + + // get the path without filename + currentSubPath = dirname(outputFilePath); + + // get the path its root (string until first '/') + separator = strchr(currentSubPath, '/'); + + // This should not happen because the given path by the user should exist. + if (separator == NULL) { + printError("This should not happen, please report if it does! (1)\n"); + return false; + } + + // iterate through all sub-directories from root + while (separator != NULL) { + // terminate the dirName string on next occurance of '/' + separator[0] = 0x00; + + // do not create root + if (currentSubPath[0] != 0x00) { + // stat currentSubPath + if (access(currentSubPath, F_OK) != 0) { + // currentSubPath exists but is not a directory + if (errno == ENOTDIR) { + printError("Extract subpath '%s' exists but is not a directory!\n", + currentSubPath); + free(outputFilePath); + return false; + } + + // currentSubPath does not exist, try to create a new directory + if (errno == ENOENT) { + errno = 0; + if (mkdir(currentSubPath, 0777) != 0) { + printError("Failed to create subpath (1): '%s'\n", currentSubPath); + printError("Errno: %s\n", strerror(errno)); + free(outputFilePath); + return false; + } + } + } + } + + // reset the previous set terminator + separator[0] = '/'; + + // set separator to next occurrence of '/' (will be set to NULL when + // there are no more occurrences of '/'. + separator = strchr(separator + 1, '/'); + } + + // last subdir + if (access(currentSubPath, F_OK) != 0) { + // currentSubPath exists but is not a directory + if (errno == ENOTDIR) { + printError("Extract path '%s' exists but is not a directory!\n", + currentSubPath); + free(outputFilePath); + return false; + } + + // currentSubPath does not exist, try to create a new directory + if (errno == ENOENT) { + if (mkdir(currentSubPath, 0777) != 0) { + printError("Failed to create subpath (2): '%s'\n", currentSubPath); + printError("Errno: %s\n", strerror(errno)); + free(outputFilePath); + return false; + } + } + } + + // cleanup + free(outputFilePath); + + return true; +} + + +void extractFile(WiseScriptFileHeader * data) { + bool result; + char outputFilePath[PATH_MAX]; + + // Create the final absolute filepath and make sure the path exists (will be + // created when it doesn't exist). + if (preparePath(OutputPath, data->destFile, outputFilePath) == false) { + printError("preparePath failed.\n"); + stopWiseScriptParse(); + return; + } + + // Seek to deflated file start + if (fseek(InflateObjPtr->inputFile, ((long)data->deflateStart) + ScriptDeflateOffset, SEEK_SET) != 0) { + printError("Failed seek to file offset 0x%08X\n", data->deflateStart); + printError("Errno: %s\n", strerror(errno)); + stopWiseScriptParse(); + return; + } + + // Inflate/extract the file + result = inflateExtractNextFile(InflateObjPtr, outputFilePath); + if (result == false) { + printError("Failed to extract '%s'\n", outputFilePath); + stopWiseScriptParse(); + return; + } + + // Set file access/modification datetime + struct tm fileCreation; + time_t creationSeconds; + convertMsDosTime(&fileCreation, data->date, data->time); + creationSeconds = mktime(&fileCreation); + const struct utimbuf times = { + .actime = creationSeconds, + .modtime = creationSeconds + }; + if (utime(outputFilePath, ×) != 0) { + printWarning("Failed to set access and modification datetime for file " + "'%s'\n", outputFilePath); + } + + printInfo("Extracted %s\n", data->destFile); +} + + +void noExtractFile(WiseScriptFileHeader * data) { + // Inflate/extract the file + bool result = inflateExtractNextFile(InflateObjPtr, NULL); + if (result == false) { + printError("Failed to no-extract '%s'\n", data->destFile); + stopWiseScriptParse(); + return; + } + printInfo("CRC32 success for '%s'\n", data->destFile); +} + + +bool setPath(const char * optarg, char * dest) { + // Resolve absolute path + char * outputPath = realpath(optarg, dest); + if (outputPath == NULL) { + printError("Invalid PATH given, could not resolve absolute path for " + "'%s'. Errno: %s\n", optarg, strerror(errno)); + return false; + } + + size_t outputPathLen = strlen(outputPath); + // -2 for the potential '/' we may add + if (outputPathLen >= (MAX_OUTPUT_PATH - 1)) { + printError("Absolute path of PATH is to large.\n"); + return false; + } + + // Make sure the path ends with a '/' + if (dest[outputPathLen - 1] != '/') { + strcat(dest, "/"); + } + + // Make sure the path exists + if (access(dest, F_OK) != 0) { + // dest exists but is not a directory + if (errno == ENOTDIR) { + printError("'%s' is not a directory.\n", dest); + return false; + } + // NOTE: realpath would have failed when the directory does not exist. + // dest does not exist + /*if (errno == ENOENT) { + printError("'%s' does not exist.\n", dest); + return false; + }*/ + } + + return true; +} + + +int main(int argc, char *argv[]) { + char inputFile[PATH_MAX]; + long overlayOffset; + FILE * fp; + REWError status; + enum Operation operation = OP_NONE; + inputFile[0] = 0x00; + + // https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Options.html + // https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html + struct option long_options[] = { + // OPERATIONS + {"extract" , required_argument, NULL, 'x'}, + {"raw" , required_argument, NULL, 'r'}, + {"list" , no_argument , NULL, 'l'}, + {"verify" , no_argument , NULL, 'V'}, + {"script-debug", no_argument , NULL, 'z'}, + {"version" , no_argument , NULL, 'v'}, + {"help" , no_argument , NULL, 'h'}, + // OPTIONS + {"temp" , required_argument, NULL, 't'}, + {"debug" , no_argument , NULL, 'd'}, + {"preserve" , no_argument , NULL, 'p'}, + {"silent" , no_argument , NULL, 's'}, + {"no-extract" , no_argument , NULL, 'n'}, + {NULL , 0 , NULL, 0} + }; + + int option_index = 0; + for (;;) { + int opt = getopt_long(argc, argv, "x:r:t:lhdspznVv", + long_options, &option_index); + + if (opt == -1) { + break; + } + + switch (opt) { + // OPERATIONS + case 'x': + { + if (operation != OP_NONE) { + printError("More then one operation is set! Do set only one.\n"); + return 1; + } + operation = OP_EXTRACT; + if (setPath(optarg, OutputPath) == false) { + return 1; + } + } + break; + + case 'r': + if (operation != OP_NONE) { + printError("More then one operation is set! Do set only one.\n"); + return 1; + } + operation = OP_EXTRACT_RAW; + if (setPath(optarg, OutputPath) == false) { + return 1; + } + break; + + case 'l': + if (operation != OP_NONE) { + printError("More then one operation is set! Do set only one.\n"); + return 1; + } + operation = OP_LIST; + break; + + case 'V': + if (operation != OP_NONE) { + printError("More then one operation is set! Do set only one.\n"); + return 1; + } + operation = OP_VERIFY; + break; + + case 'z': + if (operation != OP_NONE) { + printError("More then one operation is set! Do set only one.\n"); + return 1; + } + operation = OP_SCRIPT_DEBUG; + break; + + case 'v': + printf("REWise v%s\n", REWISE_VERSION_STR); + return 0; + + case 'h': + printHelp(); + return 0; + + // OPTIONS + case 'd': + setPrintFlag(PRINT_DEBUG); + break; + + case 's': + setPrintFlags(PRINT_SILENT); + break; + + case 't': + if (setPath(optarg, TempPath) == false) { + printError("Invalid TMP_PATH given.\n"); + return 1; + } + break; + + case 'p': + PreserveTmp = 1; + break; + + case 'n': + NoExtract = 1; + break; + + case '?': + // invalid option + printError("Invalid operation or option\n"); + return 1; + + default: + printError("default\n"); + break; + } + } + + if ((argc - 1 ) < optind) { + printError("Please supply a input file\n"); + return 1; + } + if ((argc - 1 ) > optind) { + printError("Please supply only one input file\n"); + return 1; + } + + if (strlen(argv[optind]) > (PATH_MAX - 1)) { + printError("What are you trying to do? INPUT_FILE is larger then PATH_MAX\n"); + return 1; + } + strcpy(inputFile, argv[optind]); + + if (operation == OP_NONE) { + printError("Please specify a operation.\n"); + return 1; + } + + /* Check if input file exists */ + if (access(inputFile, F_OK) != 0) { + printError("InputFile '%s' not found. Errno: %s\n", inputFile, + strerror(errno)); + return 1; + } + + // Get offset to overlay data + overlayOffset = pefileGetOverlayOffset(inputFile); + if (overlayOffset == -1) { + printError("Failed to find overlay offset.\n", inputFile); + return 1; + } + + printDebug("InputFile: %s\n", inputFile); + printDebug("OverlayOffset: %ld\n", overlayOffset); + + /* Open inputFile */ + fp = fopen(inputFile, "rb"); + + if (fp == NULL) { + printError("Failed to open inputFile '%s'\n", inputFile); + printError("Errno: %s\n", strerror(errno)); + return 1; + }; + + // Seek to overlayData + if (fseek(fp, overlayOffset, SEEK_SET) != 0) { + printError("Failed to seek to overlayData. Offset: 0x%08X\n", overlayOffset); + printError("Errno: %s\n", strerror(errno)); + fclose(fp); + return 1; + } + + // Read Wise overlay header + WiseOverlayHeader overlayHeader; + if ((status = readWiseOverlayHeader(fp, &overlayHeader)) != REWERR_OK) { + printError("Failed to read WiseOverlayHeader.\n"); + fclose(fp); + return 1; + } + freeWiseOverlayHeader(&overlayHeader); + + // Here we arrived at the delated data, each entry followed by a CRC32 + // https://en.wikipedia.org/wiki/DEFLATE + if (huffmanInitFixedTrees() == false) { + printError("Failed to huffmanInitFixedTrees, out of mem?\n"); + fclose(fp); + return 1; + } + + // Initial check on free disk space (TMP_PATH) + unsigned long tmpFreeDiskSpace = getFreeDiskSpace(TempPath); + if (tmpFreeDiskSpace == 0) { // failed to determine free disk space + fclose(fp); + return 1; + } + // make sure at-least 1 MiB is available at the TMP path + if (tmpFreeDiskSpace < SIZE_MiB) { + printError("At-least 1 MiB of free space is required in the TMP_PATH.\n"); + fclose(fp); + return 1; + } + printInfo("Free tmp disk space: "); + printPrettySize(tmpFreeDiskSpace); + printf("\n"); + + bool result; + InflateObject inflateObj; + inflateInit(&inflateObj, fp); + InflateObjPtr = &inflateObj; + + // Raw extract + if (operation == OP_EXTRACT_RAW) { + uint32_t extractCount = 0; + char extractFilePath[PATH_MAX]; + + // Start inflating and outputting files + while (ftell(fp) < inflateObj.inputFileSize) { + char fileName[21]; + if (snprintf(fileName, 20, "EXTRACTED_%09u", extractCount) > 20) { + // truncated + printError("Failed to format filename, it truncated.\n"); + fclose(fp); + huffmanFreeFixedTrees(); + return 1; + } + if (preparePath(OutputPath, fileName, extractFilePath) == false) { + printError("Failed to create directories for '%s'.\n", fileName); + fclose(fp); + huffmanFreeFixedTrees(); + return 1; + } + + result = inflateExtractNextFile(&inflateObj, (const char *)extractFilePath); + if (result == false) { + printError("Failed to extract '%s'.\n", extractFilePath); + fclose(fp); + huffmanFreeFixedTrees(); + return 1; + } + + printInfo("Extracted '%s'\n", extractFilePath); + extractCount++; + } + + printInfo("Extracted %d files.\n", extractCount); + } + + else { + char tmpFileScript[PATH_MAX]; + + // Skip WiseColors.dib + if (NoExtract == 0) { + result = inflateExtractNextFile(&inflateObj, NULL); + if (result == false) { + printError("Failed to extract 'WiseColors.dib'.\n"); + fclose(fp); + huffmanFreeFixedTrees(); + return 1; + } + } + + // Create filepath for WiseScript.bin + if (preparePath(TempPath, "WiseScript.bin", tmpFileScript) == false) { + fclose(fp); + huffmanFreeFixedTrees(); + printf("Failed to create filepath for WiseScript.bin.\n"); + return 1; + } + // Extract WiseScript.bin + if (NoExtract == 0) { + result = inflateExtractNextFile(&inflateObj, tmpFileScript); + if (result == false) { + printError("Failed to extract '%s'.\n", tmpFileScript); + fclose(fp); + huffmanFreeFixedTrees(); + return 1; + } + } + + // Determine the inflate data offset inside WiseScript.bin (this needs to + // be added to the inflateStart we got for files from WiseScript to get to + // the real inflateStart offset in the PE file.) + WiseScriptParsedInfo * parsedInfo = wiseScriptGetParsedInfo(tmpFileScript); + ScriptDeflateOffset = inflateObj.inputFileSize - parsedInfo->inflateStartOffset; + printDebug("scriptDeflateOffset: %ld (0x%08X).\n", parsedInfo->inflateStartOffset); + + WiseScriptCallbacks callbacks; + initWiseScriptCallbacks(&callbacks); + + // LIST + if (operation == OP_LIST) { + callbacks.cb_0x00 = &printFile; + printf(" FILESIZE FILEDATE FILETIME FILEPATH\n"); + printf("------------ ---------- -------- ----------------------------\n"); + status = parseWiseScript(tmpFileScript, &callbacks); + if (status != REWERR_OK) { + printError("Parsing WiseScript failed.\n"); + } + printf("------------ ---------- -------- ----------------------------\n"); + printf("Total size: "); + printPrettySize(parsedInfo->inflatedSize0x00); + printf(" (%zu bytes)\n", parsedInfo->inflatedSize0x00); + } + // EXTRACT + else + if (operation == OP_EXTRACT) { + // Check if there is enough free disk space + unsigned long outputFreeDiskSpace = getFreeDiskSpace(OutputPath); + if (outputFreeDiskSpace == 0) { // failed to determine free disk space + fclose(fp); + return 1; + } + if (outputFreeDiskSpace <= parsedInfo->inflatedSize0x00) { + printError("Not enough free disk space at '%s'. Required: %ld Left: " + "%ld\n", OutputPath, parsedInfo->inflatedSize0x00, + outputFreeDiskSpace); + fclose(fp); + return 1; + } + + // Start inflating and outputting files + callbacks.cb_0x00 = &extractFile; + status = parseWiseScript(tmpFileScript, &callbacks); + + // Something went wrong + if (status != REWERR_OK) { + printError("Parsing WiseScript failed.\n"); + } + } + // SCRIPT_DEBUG + else + if (operation == OP_SCRIPT_DEBUG) { + status = wiseScriptDebugPrint(tmpFileScript); + if (status != REWERR_OK) { + printError("Debug print WiseScript failed.\n"); + } + } + else + if (operation == OP_VERIFY) { + callbacks.cb_0x00 = &noExtractFile; + status = parseWiseScript(tmpFileScript, &callbacks); + if (status != REWERR_OK) { + printError("Parsing WiseScript failed.\n"); + } + printInfo("All looks good!\n"); + } + + // remove tmp files + if (PreserveTmp == 0 && NoExtract == 0) { + if (remove(tmpFileScript) != 0) { + printError("Failed to remove '%s'. Errno: %s\n", tmpFileScript, + strerror(errno)); + } + } + } + + // Cleanup + huffmanFreeFixedTrees(); + fclose(fp); + + return status; +} diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..7c26e7b --- /dev/null +++ b/src/version.h @@ -0,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 . + */ + +#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 diff --git a/src/wiseoverlay.c b/src/wiseoverlay.c new file mode 100644 index 0000000..91df5be --- /dev/null +++ b/src/wiseoverlay.c @@ -0,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 . + */ + +#include +#include + +#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; + } +} diff --git a/src/wiseoverlay.h b/src/wiseoverlay.h new file mode 100644 index 0000000..863e2c5 --- /dev/null +++ b/src/wiseoverlay.h @@ -0,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 . + */ + +#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 diff --git a/src/wisescript.c b/src/wisescript.c new file mode 100644 index 0000000..9a3b6f2 --- /dev/null +++ b/src/wisescript.c @@ -0,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 . + */ + +#include +#include +#include +#include + + +#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; +} diff --git a/src/wisescript.h b/src/wisescript.h new file mode 100644 index 0000000..ef4b2cd --- /dev/null +++ b/src/wisescript.h @@ -0,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 . + */ + +/* +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 +#include + + +#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