From 4e132306439f0af69870d758f7f5621c512f9810 Mon Sep 17 00:00:00 2001 From: CrabJounrnal <59743349+CrabJounrnal@users.noreply.github.com> Date: Fri, 9 Apr 2021 20:26:22 +0700 Subject: [PATCH] Add files via upload --- LICENSE | 674 ++++++++++++++++ NFSUExtraOptions+/CodeCaves.h | 298 +++++++ NFSUExtraOptions+/ExtraOptionsStuff.h | 346 ++++++++ NFSUExtraOptions+/GlobalVariables.h | 32 + NFSUExtraOptions+/HotkeyStuff.h | 155 ++++ NFSUExtraOptions+/InGameFunctions.h | 50 ++ NFSUExtraOptions+/NFSUExtraOptions+.sln | 22 + NFSUExtraOptions+/NFSUExtraOptions.aps | Bin 0 -> 2432 bytes NFSUExtraOptions+/NFSUExtraOptions.rc | Bin 0 -> 4722 bytes NFSUExtraOptions+/NFSUExtraOptions.vcxproj | 125 +++ .../NFSUExtraOptions.vcxproj.user | 4 + NFSUExtraOptions+/PreRaceStatsScreen.h | 137 ++++ NFSUExtraOptions+/QROptionsScreen.h | 45 ++ NFSUExtraOptions+/UserCalls.h | 137 ++++ NFSUExtraOptions+/dllmain.cpp | 26 + NFSUExtraOptions+/resource.h | 14 + NFSUExtraOptions+/stdafx.cpp | 1 + NFSUExtraOptions+/stdafx.h | 16 + NFSUExtraOptions+/targetver.h | 8 + NFSUExtraOptions+Settings.ini | 48 ++ README.md | 5 + includes/CPatch.h | 145 ++++ includes/IniReader.h | 311 ++++++++ includes/ini_parser.hpp | 327 ++++++++ includes/injector/assembly.hpp | 178 +++++ includes/injector/calling.hpp | 127 +++ includes/injector/gvm/gvm.hpp | 229 ++++++ includes/injector/gvm/translator.hpp | 203 +++++ includes/injector/hooking.hpp | 687 ++++++++++++++++ includes/injector/injector.hpp | 749 ++++++++++++++++++ includes/injector/utility.hpp | 56 ++ includes/stdafx.h | 16 + 32 files changed, 5171 insertions(+) create mode 100644 LICENSE create mode 100644 NFSUExtraOptions+/CodeCaves.h create mode 100644 NFSUExtraOptions+/ExtraOptionsStuff.h create mode 100644 NFSUExtraOptions+/GlobalVariables.h create mode 100644 NFSUExtraOptions+/HotkeyStuff.h create mode 100644 NFSUExtraOptions+/InGameFunctions.h create mode 100644 NFSUExtraOptions+/NFSUExtraOptions+.sln create mode 100644 NFSUExtraOptions+/NFSUExtraOptions.aps create mode 100644 NFSUExtraOptions+/NFSUExtraOptions.rc create mode 100644 NFSUExtraOptions+/NFSUExtraOptions.vcxproj create mode 100644 NFSUExtraOptions+/NFSUExtraOptions.vcxproj.user create mode 100644 NFSUExtraOptions+/PreRaceStatsScreen.h create mode 100644 NFSUExtraOptions+/QROptionsScreen.h create mode 100644 NFSUExtraOptions+/UserCalls.h create mode 100644 NFSUExtraOptions+/dllmain.cpp create mode 100644 NFSUExtraOptions+/resource.h create mode 100644 NFSUExtraOptions+/stdafx.cpp create mode 100644 NFSUExtraOptions+/stdafx.h create mode 100644 NFSUExtraOptions+/targetver.h create mode 100644 NFSUExtraOptions+Settings.ini create mode 100644 README.md create mode 100644 includes/CPatch.h create mode 100644 includes/IniReader.h create mode 100644 includes/ini_parser.hpp create mode 100644 includes/injector/assembly.hpp create mode 100644 includes/injector/calling.hpp create mode 100644 includes/injector/gvm/gvm.hpp create mode 100644 includes/injector/gvm/translator.hpp create mode 100644 includes/injector/hooking.hpp create mode 100644 includes/injector/injector.hpp create mode 100644 includes/injector/utility.hpp create mode 100644 includes/stdafx.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9cecc1d --- /dev/null +++ b/LICENSE @@ -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. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +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: + + {project} Copyright (C) {year} {fullname} + 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/NFSUExtraOptions+/CodeCaves.h b/NFSUExtraOptions+/CodeCaves.h new file mode 100644 index 0000000..194d8d9 --- /dev/null +++ b/NFSUExtraOptions+/CodeCaves.h @@ -0,0 +1,298 @@ +#include "stdio.h" +#include +#include "..\includes\injector\injector.hpp" +#include "..\includes\IniReader.h" + +char* TakeoverString = "NFSU Extra Options+ - © 2021 ExOpts Team & Crab Gazette."; +bool ShouldShowLapPopup; +void* vector3playerspeed = nullptr; + +// 0x4DF385 +void __declspec(naked) SplashScreenCodeCave() +{ + _asm + { + call FEngSetVisible // edi = package, edx = object + mov eax, [ebp + 0xC] // package name + mov edx, 0x6C3A3C // "LicenseBlurb" + push TakeoverString + call FEngPrintf // eax = package, edx = object, push fmt + add esp, 4 + push 0x4DF38A + retn + } +} + +void __declspec(naked) StartingCashCodeCave() +{ + _asm + { + push ebx + mov ebx, StartingCash + mov dword ptr ds : [esi + 0x1374] , ebx + pop ebx + push 0x5A0A45 + retn + } +} + +void __declspec(naked) TournamentCodeCave() +{ + _asm + { + call QRModeSelectScreen_MaybeMakeGameModeAvailable + + newcode : + mov ebx, [esp + 0x38] + mov al, byte ptr ds : [0x7589FE] + test al, al + jz caveexit + cmp dword ptr ds : [0x777B4C] , 04 + jz caveexit + + mov edx, dword ptr ds : [esp + 0x0C] + inc edx + push edx + push 0x6C5F64 //"Mode_%d" + push 0x20 + lea esi, [esp + 0x20] + mov dword ptr ds : [esp + 0x18] , edx + call FEngSNPrintf + mov esi, dword ptr ds : [ebx + 0x0C] + add esp, 0x0C + lea edx, [esp + 0x14] + call FEHashUpper + test esi, esi + mov edi, eax + je loc_4B7988 + mov eax, esi + call FEngFindPackage + test eax, eax + je loc_4B7988 + mov ecx, edi + mov edx, eax + call FEPackage_FindObjectByHash + test eax, eax + jne loc_4B79A1 + + loc_4B7988 : + mov ebx, dword ptr ds : [0x73578C] + test ebx, ebx + je loc_4B79B7 + mov eax, [ebx + 8] + push edi + mov ecx, esi + call FEngine_FindObject + test eax, eax + je loc_4B79B1 + + loc_4B79A1 : + cmp dword ptr ds : [eax + 0x18] , 2 + jne loc_4B79B1 + mov ebx, dword ptr ds : [0x73578C] + mov esi, eax + jmp loc_4B79B9 + + loc_4B79B1 : + mov ebx, dword ptr ds : [0x73578C] + + loc_4B79B7 : + xor esi, esi + + loc_4B79B9 : + push esi + call FEngSetVisible_obj + add esp, 4 + test ebx, ebx + je loc_4B79E0 + push 1 + lea edx, [esp + 0x18] + call FEHashUpper + mov edi, eax + mov eax, [esp + 0x3C] + mov eax, [eax + 0x0C] + push ebx + call cFEng_SetButtonState + + loc_4B79E0 : + mov edx, 0x6C5F04 //"GENERICPLACEHOLDER" + or ecx, -1 + mov al, 0x47 + lea ebx, [ebx + 0] + + loc_4B79F0 : + imul ecx, ecx, 0x21 + movzx eax, al + add ecx, eax + mov al, [edx + 01] + inc edx + test al, al + jne loc_4B79F0 + push ecx + push 0xCDA558A7 // "TOURNAMENT" + push 0x80E29CC5 // desc + mov edx, 0x6C5EDC + call FEHashUpper + mov ecx, [esp + 0x44] + push eax + push 0 + push ecx + mov ebx, 4 + call QRModeSelectScreen_MaybeMakeGameModeAvailable + + + caveexit : + push 0x4B7927 + retn + } +} + +// 0x4BB3C4 +void __declspec(naked) LapPopupCodeCave_QRTrackSelectScreen_NotificationMessage() +{ + _asm + { + mov al, ShouldShowLapPopup + test al,al + jz originalcode + + showlappopup: + mov eax,[ebp+0x0C] + push 255 + mov edi,0x6C210C // "MU_QuickRaceLapPopup_PC.fng" + call FEngQueuePackagePush + add esp,04 + mov ShouldShowLapPopup,0 + jmp caveexit + + originalcode: + mov eax, [ebp + 0x0C] + push 255 + mov edi, 0x6C2588 // "MU_QuickRaceOptions_PC.fng" + call FEngQueuePackagePush + add esp, 04 + + caveexit: + push 0x4BB3D9 + retn + } +} + +//0x4BB41A +void __declspec(naked) LapPopupCodeCave_QRTrackSelectScreen_NotificationMessage_2() +{ + _asm + { + mov ecx, dword ptr ds : [0x777CC8] // CurrentRaceMode + cmp ecx, 0 // Circuit + je ShowLapPopup + cmp ecx, 2 // Lap KO + je ShowLapPopup + cmp ecx, 4 // Tournament + je ShowLapPopup + cmp ecx, 6 // Drift + je ShowLapPopup + + DontShowLapPopup : + mov ShouldShowLapPopup, 0 + jmp caveexit + + ShowLapPopup : + mov ShouldShowLapPopup, 1 + + caveexit : + push 0x4BB420 + retn + } +} + +//0x4B7B88 +void __declspec(naked) LapPopupCodeCave_QRModeSelectScreen_NotificationMessage() +{ + _asm + { + mov dword ptr ds:[0x777CC8], edx // CurrentRaceMode + cmp edx, 0 // Circuit + je ShowLapPopup + cmp edx, 2 // Lap KO + je ShowLapPopup + cmp edx, 4 // Tournament + je ShowLapPopup + cmp edx, 6 // Drift + je ShowLapPopup + + DontShowLapPopup: + mov ShouldShowLapPopup, 0 + jmp caveexit + + ShowLapPopup: + mov ShouldShowLapPopup, 1 + + caveexit: + push 0x4B7B8E + retn + } +} + +//0x4BD748 +void __declspec(naked) LapPopupCodeCave_QRLapPopupScreen_NotificationMessage() +{ + _asm + { + mov eax, [esi + 0x0C] + push 255 + mov edi, 0x6C2588 // "MU_QuickRaceOptions_PC.fng" + call FEngQueuePackagePush + add esp, 4 + pop edi + pop esi + retn 0x10 + } +} + +//0x4BD740 +void __declspec(naked) LapPopupCodeCave_QRLapPopupScreen_NotificationMessage_2() +{ + _asm + { + mov ShouldShowLapPopup, 1 + add esp, 4 + pop edi + pop esi + retn 0x10 + } +} + +void __declspec(naked) SkipMovieCodeCave() +{ + _asm + { + mov eax, [_SkipMovies] + test eax, eax + jz loc_5A4A5D + push 0xC3960EB9 + mov edx, _gFEPackageManager + call FEPackageManager_BroadcastMessage + retn + + loc_5A4A5D: + push 0x5A4A5D + retn + } +} + +// not a cave but... +void __declspec(naked) HookSpeed() +{ + _asm + { + // check ret address + // didn't found better way yet + cmp dword ptr[esp + 0x2F0], 0x42036E + jne skip + mov [vector3playerspeed], ecx + skip : + push 0x585810 + ret + } +} \ No newline at end of file diff --git a/NFSUExtraOptions+/ExtraOptionsStuff.h b/NFSUExtraOptions+/ExtraOptionsStuff.h new file mode 100644 index 0000000..9f986c8 --- /dev/null +++ b/NFSUExtraOptions+/ExtraOptionsStuff.h @@ -0,0 +1,346 @@ +#include "stdio.h" +#include +#include "..\includes\injector\injector.hpp" +#include "..\includes\IniReader.h" + +#include "GlobalVariables.h" +#include "InGameFunctions.h" +#include "UserCalls.h" + +int hotkeyUnlockAllThings, hotkeyAnyTrackInAnyMode, hotkeyFreezeCamera, hotkeyAutoDrive, hotkeyNoGravity, hotkeyJump; +int GameRegion, EATraxSkin, WindowedMode, WindowedModeX, WindowedModeY; +int StartingCash, StartingStylePoints, MinLaps, MaxLaps, MinRacers, MaxRacers, DragInAnyMode; + +bool ShowMoreRaceOptions, ShowLanguageSelectScreen, AnyTrackInAnyMode, MorePaintTypes, ShowSpecialVinyls, ExOptsTeamTakeOver, mpsInsteadOfMiPH; +bool EnableHiddenCameras, UnlockAllThings, ShowTournament, GarageZoom, GarageRotate, ShowDebugOptionsMenu, SkipMovies, EnableSound, EnableMusic; +bool AllowMultipleInstances, once1, once2, WheelFix, DamageFix, EnablePreRaceStats, NoGravity, NoOutOfMapReset, DriftPhysicsInAnyMode; + +float SplashScreenTimeLimit, GameSpeed, MiniWorldCarSpeedCruising, MiniWorldCarSpeedFast, MiniWorldCarSpeedSlow, JumpSpeed, DragCrashSensitivity; + +#include "QROptionsScreen.h" +#include "PreRaceStatsScreen.h" + +#include "CodeCaves.h" +#include "HotkeyStuff.h" + +void ReadConfig() +{ + CIniReader iniReader("NFSUExtraOptions+Settings.ini"); + + // Hotkeys + hotkeyJump = iniReader.ReadInteger("Hotkeys", "Jump", 49); // 1 + hotkeyNoGravity = iniReader.ReadInteger("Hotkeys", "NoGravity", 118); // F7 + hotkeyAnyTrackInAnyMode = iniReader.ReadInteger("Hotkeys", "AnyTrackInAnyMode", 36); // HOME + hotkeyFreezeCamera = iniReader.ReadInteger("Hotkeys", "FreezeCamera", 19); // Pause/Break + hotkeyUnlockAllThings = iniReader.ReadInteger("Hotkeys", "UnlockAllThings", 116); //F5 + hotkeyAutoDrive = iniReader.ReadInteger("Hotkeys", "AutoDrive", 117); //F6 + + // LapControllers + MinLaps = iniReader.ReadInteger("LapControllers", "Minimum", 0); + MaxLaps = iniReader.ReadInteger("LapControllers", "Maximum", 127); + + // RacerControllers + MinRacers = iniReader.ReadInteger("RacerControllers", "Minimum", 1); + MaxRacers = iniReader.ReadInteger("RacerControllers", "Maximum", 4); + + // Menu + ShowMoreRaceOptions = iniReader.ReadInteger("Menu", "ShowMoreRaceOptions", 1) != 0; + ShowLanguageSelectScreen = iniReader.ReadInteger("Menu", "ShowLanguageSelectScreen", 0) != 0; + ShowSpecialVinyls = iniReader.ReadInteger("Menu", "ShowSpecialVinyls", 1) != 0; + ShowDebugOptionsMenu = iniReader.ReadInteger("Menu", "ShowDebugOptionsMenu", 0) != 0; + ShowTournament = iniReader.ReadInteger("Menu", "ShowTournament", 0) != 0; + EATraxSkin = iniReader.ReadInteger("Menu", "EATraxSkin", -1); + AnyTrackInAnyMode = iniReader.ReadInteger("Menu", "AnyTrackInAnyRaceMode", 0) != 0; + SplashScreenTimeLimit = iniReader.ReadFloat("Menu", "SplashScreenTimeLimit", 30.0f); + MiniWorldCarSpeedFast = iniReader.ReadFloat("Menu", "MiniWorldCarSpeedFast", 50.0f); + MiniWorldCarSpeedCruising = iniReader.ReadFloat("Menu", "MiniWorldCarSpeedCruising", 20.0f); + MiniWorldCarSpeedSlow = iniReader.ReadFloat("Menu", "MiniWorldCarSpeedSlow", 1.0f); + GarageZoom = iniReader.ReadInteger("Menu", "ShowcaseCamInfiniteZoom", 0) != 0; + GarageRotate = iniReader.ReadInteger("Menu", "ShowcaseCamInfiniteRotation", 0) != 0; + ExOptsTeamTakeOver = iniReader.ReadInteger("Menu", "DisableTakeover", 0) == 0; + + // Gameplay + DriftPhysicsInAnyMode = iniReader.ReadInteger("Gameplay", "DriftPhysicsInAnyMode", 0) != 0; + DragCrashSensitivity = iniReader.ReadFloat("Gameplay", "DragCrashSensitivity", 35.0f); + DragInAnyMode = iniReader.ReadInteger("Gameplay", "DragInAnyMode", 0); + JumpSpeed = iniReader.ReadFloat("Gameplay", "JumpSpeed", 20.0f); + NoOutOfMapReset = iniReader.ReadInteger("Gameplay", "NoOutOfMapReset", 0) != 0; + EnableHiddenCameras = iniReader.ReadInteger("Gameplay", "EnableHiddenCameraModes", 0) != 0; + EnablePreRaceStats = iniReader.ReadInteger("Gameplay", "EnablePreRaceStats", 0) != 0; + GameSpeed = iniReader.ReadFloat("Gameplay", "GameSpeed", 1.0f); + GameRegion = iniReader.ReadInteger("Gameplay", "GameRegion", 0); + StartingCash = iniReader.ReadInteger("Gameplay", "StartingCash", 0); + StartingStylePoints = iniReader.ReadInteger("Gameplay", "StartingStylePoints", 0); + UnlockAllThings = iniReader.ReadInteger("Gameplay", "UnlockAllThings", 0) != 0; + + // Fixes + WheelFix = iniReader.ReadInteger("Fixes", "DisappearingWheelsFix", 1) != 0; + DamageFix = iniReader.ReadInteger("Fixes", "DamageFix", 0) != 0; + + // Misc + mpsInsteadOfMiPH = iniReader.ReadInteger("Misc", "mpsInsteadOfMiPH", 0) != 0; + WindowedMode = iniReader.ReadInteger("Misc", "WindowedMode", 0); + SkipMovies = iniReader.ReadInteger("Misc", "SkipMovies", 0) != 0; + EnableSound = iniReader.ReadInteger("Misc", "EnableSound", 1) != 0; + EnableMusic = iniReader.ReadInteger("Misc", "EnableMusic", 1) != 0; + AllowMultipleInstances = iniReader.ReadInteger("Misc", "AllowMultipleInstances", 0) == 1; +} + +void Init() +{ + ReadConfig(); + + // Restrictions + MinLaps %= 128; + MaxLaps %= 128; + MinRacers = (MinRacers - 1) % 4 + 1; + MaxRacers = (MaxRacers - 1) % 4 + 1; + + // Allow Multiple Instances + if (AllowMultipleInstances) + { + injector::WriteMemory(0x4472F1, 0xEB, true); + } + + // Set Lap Controllers + // QROptionsScreen::ScrollLaps + injector::WriteMemory(0x4B85AB, MinLaps, true); // Minimum Lap Controller for Increment + injector::WriteMemory(0x4B8587, MinLaps, true); // Minimum Lap Controller for Decrement + injector::WriteMemory(0x4B85A7, MaxLaps, true); // Maximum Lap Controller for Increment + injector::WriteMemory(0x4B858B, MaxLaps, true); // Maximum Lap Controller for Decrement + // QRLapPopupScreen::ScrollLaps + injector::WriteMemory(0x4BD4B3, MinLaps, true); // Minimum Lap Controller for Decrement + injector::WriteMemory(0x4BD4D9, MinLaps, true); // Minimum Lap Controller for Increment + injector::WriteMemory(0x4BD4BB, MaxLaps, true); // Maximum Lap Controller for Decrement + injector::WriteMemory(0x4BD4D5, MaxLaps, true); // Maximum Lap Controller for Increment + + if (NoOutOfMapReset) + { + injector::MakeNOP(0x42653E, 5, true); + } + + // Enable Drift Camera Mode For All + if (EnableHiddenCameras) + { + injector::WriteMemory(0x42E091, 0xEB, true); // Player::CycleDriveCameraPovType + injector::MakeNOP(0x4BE78B, 2, true); + injector::MakeNOP(0x4BE749, 2, true); + } + + // Enable Pre-Race Stats from PS2 Demo + if (EnablePreRaceStats) + { + //injector::MakeNOP(0x495905, 2, true); // PreRaceStatsScreen::PreRaceStatsScreen + injector::WriteMemory(0x6C7A08, &PreRaceStatsScreen_NotificationMessage, true); // PreRaceStatsScreen::vtable + } + + // Splash Screen Time Limit + injector::WriteMemory(0x6ccac0, SplashScreenTimeLimit, true); + + // Any Track In Any Mode + if (AnyTrackInAnyMode) + { + injector::MakeNOP(0x4BA16C, 2, true); + injector::MakeNOP(0x4BA202, 2, true); + } + + // Show Language Select Screen + if (ShowLanguageSelectScreen) + { + injector::WriteMemory(0x6FA1A4, 0x6C2368, true); // BootFlowNTSC - "LS_LangSelect.fng" + injector::WriteMemory(0x6FA1BC, 0x6C2368, true); // BootFlowPAL - "LS_LangSelect.fng" + injector::MakeJMP(0x4DEEDA, 0x4DEF81, true); // Always show more languages + } + + // Show Tournament in Quick Race Menu + if (ShowTournament) + { + injector::MakeJMP(0x4B7922, TournamentCodeCave, true); // QRModeSelectScreen::BuildModeList + } + + // Fix racer counts (Old ExOpts fix) + //injector::WriteMemory(0x758980, 4, true); // Circuit + //injector::WriteMemory(0x7589A0, 4, true); // Sprint + //injector::WriteMemory(0x7589C0, 4, true); // LapKnockout + //injector::WriteMemory(0x7589E0, 1, true); // FreeRun + //injector::WriteMemory(0x758A04, 4, true); // Tournament + //injector::WriteMemory(0x758A88, 4, true); // Drag + //injector::WriteMemory(0x758AB4, 1, true); // Drift + + // Add Opponents option to Race Options menu + if (ShowMoreRaceOptions) + { + // Show lap popup + injector::MakeJMP(0x4BB3C4, LapPopupCodeCave_QRTrackSelectScreen_NotificationMessage, true); + injector::MakeJMP(0x4BB41A, LapPopupCodeCave_QRTrackSelectScreen_NotificationMessage_2, true); + injector::MakeJMP(0x4B7B88, LapPopupCodeCave_QRModeSelectScreen_NotificationMessage, true); + injector::MakeJMP(0x4BD748, LapPopupCodeCave_QRLapPopupScreen_NotificationMessage, true); // Switch from lap popup to race options when pressed enter + injector::MakeJMP(0x4BD740, LapPopupCodeCave_QRLapPopupScreen_NotificationMessage_2, true); // Reactivate lap popup when pressed back + + // Fix Lap Controller for Lap KO - RaceStarter::SetupLapKO + injector::WriteMemory(0x4B4eb5, 0x10, true); // Read normal lap count instead of KO lap + injector::MakeNOP(0x4B4ebc, 5, true); // Don't fix it to 3 + + // TEMP - Overlap options 4 and 5 for Tournament Menu (to fix invisible 5th option text and data) + injector::WriteMemory(0x4B9108, 4, true); + + // Make Circuit, Sprint, Drag and KO use the same options screen + injector::MakeCALL(0x4B89BE, QROptionsScreen_SetupCircuit, true); // Sprint + injector::MakeCALL(0x4b89c9, QROptionsScreen_SetupCircuit, true); // KO + injector::MakeCALL(0x4B8A17, QROptionsScreen_SetupCircuit, true); // Drag + + // Show Racers Option + injector::WriteMemory(0x4B8EDF, QROptionsScreen_ScrollNumRacers, true); // QROptionsScreen::SetupCircuit + injector::MakeCALL(0x4B8EE3, QROptionsScreen_DrawNumRacers, true); // QROptionsScreen::SetupCircuit + injector::WriteMemory(0x4B90A5, QROptionsScreen_ScrollNumRacers, true); // QROptionsScreen::SetupTournament + injector::MakeCALL(0x4B90A9, QROptionsScreen_DrawNumRacers, true); // QROptionsScreen::SetupTournament + } + + // Show Special Vinyls + if (ShowSpecialVinyls) + { + // Unlock all vinyls + injector::WriteMemory(0x4E27D7, 0, true); + injector::WriteMemory(0x4AAA6D, 0xEB, true); + injector::MakeNOP(0x5A3577, 2, true); + + // No Restrictions + injector::WriteMemory(0x4E4791, 0x3AEB, true); // EB 3A - jmp 0x4E47CD + injector::MakeRangedNOP(0x4E46C9, 0x4E46E0, true); + injector::WriteMemory(0x4E46C9, 0xB8, true); // mov eax,0x00000000 + injector::WriteMemory(0x4E46CA, 0, true); + injector::WriteMemory(0x4E46CE, 0xB9, true); // mov ecx,0x00000000 + injector::WriteMemory(0x4E46CF, 0, true); + injector::WriteMemory(0x4E46D4, 0xBE, true); // mov esi,0x00000000 + injector::WriteMemory(0x4E46D5, 0, true); + injector::WriteMemory(0x4E46DA, 0xBB, true); // mov ebx,0x00000000 + injector::WriteMemory(0x4E46DB, 0, true); + } + + // Copyright text + if (ExOptsTeamTakeOver) + { + injector::MakeJMP(0x4DF385, SplashScreenCodeCave, true); + } + + // Fix Invisible Wheels + if (WheelFix) + { + injector::WriteMemory(0x5696E4, 0x01, true); // CarPartCuller::CullParts + } + + // Damage Fix + if (DamageFix) + { + injector::WriteMemory(0x465DA4, 0xEB, true); // Collision::CheckForWildCollision + } + + // Garage Hacks + if (GarageZoom) + { + injector::WriteMemory(0x48721D, 0xEB, true); + injector::WriteMemory(0x487236, 0xEB, true); + } + if (GarageRotate) + { + injector::WriteMemory(0x4871F2, 0xEB, true); + injector::WriteMemory(0x487209, 0xEB, true); + } + + // Game Speed + injector::WriteMemory(_Tweak_GameSpeed, GameSpeed, true); + + // Debug Menu + if (ShowDebugOptionsMenu) + { + injector::WriteMemory(0x006C56E4, 0x00000000, true); + } + + // EA Trax Skin + if (EATraxSkin != -1) + { + EATraxSkin %= 4; + injector::MakeNOP(0x4AA212, 3, true); // Don't zero it at boot + injector::WriteMemory(_ChyronSkin, EATraxSkin, true); + } + + // Game Region + injector::WriteMemory(_BuildRegion, GameRegion, true); + + // Starting Cash + injector::MakeRangedNOP(0x5A0A3F, 0x5A0A45, true); + injector::MakeJMP(0x5A0A3F, StartingCashCodeCave, true); + + // Starting Style Points + injector::WriteMemory(0x41CF8E, StartingStylePoints, true); + + // Unlock all things (Load preference) + if (UnlockAllThings) + { + injector::WriteMemory(_UnlockEverythingThatCanBeLocked, 1, true); + } + + // Garage Car Speed + injector::WriteMemory(0x6B6B28, MiniWorldCarSpeedFast, true); // While changing cars + injector::WriteMemory(0x6B6B2C, MiniWorldCarSpeedCruising, true); // Normal car speed + injector::WriteMemory(0x6B6B30, MiniWorldCarSpeedSlow, true); // Used on rim tuning and paint + + // Windowed Mode + if (WindowedMode > 0) + { + injector::WriteMemory(_WindowedMode, 1, true); // Vanilla Windowed Mode + } + + // Skip movies + if (SkipMovies) + { + injector::WriteMemory(_SkipMovies, 1, true); // Vanilla Skip Movies + injector::MakeJMP(0x5A4A50, SkipMovieCodeCave, true); // MovieStart (fix blank screen when skipped a movie) + } + + if (!EnableSound) injector::WriteMemory(_IsSoundEnabled, 0, true); // Enable sound + if (!EnableMusic) injector::WriteMemory(_IsAudioStreamingEnabled, 0, true); // Enable music + + // Other Things + injector::MakeCALL(0x4479BE, Thing, true); + + // Set virtual function hook + injector::WriteMemory(0x6B9670 /* .rdata */, HookSpeed, true); + + if (DriftPhysicsInAnyMode) + injector::MakeNOP(0x57F347, 6, true); + + if (DragCrashSensitivity > 50.0) + injector::WriteMemory(0x6B72C0 /* .rdata */, DragCrashSensitivity, true); + injector::WriteMemory(0x6B72C8 /* .rdata */, DragCrashSensitivity, true); + + switch (DragInAnyMode) { + case 2: + // setting laps number + injector::WriteMemory(0x4342CF, 0xEB /*jmp short*/, true); + // control mode + injector::MakeNOP(0x430ADE, 2, true); + // AI control mode + injector::MakeNOP(0x425184, 2, true); + // and do the same that in case 1 + case 1: + *(BYTE*)_bIsDragRace = 1; + // do not call function there + injector::MakeNOP(0x538391, 5, true); + break; + + + case 3: + // blow engine + injector::MakeNOP(0x46CAF2, 6, true); + // crash + injector::MakeNOP(0x465DAE, 6, true); + // drag's end of race + injector::MakeNOP(0x43578C, 6, true); + // drag's post race sign + injector::MakeNOP(0x4A7E51, 6, true); + break; + default: + break; + } +} \ No newline at end of file diff --git a/NFSUExtraOptions+/GlobalVariables.h b/NFSUExtraOptions+/GlobalVariables.h new file mode 100644 index 0000000..725d92a --- /dev/null +++ b/NFSUExtraOptions+/GlobalVariables.h @@ -0,0 +1,32 @@ +#pragma once + +#include "stdio.h" +#include + +// Global variables +#define _TheGameFlowManager 0x77A920 +#define _gFEPackageManager 0x746104 +#define _WindowedMode 0x73637C +#define _hWnd 0x736380 +#define _IsLostFocus 0x7363B6 +#define _ResX 0x701034 +#define _ResY 0x701038 +#define _UnlockEverythingThatCanBeLocked 0x735EC1 +#define _FEDatabase 0x748F70 +#define _BuildRegion 0x734998 +#define _ChyronSkin 0x758928 +#define _Tweak_GameSpeed 0x6B7994 +#define _SkipMovies 0x73496C +#define _IsSoundEnabled 0x6F1DD8 +#define _IsAudioStreamingEnabled 0x6F1DDC +#define _pPreRaceScreenManager 0x735ED8 +#define _pRaceCoordinator 0x7361F0 +#define _bIsDriftRace 0x7361A4 +#define _bIsDragRace 0x7361A8 +#define _unk_78A344 0x78A344 +#define _NumberOfPlayers 0x78A320 +#define _NumberOfOpponents 0x78A324 +#define _PlayersByIndex 0x7361BC +#define _PlayersByNumber 0x7361B4 +#define _pCurrentRace 0x73619C +#define _Camera_StopUpdating 0x735F5C diff --git a/NFSUExtraOptions+/HotkeyStuff.h b/NFSUExtraOptions+/HotkeyStuff.h new file mode 100644 index 0000000..5bbf469 --- /dev/null +++ b/NFSUExtraOptions+/HotkeyStuff.h @@ -0,0 +1,155 @@ +#include "stdio.h" +#include +#include "..\includes\injector\injector.hpp" +#include "..\includes\IniReader.h" + +bool once3, IsOnFocus, AutoDrive; +DWORD TheGameFlowManager; +HWND windowHandle; + +void Thing() +{ + UpdateCameraMovers(); // Hijacked call from main loop + + _asm pushad; + + TheGameFlowManager = *(DWORD*)_TheGameFlowManager; // 3 = FE, 4&5 = Loading screen, 6 = Gameplay + windowHandle = *(HWND*)_hWnd; + IsOnFocus = !(*(bool*)_IsLostFocus); + + // Windowed Mode Related Fixes (Center and Resize) + if (WindowedMode && windowHandle && !once3) + { + RECT o_cRect, n_cRect, n_wRect; + GetClientRect(windowHandle, &o_cRect); + + DWORD wStyle = GetWindowLongPtr(windowHandle, GWL_STYLE); + + switch (WindowedMode) + { + case 1: wStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU); break; + case 2: default: wStyle |= (WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_OVERLAPPEDWINDOW); break; + } + + SetWindowLongPtr(windowHandle, GWL_STYLE, wStyle); + + // make window change style + SetWindowPos(windowHandle, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME); + + //GetWindowRect(windowHandle, &n_wRect); + GetClientRect(windowHandle, &n_cRect); + int n_wWidth = *(int*)_ResX; + int n_wHeight = *(int*)_ResY; + int dif_wWidth = o_cRect.right - n_cRect.right; + int dif_wHeight = o_cRect.bottom - n_cRect.bottom; + int newWidth = n_wWidth + dif_wWidth; + int newHeight = n_wHeight + dif_wHeight; + + HWND hDesktop = GetDesktopWindow(); + RECT desktop; + GetWindowRect(hDesktop, &desktop); + + int newXPos = ((desktop.right - desktop.left) - newWidth) / 2; + int newYPos = ((desktop.bottom - desktop.top) - newHeight) / 2; + + SetWindowPos(windowHandle, NULL, newXPos, newYPos, newWidth, newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + once3 = 1; + } + + // Lock EA Trax Skin + if (EATraxSkin != -1) + { + injector::WriteMemory(_ChyronSkin, EATraxSkin, true); + } + + // Any Track in Any Mode + if ((GetAsyncKeyState(hotkeyAnyTrackInAnyMode) & 1)) + { + CIniReader iniReader("NFSUExtraOptionsSettings.ini"); + AnyTrackInAnyMode = !AnyTrackInAnyMode; + iniReader.WriteInteger("Menu", "AnyTrackInAnyRaceMode", AnyTrackInAnyMode); + + if (AnyTrackInAnyMode) + { + injector::MakeNOP(0x4BA16C, 2, true); + injector::MakeNOP(0x4BA202, 2, true); + } + + else + { + injector::WriteMemory(0x4BA16C, 0x1D74, true); + injector::WriteMemory(0x4BA202, 0x2C74, true); + } + } + + // Drunk Driver + if ((GetAsyncKeyState(hotkeyAutoDrive) & 1) && (TheGameFlowManager == 6) && IsOnFocus) + { + AutoDrive = !AutoDrive; + + DWORD PlayerThing = *(DWORD*)_PlayersByIndex; + + + if (PlayerThing) + { + if (AutoDrive) + { + Player_AutoPilotOn((DWORD*)PlayerThing); + } + else + { + MakeUserCall(Player_AutoPilotOff, 1, edi, PlayerThing); + } + } + } + + // Freeze Camera + if ((GetAsyncKeyState(hotkeyFreezeCamera) & 1) && IsOnFocus) + { + *(bool*)_Camera_StopUpdating = !(*(bool*)_Camera_StopUpdating); + } + + // Unlock All Things + if ((GetAsyncKeyState(hotkeyUnlockAllThings) & 1)) + { + UnlockAllThings = !UnlockAllThings; + CIniReader iniReader("NFSUExtraOptionsSettings.ini"); + iniReader.WriteInteger("Gameplay", "UnlockAllThings", UnlockAllThings); + + injector::WriteMemory(_UnlockEverythingThatCanBeLocked, UnlockAllThings, true); + } + + + if ((GetAsyncKeyState(hotkeyNoGravity) & 1) && (TheGameFlowManager == 6) && IsOnFocus) + { + NoGravity = !NoGravity; + if (NoGravity) + { + injector::MakeNOP(0x46AB56, 3, true); + } + else + { + injector::WriteMemory(0x46AB56, 0x5F0450FF, true); + } + } + + if ((GetAsyncKeyState(hotkeyJump) & 1) && vector3playerspeed != nullptr && (TheGameFlowManager == 6) && IsOnFocus) + { + // sometimes it's not PLAYER speed + *(float*)((BYTE*)vector3playerspeed + 0x78) += JumpSpeed; // add to speed.z + } + vector3playerspeed = nullptr; + + if (mpsInsteadOfMiPH) + { + char *MPH = GetString(0, 0x8569AB44); + injector::WriteMemory(0x6CC9F0, 0x3f800000, true); + injector::WriteMemory(0x6CC8B0, 0x41d80000, true); + + *(DWORD*)MPH = *(DWORD*)"m/s"; + mpsInsteadOfMiPH = false; + } + + _asm popad; +} \ No newline at end of file diff --git a/NFSUExtraOptions+/InGameFunctions.h b/NFSUExtraOptions+/InGameFunctions.h new file mode 100644 index 0000000..b49ebba --- /dev/null +++ b/NFSUExtraOptions+/InGameFunctions.h @@ -0,0 +1,50 @@ +#pragma once + +#include "stdio.h" +#include + +// Function pointers +int(__stdcall* Player_AutoPilotOn)(DWORD* _Player) = (int(__stdcall*)(DWORD*))0x42E230; +char*(__stdcall* Player_GetName)(DWORD* _Player) = (char* (__stdcall*)(DWORD*))0x431D30; +void(*UpdateCameraMovers)() = (void(*)())0x48E8B0; +void(*MovieStop)() = (void(*)())0x5A4C80; +void(*feDisableStartRaceScreens)() = (void(*)())0x495E90; +int(__thiscall* QRLapPopupScreen_DrawLaps)(DWORD* QRLapPopupScreen) = (int(__thiscall*)(DWORD*))0x4B81F0; +int(__thiscall* QROptionsScreen_DrawNumRaces)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B8280; +int(__thiscall* QROptionsScreen_DrawCatchUp)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B8310; +int(__thiscall* QROptionsScreen_DrawTraffic)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B83C0; +int(__thiscall* QROptionsScreen_DrawAISkill)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B84A0; +int(__thiscall* QROptionsScreen_ScrollLaps)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B8560; +int(__thiscall* QROptionsScreen_ScrollNumRaces)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B85C0; +int(__thiscall* QROptionsScreen_ScrollCatchUp)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B8650; +int(__thiscall* QROptionsScreen_ScrollTraffic)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B8690; +int(__thiscall* QROptionsScreen_ScrollAISkill)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B86F0; +int(__fastcall* cFrontendDatabase_GetOptions)(DWORD* cFrontendDatabase, int eRaceType) = (int(__fastcall*)(DWORD*, int))0x4AB510; +int(__cdecl* FEngSetVisible_obj)(void* FEObject) = (int(__cdecl*)(void*))0x4F6970; +int(__stdcall* RaceCoordinator_RCPostMessage)(DWORD* RaceCoordinator, int RCMessage, int unk) = (int(__stdcall*)(DWORD*, int, int))0x421C00; +char* (__fastcall* GetString)(int crap, DWORD code) = (char*(__fastcall*)(int, DWORD))0x59FB80; + +// Functions which has odd calling conventions (using NFSUCalls.h to wrap them) +int(*Player_AutoPilotOff)(DWORD* EDI_Player) = (int(*)(DWORD*))0x42E400; +char*(*DriverInfo_GetName)(DWORD* EAX_DriverInfo) = (char*(*)(DWORD*))0x580AF0; +int(*QROptionsScreen_ShowSingleOption)(DWORD* QROptionsScreen, int a2) = (int(*)(DWORD*, int))0x4B8D50; +int(*FEHashUpper)(char const* EDX_StringToHash) = (int(*)(char const*))0x4FD230; +int(*FEngPrintf_obj)(DWORD* EAX_obj, const char* format, ...) = (int(*)(DWORD*, const char*, ...))0x4F68A0; +int(*FEngPrintf_H)(const char* EAX_pkg, int EDI_hash, const char* format, ...) = (int(*)(const char*, int, const char*, ...))0x4F6850; +int(*FEngPrintf)(const char* EAX_pkg, const char* EDX_obj_name, const char* format, ...) = (int(*)(const char*, const char*, const char*, ...))0x4F67F0; +int(*FEngSNPrintf)(const char* ESI_buf, int len, const char* format, ...) = (int(*)(const char*, int, const char*, ...))0x4F68C0; +int(*cFEng_SetButtonState)(DWORD* cFeng, char const* EAX_pkg, int EDI_hash, bool state) = (int(*)(DWORD*, char const*, int, bool))0x4F5F80; +int(*FEngQueuePackagePush)(char const* EAX_pkg, char const* EDI_pkg2, int state) = (int(*)(char const*, char const*, int))0x4F63E0; +int(*FEngSwitchPackages)(char const* pkg, char const* EDI_pkg2) = (int(*)(char const*, char const*))0x4F6360; +int(*FEngFindPackage)(char const* EAX_pkg) = (int(*)(char const*))0x4F65D0; +int(*FEPackage_FindObjectByHash)(DWORD* EDX_pkg, int ECX_hash) = (int(*)(DWORD*, int))0x4FFB70; +int(*FEPackageManager_BroadcastMessage)(DWORD* EDX_FEPackageManager, int msgHash) = (int(*)(DWORD*, int))0x4F3DC0; +int(*FEngine_FindObject)(DWORD* EAX_FEngine, unsigned int hash, const char* ECX_pkg) = (int(*)(DWORD*, unsigned int, char const*))0x4EF050; +int(*QROptionsScreen_SetupCircuit)(DWORD* EAX_QROptionsScreen) = (int(*)(DWORD*))0x4B8E60; +int(*FEngSetScript_obj)(DWORD* EDI_feObj, char const* script, bool unk) = (int(*)(DWORD*, char const*, bool))0x4F6B70; +int(*FEngSetScript_H)(char const* ESI_pkg, int EAX_hash, char const* script, bool unk) = (int(*)(char const*, int, char const*, bool))0x4F6C30; +int(*FEngSetScript)(char const* EAX_pkg, char const* EDX_obj_name, char const* script, bool unk) = (int(*)(char const*, char const*, char const*, bool))0x4F6BD0; +int(*PreRaceStatsScreen_SetVisibility)(DWORD* PreRaceStatsScreen, bool EAX_visible) = (int(*)(DWORD*, bool))0x4959D0; +bool(*FEngIsScriptRunning)(char const* EAX_pkg, int EDI_hash, char const* script) = (bool(*)(char const*, int, char const*))0x4F6C80; +int(*FEngSetVisible)(char const* EDI_pkg,char const* EDX_obj) = (int(*)(char const*, char const*))0x414A60; +DWORD QRModeSelectScreen_MaybeMakeGameModeAvailable = 0x4B6FE0; \ No newline at end of file diff --git a/NFSUExtraOptions+/NFSUExtraOptions+.sln b/NFSUExtraOptions+/NFSUExtraOptions+.sln new file mode 100644 index 0000000..5362455 --- /dev/null +++ b/NFSUExtraOptions+/NFSUExtraOptions+.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NFSUExtraOptions", "NFSUExtraOptions.vcxproj", "{3C558AD9-5F9C-4A14-8F07-800F46C132C7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3C558AD9-5F9C-4A14-8F07-800F46C132C7}.Debug|Win32.ActiveCfg = Debug|Win32 + {3C558AD9-5F9C-4A14-8F07-800F46C132C7}.Debug|Win32.Build.0 = Debug|Win32 + {3C558AD9-5F9C-4A14-8F07-800F46C132C7}.Release|Win32.ActiveCfg = Release|Win32 + {3C558AD9-5F9C-4A14-8F07-800F46C132C7}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/NFSUExtraOptions+/NFSUExtraOptions.aps b/NFSUExtraOptions+/NFSUExtraOptions.aps new file mode 100644 index 0000000000000000000000000000000000000000..c32be11318f9113c44b01d670b0258674ab5d748 GIT binary patch literal 2432 zcmb_e-EPxJ6#hcPKS$XHmA^W*dbyt8b)(~ za@eKN%bXjW9xOcJT89k-4YXhqlG;$gZ!;UhzG&|D%lp9fZ}OiZ1(4;xJs5TmCcS>? z*3n={Vdec>S1*xff%EIO2t{6&Fpdc>5gx~jvzTS|jTb~}8!iH_7Mk4E;n34S`Mblt zjCaNVJ_8c-1>8R?5dD21TaF@s+8ceA^&bKXls86*0fu;v9exkcr{p1?VMLlYil)LC zRB)ej1AlS_?%@h~4br^CAv&agMVmT2^+hx-J*H)clpWDOk5It@J^V_(M2|^$gs##r zv;)c;Dt?TqB?OK2|(KNhm4U#`Z@Ng0Vlvp zSRr(kKCRQjbyT@KIg({!Y|1QUBub=1YK)Zie{t>6OE71|FK0?AH!T(=cOWNyYv1Fa zukP_NWsaz2!aE#iA_eY${QkDo{pDqUd5fprp-F!WtdtHdOFT@oS$dST0*_e9*@$uI zVHfFFfW)SxGcgc_wi~pq zrdxOHplbsGCOMdfQ&zz{*;66>BIcURnQTgP)E&^e7*P4+@iwLp&WzMH5@_ zam0KG@q)XEy9xJ_js7$rbnPU4VU3kHUx|5IUE}#W^C?xbT+8orUuH!~57lF-Xee8B z21(zL3@^>{LF#0M{plV%#VT#vV@*nz8PR&dJ>psVmd|KaxJv$XnJE+1i8M6n9-E}8 zvDW46lj0@UBPCDUs4W9t)4qHxKhDL{W8&0pqtP;%Fbca&pR&;P6QQ*kw0f6}Utamo zdvZ7ELLAFPV=gyzWn^4T!Ni7(_)+YVG{S!^w=bd3- zmtAbYn2^mfyEAic@43ub{{Gpp4U6p9x^`s)>)VjKV(AjcDLQ$T#7z#pDpKhi*n8F z>o_{`-m_DyTf>@GvO_zu3p=wivn8HuR<)1J)cHNOeP*gW2l>)r^{${@7$d8I%Phtn zQ0m)P>>q)SaA;ul2YwH+x`>^^qs;s^_pC?$?}P6SUr(3BfAtUx8u{qB2P;KV2fLSx zjevaMyINz=P9Fb_XTR7l=l}J|d)Iz2^E$)t!&nw|9YU`X+0?dmFgqZQd=}Nom?rsD zB$6(Orz)5D{^W}0Qa0dmM$}%Q(U~1VA)Twt)|hQFR$OF;8SRslBVxC1@9izT>&)y= zXe7IgUVF?|*)4v$RXsY7cw4#9!iFQLOO9f-d>Pj!_%)34GCHhl)V&2-^KxJ7``BJ_ zRY7mj_{y%b=e9Ynv}KU1!yWEmM*)jenF+LE(#BKe%m^>u!#7pr7Bn zgO0j(#xzsp8uT%zAn5mPr`CPdOzsMjv+ICr>+8@$!UvTJd7I?wt=V-Jed0Hn<5QZfIu%yK*uszMZG=G%z10KV(R=nd^JI;p+4>C)+Mu?G)+b|b$`T6}zsQY!PXyW4>m4U8kB(pZu9JuV+}cV_&VFA+C+=jobH1dQhlcXjwHXu1D30WLtGnaIKM0IQJ?g0QTod}?RRnd2Yg{U + + + + Debug + Win32 + + + Release + Win32 + + + + {3C558AD9-5F9C-4A14-8F07-800F46C132C7} + Win32Proj + NFSUExtraOptions + NFSUExtraOptions+ + 10.0.17134.0 + + + + DynamicLibrary + true + v141 + NotSet + + + DynamicLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + true + .asi + NFSUExtraOptions+ + + + false + .asi + $(SolutionDir)$(Configuration)\$(ProjectName)\scripts\ + NFSUExtraOptions+ + + + + Use + Level3 + Disabled + + + MultiThreadedDebug + UninitializedLocalUsageCheck + + + + + EditAndContinue + false + + + Windows + true + + + + + Level4 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + Default + + + + + false + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + + \ No newline at end of file diff --git a/NFSUExtraOptions+/NFSUExtraOptions.vcxproj.user b/NFSUExtraOptions+/NFSUExtraOptions.vcxproj.user new file mode 100644 index 0000000..6e2aec7 --- /dev/null +++ b/NFSUExtraOptions+/NFSUExtraOptions.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/NFSUExtraOptions+/PreRaceStatsScreen.h b/NFSUExtraOptions+/PreRaceStatsScreen.h new file mode 100644 index 0000000..7d7b35d --- /dev/null +++ b/NFSUExtraOptions+/PreRaceStatsScreen.h @@ -0,0 +1,137 @@ +void __fastcall PreRaceStatsScreen_SetInitScript(DWORD* _PreRaceStatsScreen, void* EDX_Unused) +{ + int NumPlayers = _PreRaceStatsScreen[52]; + + for (int i = 0; i < NumPlayers; i++) + { + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 16], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 20], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 24], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 28], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 32], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 36], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 40], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 44], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 48], esp, "Init", esp, 1); + } +} + +void __fastcall PreRaceStatsScreen_DrawCars(DWORD* _PreRaceStatsScreen, void* EDX_Unused) +{ + int NumPlayers = _PreRaceStatsScreen[52]; + DWORD* PCurrentRace = *(DWORD**)_pCurrentRace; + DWORD* CarPtr; + char* CarName; + + for (int i = 0; i < NumPlayers; i++) + { + if (i >= PCurrentRace[9]) CarPtr = 0; + else CarPtr = (DWORD*)PCurrentRace[328 + i]; + + if (CarPtr) + { + CarPtr = (DWORD*)CarPtr[0]; + if (CarPtr) + { + CarName = (char*)CarPtr[3]; + if (CarName) MakeUserCall(FEngPrintf_obj, 2, eax, _PreRaceStatsScreen[i + 16], esp, CarName); + } + } + } +} + +void __fastcall PreRaceStatsScreen_DrawDrivers(DWORD* _PreRaceStatsScreen, void* EDX_Unused) +{ + int NumPlayers = _PreRaceStatsScreen[52]; + DWORD* PCurrentRace = *(DWORD**)_pCurrentRace; + DWORD* Player = *(DWORD**)_PlayersByNumber; + DWORD* Player2 = *(DWORD**)(_PlayersByNumber + 4); + DWORD* CarPtr, *DriverInfo; + char* DriverName; + + for (int i = 0; i < NumPlayers; i++) + { + if (i >= PCurrentRace[9]) CarPtr = 0; + else CarPtr = (DWORD*)PCurrentRace[328 + i]; + + if (CarPtr) + { + CarPtr = (DWORD*)CarPtr[0]; + if (CarPtr) + { + DriverInfo = (DWORD*)CarPtr[5]; + if (DriverInfo) + { + // Check if Player's car + if (Player && (CarPtr == *(DWORD**)Player[1])) DriverName = Player_GetName(Player); + else if (Player2 && (CarPtr == *(DWORD**)Player2[1])) DriverName = Player_GetName(Player2); + else DriverName = (char*)MakeUserCall(DriverInfo_GetName, 1, eax, DriverInfo); + + MakeUserCall(FEngPrintf_obj, 4, eax, _PreRaceStatsScreen[i + 20], esp, "0%d: %s", esp, i + 1, esp, DriverName); + } + } + } + } +} + +void __fastcall PreRaceStatsScreen_NotificationMessage(DWORD* _PreRaceStatsScreen, void* EDX_Unused, DWORD msg, DWORD* FEObj, int arg1, int arg2) +{ + DWORD* PreRaceScreenManager; // eax + char* RaceHUDPkg; + + switch (msg) + { + case 0xB5AF2461: // PAD_START + RaceCoordinator_RCPostMessage(*(DWORD**)_pRaceCoordinator, 4, 0); + break; + case 0xC98356BA: + PreRaceScreenManager = *(DWORD**)_pPreRaceScreenManager; + + if (PreRaceScreenManager[1] && !_PreRaceStatsScreen[53]) + { + _PreRaceStatsScreen[53] = 1; + + PreRaceStatsScreen_SetInitScript(_PreRaceStatsScreen, EDX_Unused); + MakeUserCall(PreRaceStatsScreen_SetVisibility, 2, esp, _PreRaceStatsScreen, eax, 1); + PreRaceStatsScreen_DrawCars(_PreRaceStatsScreen, EDX_Unused); + PreRaceStatsScreen_DrawDrivers(_PreRaceStatsScreen, EDX_Unused); + + if (*(bool*)_unk_78A344 || *(bool*)_bIsDragRace) + { + RaceHUDPkg = "drag_HUD_test.fng"; + } + else if (*(int*)_NumberOfPlayers == 2) + { + RaceHUDPkg = "2PHudBot.fng"; + } + else + { + RaceHUDPkg = "HUD_SingleRace.fng"; + } + MakeUserCall(FEngSetScript, 4, eax, RaceHUDPkg, edx, "GaugeCluster", esp, "Fade", esp, 1); + } + + if (_PreRaceStatsScreen[49]) + { + if (_PreRaceStatsScreen[50]) + { + if (!(bool)MakeUserCall(FEngIsScriptRunning, 3, eax, _PreRaceStatsScreen[3], edi, 0x798C7EFF, esp, "FadeOut")) + { + if (_PreRaceStatsScreen[51]) RaceCoordinator_RCPostMessage(*(DWORD**)_pRaceCoordinator, 4, 0); + else PreRaceScreenManager[0] = 1; + } + } + else + { + _PreRaceStatsScreen[50] = 1; + MakeUserCall(FEngSetScript_H, 4, esi, _PreRaceStatsScreen[3], eax, 0x798C7EFF, esp, "FadeOut", esp, 1); + } + } + + if (PreRaceScreenManager[0]) feDisableStartRaceScreens(); // ReadyToSkip + break; + case 0xD8692111: + _PreRaceStatsScreen[49] = 1; + break; + } +} \ No newline at end of file diff --git a/NFSUExtraOptions+/QROptionsScreen.h b/NFSUExtraOptions+/QROptionsScreen.h new file mode 100644 index 0000000..48471e0 --- /dev/null +++ b/NFSUExtraOptions+/QROptionsScreen.h @@ -0,0 +1,45 @@ +#pragma once + +#define _CurrentRaceMode 0x777CC8 + +int __fastcall QROptionsScreen_DrawNumRacers(DWORD* QROptionsScreen, void* EDX_Unused) +{ + const char* FEPkg; + int PtrNumRacers; + char buf[32]; + + int CurrentRaceMode = *(int*)_CurrentRaceMode; + int OptionNumber = QROptionsScreen[33]; + + MakeUserCall(FEngSNPrintf, 4, esi, buf, esp, 32, esp, "Option%1dTextString", esp, OptionNumber); + FEPkg = (const char*)*((DWORD*)QROptionsScreen + 3); + MakeUserCall(FEngPrintf, 3, eax, FEPkg, edx, buf, esp, "Racers:"); + MakeUserCall(FEngSNPrintf, 4, esi, buf, esp, 32, esp, "Option%1dData", esp, OptionNumber); + PtrNumRacers = cFrontendDatabase_GetOptions((DWORD*)_FEDatabase, CurrentRaceMode); + return (int)MakeUserCall(FEngPrintf, 4, eax, FEPkg, edx, buf, esp, "%d", esp, *(DWORD*)(PtrNumRacers)); +} + +int __fastcall QROptionsScreen_ScrollNumRacers(DWORD* QROptionsScreen, void* EDX_Unused, int Action) +{ + int* PtrNumRacers; // ecx + int NumRacers; // eax + + int CurrentRaceMode = *(int*)_CurrentRaceMode; + + PtrNumRacers = (int*)(cFrontendDatabase_GetOptions((DWORD*)_FEDatabase, CurrentRaceMode)); + NumRacers = *PtrNumRacers; + if (Action == 0x9120409E) // PAD_LEFT + { + if (--NumRacers < MinRacers) + { + *PtrNumRacers = MaxRacers; + return QROptionsScreen_DrawNumRacers(QROptionsScreen, EDX_Unused); + } + } + else if (Action == 0xB5971BF1 && ++NumRacers > MaxRacers) // PAD_RIGHT + { + NumRacers = MinRacers; + } + *PtrNumRacers = NumRacers; + return QROptionsScreen_DrawNumRacers(QROptionsScreen, EDX_Unused); +} \ No newline at end of file diff --git a/NFSUExtraOptions+/UserCalls.h b/NFSUExtraOptions+/UserCalls.h new file mode 100644 index 0000000..87bd3e2 --- /dev/null +++ b/NFSUExtraOptions+/UserCalls.h @@ -0,0 +1,137 @@ +#pragma once + +#include "stdio.h" +#include + +enum reg : int +{ + eax, + ebx, + ecx, + edx, + edi, + esi, + esp +}; + +typedef struct +{ + reg reg; + void* val; +} FuncArg; + +typedef struct +{ + void* _eax; + void* _ebx; + void* _ecx; + void* _edx; + void* _edi; + void* _esi; +} Regs; + +/* A wrapper to call the functions which doesn't fit any calling convention. + * This is usually caused by the "Link-Time Code Generation". + * + * Usage: After number of arguments, you need to push the register name (eax, ebx, etc.) and the argument itself. + * If argument is pushed regularly, use "esp" as the register name. + * Stack difference will get calculated by the function automatically. + * + * For example: MakeUserCall(FEngPrintf, 3, eax, FEPkg, edx, buf, esp, str); + * // It's int by default. Don't use "" in here as it will give you a compile-time error. + * FEngPrintf // The name of the function we want to call. + * 3 // Number of arguments taken by the function. + * eax, FEPkg // "FEPkg" will get passed through "eax" register. (ASM: mov eax, FEPkg) + * edx, buf // "buf" will get passed through "edx" register. (ASM: mov edx, buf) + * esp, str // "str" will get passed through a regular "push" instruction. (ASM: push str) +*/ +template +R MakeUserCall(R _func, int _numArgs, ...) +{ + R _retval = 0; + std::vector _args; + std::vector _pushArgs; + FuncArg _anArg; + Regs _regs, _regs_backup; + va_list ap; + + // Get current registers + _asm + { + mov _regs._eax, eax + mov _regs._ebx, ebx + mov _regs._ecx, ecx + mov _regs._edx, edx + mov _regs._edi, edi + mov _regs._esi, esi + } + + // backup regs + _regs_backup = _regs; + + va_start(ap, _numArgs); + + for (int i = 0; i < _numArgs; i ++) + { + _anArg.reg = va_arg(ap, reg); + _anArg.val = va_arg(ap, void*); + + switch (_anArg.reg) + { + case esp: + _pushArgs.push_back(_anArg); + break; + default: + _args.push_back(_anArg); + break; + } + } + + va_end(ap); + + // get stack difference + int _stack = _pushArgs.size() * 4; + + // move args to the register struct + for (FuncArg i : _args) + { + *((void**)&_regs + i.reg) = i.val; + } + + // push args in reverse order + reverse(_pushArgs.begin(), _pushArgs.end()); + + for (FuncArg s : _pushArgs) + { + _asm push s.val; + } + + // Set registers before call + _asm + { + mov eax, _regs._eax + mov ebx, _regs._ebx + mov ecx, _regs._ecx + mov edx, _regs._edx + mov edi, _regs._edi + mov esi, _regs._esi + + // invoke the call + call _func; + add esp, _stack; + mov _retval, eax; + } + + // Restore regs + _asm + { + //mov eax, _regs_backup._eax + mov ebx, _regs_backup._ebx + mov ecx, _regs_backup._ecx + mov edx, _regs_backup._edx + mov edi, _regs_backup._edi + mov esi, _regs_backup._esi + } + + return _retval; +} diff --git a/NFSUExtraOptions+/dllmain.cpp b/NFSUExtraOptions+/dllmain.cpp new file mode 100644 index 0000000..cd54af9 --- /dev/null +++ b/NFSUExtraOptions+/dllmain.cpp @@ -0,0 +1,26 @@ +#include "stdafx.h" +#include "stdio.h" +#include +#include +#include "ExtraOptionsStuff.h" + +BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD reason, LPVOID /*lpReserved*/) +{ + if (reason == DLL_PROCESS_ATTACH) + { + uintptr_t base = (uintptr_t)GetModuleHandleA(NULL); + IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)(base); + IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(base + dos->e_lfanew); + + if ((base + nt->OptionalHeader.AddressOfEntryPoint + (0x400000 - base)) == 0x670CB5) // Check if .exe file is compatible - Thanks to thelink2012 and MWisBest + Init(); + + else + { + MessageBoxA(NULL, "This .exe is not supported.\nPlease use v1.4 English speed.exe (3,03 MB (3.178.496 bytes)).", "NFSU Extra Options", MB_ICONERROR); + return FALSE; + } + } + return TRUE; + +} \ No newline at end of file diff --git a/NFSUExtraOptions+/resource.h b/NFSUExtraOptions+/resource.h new file mode 100644 index 0000000..86e7385 --- /dev/null +++ b/NFSUExtraOptions+/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by NFSUExtraOptions.rc + +// Yeni nesneler için sonraki varsayýlan deðerler +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/NFSUExtraOptions+/stdafx.cpp b/NFSUExtraOptions+/stdafx.cpp new file mode 100644 index 0000000..fd4f341 --- /dev/null +++ b/NFSUExtraOptions+/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/NFSUExtraOptions+/stdafx.h b/NFSUExtraOptions+/stdafx.h new file mode 100644 index 0000000..f3a0737 --- /dev/null +++ b/NFSUExtraOptions+/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + + + +// TODO: reference additional headers your program requires here diff --git a/NFSUExtraOptions+/targetver.h b/NFSUExtraOptions+/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/NFSUExtraOptions+/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/NFSUExtraOptions+Settings.ini b/NFSUExtraOptions+Settings.ini new file mode 100644 index 0000000..b35b5e9 --- /dev/null +++ b/NFSUExtraOptions+Settings.ini @@ -0,0 +1,48 @@ +[Hotkeys] // Look at http://cherrytree.at/misc/vk.htm for key values (decimal) +AnyTrackInAnyMode = 36 // Shows all race tracks in every game mode. Your latest preference will be saved. (Default = 36 = Home Key) +FreezeCamera = 19 // Freezes/unfreezes camera when pressed. (Default = 19 = Pause/Break key) +UnlockAllThings = 116 // Unlocks everything in game. (Default = 116 = F5 key) +AutoDrive = 117 // Enables Auto Drive (Drunk Driver) hack. (Default = 117 = F6 key) + +[LapControllers] +Minimum = 0 // Minimum number of laps can be selected at Quick Race options screen. (Default = 0) (0/127) +Maximum = 127 // Maximum number of laps can be selected at Quick Race options screen. (Default = 127) (0/127) + +[RacerControllers] // Requires ShowMoreRaceOptions to be enabled. +Minimum = 1 // Minimum number of racers can be selected at Quick Race options screen. (Default = 1) (1/4) +Maximum = 4 // Maximum number of racers can be selected at Quick Race options screen. (Default = 4) (1/4) + +[Menu] +ShowMoreRaceOptions = 1 // Adds a racers option to Track Options menu, moves Laps option to a seperate pop-up. (0 = False, 1 = True (Default)) +ShowLanguageSelectScreen = 0 // Shows Language Select Screen when game starts. (0 = False (Default), 1 = True) +ShowSpecialVinyls = 1 // Shows special vinyls in Unique category. (0 = False, 1 = True (Default)) +ShowDebugOptionsMenu = 0 // Shows the hidden Debug menu option in Pause Options screen. (0 = False (Default), 1 = True) +ShowTournament = 0 // Adds hidden Tournament mode to Quick Race Menu. (0 = False (Default), 1 = True) +EATraxSkin = -1 // Changes your EA Trax Menu skin. Chyron (EA Trax info box) may not be shown if not -1 nor 0. It also gets saved in the save game. (-1 = Not Set (Default), 0 = Vanilla, 1 = Alpine, 2 = Kenwood, 3 = Audiobahn) +AnyTrackInAnyRaceMode = 0 // Current status of "Any Track In Any Mode" hack. (0 = False (Default), 1 = True) +SplashScreenTimeLimit = 30 // Duration of Splash Screen. (Default = 30.0) +MiniWorldCarSpeedFast = 50 // Speed of the cars at the infinite tunnel on menus while changing between them. (Default = 50.0) +MiniWorldCarSpeedCruising = 20 // Speed of the cars at the infinite tunnel on menus. (Default = 20.0) +MiniWorldCarSpeedSlow = 1 // Speed of the cars at the infinite tunnel on rim tuning and paint. (Default = 1.0) +ShowcaseCamInfiniteZoom = 0 // Zoom hack for Showcase Camera mode. (0 = False (Default), 1 = True) +ShowcaseCamInfiniteRotation = 0 // Rotation hack for Showcase Camera mode. (0 = False (Default), 1 = True) +DisableTakeover = 0 // Removes "Extra Options by ExOpts Team" text from Copyright area. (0 = False (Default), 1 = True) + +[Gameplay] +EnableHiddenCameraModes = 0 // Enables hidden camera modes. (0 = False (Default), 1 = True) +GameSpeed = 1 // (ONLY FOR EXPERTS) Speed modifier for races. (Default = 1.0) +GameRegion = 0 // Sets the region for the game. (0 = NTSC-U (Default), 1+ = PAL/NTSC-J) +StartingCash = 0 // Starts the game with specified amount of cash. 10000 from prologue race is not included. (Default = 0) +StartingStylePoints = 0 // Starts the game with specified amount of style points. (Default = 0) +UnlockAllThings = 0 // Current status of UnlockAllThings hack. (0 = False (Default), 1 = True) + +[Fixes] +DisappearingWheelsFix = 1 // Fixes disappearing wheels when lost focus. Useful for open-wheel cars. (0 = False, 1 = True (Default)) +DamageFix = 0 // Makes mirrors and plate drop, glass crack when taken damage. You also need to reset your car before moving. (0 = False (Default), 1 = True) + +[Misc] +WindowedMode = 0 // Runs the game in a window instead of full screen. (0 = False (Default), 1 = True, 2 = Bordered) +SkipMovies = 0 // Skips cutscenes. (0 = False (Default), 1 = True) +EnableSound = 1 // Enables in-game sounds. (0 = False, 1 = True (Default)) +EnableMusic = 1 // Enables in-game music. (0 = False, 1 = True (Default)) +AllowMultipleInstances = 0 // Allows running more than 1 instance of the game. Also lets the game run together with Most Wanted. (0 = False (Default), 1 = True) diff --git a/README.md b/README.md new file mode 100644 index 0000000..cb01ba2 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# NFSU Extra Options+ +Extra Options+ is a script mod which improves your gaming experience by many ways. It's developed by ExOpts Team and extended by Crab Gazette. + +# Download +You can [download Extra Options+](https://github.com/nlgzrgn/NFSUExOpts/releases) from Releases page. diff --git a/includes/CPatch.h b/includes/CPatch.h new file mode 100644 index 0000000..05f4ee2 --- /dev/null +++ b/includes/CPatch.h @@ -0,0 +1,145 @@ +#pragma once +#include + +class CPatch +{ +public: + inline static void Patch(void* address, void* data, int size) + { + unsigned long protect[2]; + VirtualProtect(address, size, PAGE_EXECUTE_READWRITE, &protect[0]); + memcpy(address, data, size); + VirtualProtect(address, size, protect[0], &protect[1]); + } + + inline static void Patch2(int address, void* data, int size) + { + unsigned long protect[2]; + VirtualProtect((void *)address, size, PAGE_EXECUTE_READWRITE, &protect[0]); + memcpy((void *)address, data, size); + VirtualProtect((void *)address, size, protect[0], &protect[1]); + } + + inline static void Unprotect(int address, int size) + { + unsigned long protect[2]; + VirtualProtect((void *)address, size, PAGE_EXECUTE_READWRITE, &protect[0]); + } + inline static void Nop(int address, int size) + { + unsigned long protect[2]; + VirtualProtect((void *)address, size, PAGE_EXECUTE_READWRITE, &protect[0]); + memset((void *)address, 0x90, size); + VirtualProtect((void *)address, size, protect[0], &protect[1]); + } + inline static void FillWithZeroes(int address, int size) + { + unsigned long protect[2]; + VirtualProtect((void *)address, size, PAGE_EXECUTE_READWRITE, &protect[0]); + memset((void *)address, 0x00, size); + VirtualProtect((void *)address, size, protect[0], &protect[1]); + } + inline static void RedirectCall(int address, void *func) + { + int temp = 0xE8; + Patch((void *)address, &temp, 1); + temp = (int)func - ((int)address + 5); + Patch((void *)((int)address + 1), &temp, 4); + } + inline static void RedirectJump(int address, void *func) + { + int temp = 0xE9; + Patch((void *)address, &temp, 1); + temp = (int)func - ((int)address + 5); + Patch((void *)((int)address + 1), &temp, 4); + } + inline static void SetChar(int address, char value) + { + Patch((void *)address, &value, 1); + } + inline static void SetUChar(int address, unsigned char value) + { + Patch((void *)address, &value, 1); + } + inline static void SetShort(int address, short value) + { + Patch((void *)address, &value, 2); + } + inline static void SetUShort(int address, unsigned short value) + { + Patch((void *)address, &value, 2); + } + inline static void SetInt(int address, int value) + { + Patch((void *)address, &value, 4); + } + inline static void SetUInt(int address, unsigned int value) + { + Patch((void *)address, &value, 4); + } + inline static void SetUIntWithCheck(int address, unsigned int value, unsigned int expectedValue) + { + if (*(unsigned int *)address == expectedValue) + Patch((void *)address, &value, 4); + } + inline static void SetFloat(int address, float value) + { + Patch((void *)address, &value, 4); + } + inline static void SetDouble(int address, double value) + { + Patch((void *)address, &value, 8); + } + inline static void SetPointer(int address, void *value) + { + Patch((void *)address, &value, 4); + } + + inline static void AdjustPointer(int address, void *value, DWORD offset, DWORD end) + { + int result; + if((DWORD)*(DWORD*)address >= offset && (DWORD)*(DWORD*)address <= end) { + result = (DWORD)value + (DWORD)*(DWORD*)address - (DWORD)offset; + Patch((void *)address, &result, 4); + } else { + address = address + 1; + if((DWORD)*(DWORD*)address >= offset && (DWORD)*(DWORD*)address <= end) { + result = (DWORD)value + (DWORD)*(DWORD*)address - (DWORD)offset; + Patch((void *)address, &result, 4); + } else { + address = address + 1; + if((DWORD)*(DWORD*)address >= offset && (DWORD)*(DWORD*)address <= end) { + result = (DWORD)value + (DWORD)*(DWORD*)address - (DWORD)offset; + Patch((void *)address, &result, 4); + } else { + address = address + 1; + if((DWORD)*(DWORD*)address >= offset && (DWORD)*(DWORD*)address <= end) { + result = (DWORD)value + (DWORD)*(DWORD*)address - (DWORD)offset; + Patch((void *)address, &result, 4); + } else { + address = address + 1; + if((DWORD)*(DWORD*)address >= offset && (DWORD)*(DWORD*)address <= end) { + result = (DWORD)value + (DWORD)*(DWORD*)address - (DWORD)offset; + Patch((void *)address, &result, 4); + } else { + address = address + 1; + if((DWORD)*(DWORD*)address >= offset && (DWORD)*(DWORD*)address <= end) { + result = (DWORD)value + (DWORD)*(DWORD*)address - (DWORD)offset; + Patch((void *)address, &result, 4); + } + } + } + } + } + } + } + + inline static bool FileExists(const TCHAR *fileName) + { + DWORD fileAttr; + fileAttr = GetFileAttributes(fileName); + if (0xFFFFFFFF == fileAttr && GetLastError()==ERROR_FILE_NOT_FOUND) + return false; + return true; + } +}; \ No newline at end of file diff --git a/includes/IniReader.h b/includes/IniReader.h new file mode 100644 index 0000000..f9b188a --- /dev/null +++ b/includes/IniReader.h @@ -0,0 +1,311 @@ +#ifndef INIREADER_H +#define INIREADER_H +#include "ini_parser.hpp" +#include +#include + +/* +* String comparision functions, with case sensitive option +*/ + +using std::strcmp; + +inline int strcmp(const char* str1, const char* str2, bool csensitive) +{ + return (csensitive ? ::strcmp(str1, str2) : ::_stricmp(str1, str2)); +} + +inline int strcmp(const char* str1, const char* str2, size_t num, bool csensitive) +{ + return (csensitive ? ::strncmp(str1, str2, num) : ::_strnicmp(str1, str2, num)); +} + +inline int compare(const std::string& str1, const std::string& str2, bool case_sensitive) +{ + if (str1.length() == str2.length()) + return strcmp(str1.c_str(), str2.c_str(), case_sensitive); + return (str1.length() < str2.length() ? -1 : 1); +} + +inline int compare(const std::string& str1, const std::string& str2, size_t num, bool case_sensitive) +{ + if (str1.length() == str2.length()) + return strcmp(str1.c_str(), str2.c_str(), num, case_sensitive); + return (str1.length() < str2.length() ? -1 : 1); +} + +inline int compare(const char* str1, const char* str2, bool case_sensitive) +{ + return strcmp(str1, str2, case_sensitive); +} + +inline int compare(const char* str1, const char* str2, size_t num, bool case_sensitive) +{ + return strcmp(str1, str2, num, case_sensitive); +} + +inline bool starts_with(const char* str, const char* prefix, bool case_sensitive) +{ + while (*prefix) + { + bool equal; + if (case_sensitive) + equal = (*str++ == *prefix++); + else + equal = (::tolower(*str++) == ::tolower(*prefix++)); + + if (!equal) return false; + } + return true; +} + +inline bool ends_with(const char* str, const char* prefix, bool case_sensitive) +{ + auto str2 = &str[strlen(str) - 1]; + auto prefix2 = &prefix[strlen(prefix) - 1]; + + while (prefix2 >= prefix) + { + bool equal; + if (case_sensitive) + equal = (*str2-- == *prefix2--); + else + equal = (::tolower(*str2--) == ::tolower(*prefix2--)); + + if (!equal) return false; + } + return true; +} + +class CIniReader +{ +private: + std::string m_szFileName; + +public: + linb::ini data; + + CIniReader() + { + SetIniPath(""); + } + + CIniReader(char* szFileName) + { + SetIniPath(szFileName); + } + + CIniReader(const char* szFileName) + { + SetIniPath((char*)szFileName); + } + + CIniReader(std::stringstream& ini_mem) + { + data.load_file(ini_mem); + } + + bool operator==(CIniReader& ir) + { + if (data.size() != ir.data.size()) + return false; + + for (auto& section : data) + { + for (auto& key : data[section.first]) + { + if (key.second != ir.data[section.first][key.first]) + return false; + } + } + return true; + } + + bool operator!=(CIniReader& ir) + { + return !(*this == ir); + } + + bool CompareBySections(CIniReader& ir) + { + if (data.size() != ir.data.size()) + return false; + + for (auto& section : data) + { + if (ir.data.find(section.first) == ir.data.end()) + return false; + + if (section.second.size() != ir.data.find(section.first)->second.size()) + return false; + + if (section.first != ir.data.find(section.first)->first) + return false; + } + return true; + } + + bool CompareByValues(CIniReader& ir) + { + return *this == ir; + } + + const std::string& GetIniPath() + { + return m_szFileName; + } + + void SetIniPath() + { + SetIniPath(""); + } + + void SetIniPath(char* szFileName) + { + char buffer[MAX_PATH]; + HMODULE hm = NULL; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)&ends_with, &hm); + GetModuleFileNameA(hm, buffer, sizeof(buffer)); + std::string modulePath = buffer; + + if (strchr(szFileName, ':') != NULL) + { + m_szFileName = szFileName; + } + else if (std::string(szFileName).length() == 0) + { + m_szFileName = modulePath.substr(0, modulePath.find_last_of('.')) + ".ini"; + } + else + { + m_szFileName = modulePath.substr(0, modulePath.rfind('\\') + 1) + szFileName; + } + + data.load_file(m_szFileName); + } + + int ReadInteger(char* szSection, char* szKey, int iDefaultValue) + { + try { + auto str = data.get(szSection, szKey, std::to_string(iDefaultValue)); + return std::stoi(str, nullptr, starts_with(str.c_str(), "0x", false) ? 16 : 10); + } + catch (...) { + return iDefaultValue; + } + } + + float ReadFloat(char* szSection, char* szKey, float fltDefaultValue) + { + try { + return (float)atof(data.get(szSection, szKey, std::to_string(fltDefaultValue)).c_str()); + } + catch (...) { + return fltDefaultValue; + } + } + + bool ReadBoolean(char* szSection, char* szKey, bool bolDefaultValue) + { + try { + auto& config = data[szSection]; + if (config.count(szKey)) + { + if (config[szKey].size() == 1) return config[szKey].front() != '0'; + return !!compare(config[szKey], "false", false); + } + } + catch (...) { + return bolDefaultValue; + } + } + + char* ReadString(char* szSection, char* szKey, const char* szDefaultValue) + { + char* szResult = new char[255]; + try { + auto& config = data[szSection]; + if (config.count(szKey)) + { + if (config[szKey].at(0) == '\"' || config[szKey].at(0) == '\'') + config[szKey].erase(0, 1); + + if (config[szKey].at(config[szKey].size() - 1) == '\"' || config[szKey].at(config[szKey].size() - 1) == '\'') + config[szKey].erase(config[szKey].size() - 1); + + strcpy(szResult, config[szKey].c_str()); + return szResult; + } + } + catch (...) { } + strcpy(szResult, szDefaultValue); + return szResult; + } + + std::string ReadString(char* szSection, char* szKey, std::string szDefaultValue) + { + char* str = ReadString(szSection, szKey, szDefaultValue.c_str()); + std::string* szResult = new std::string(str); + return *szResult; + } + + void WriteInteger(char* szSection, char* szKey, int iValue, bool useparser = false) + { + if (useparser) + { + data.set(szSection, szKey, std::to_string(iValue)); + data.write_file(m_szFileName); + } + else + { + char szValue[255]; + _snprintf_s(szValue, 255, "%s%d", " ", iValue); + WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str()); + } + } + + void WriteFloat(char* szSection, char* szKey, float fltValue, bool useparser = false) + { + if (useparser) + { + data.set(szSection, szKey, std::to_string(fltValue)); + data.write_file(m_szFileName); + } + else + { + char szValue[255]; + _snprintf_s(szValue, 255, "%s%f", " ", fltValue); + WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str()); + } + } + + void WriteBoolean(char* szSection, char* szKey, bool bolValue, bool useparser = false) + { + if (useparser) + { + data.set(szSection, szKey, std::to_string(bolValue)); + data.write_file(m_szFileName); + } + else + { + char szValue[255]; + _snprintf_s(szValue, 255, "%s%s", " ", bolValue ? "True" : "False"); + WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str()); + } + } + + void WriteString(char* szSection, char* szKey, char* szValue, bool useparser = false) + { + if (useparser) + { + data.set(szSection, szKey, szValue); + data.write_file(m_szFileName); + } + else + { + WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str()); + } + } +}; + +#endif //INIREADER_H diff --git a/includes/ini_parser.hpp b/includes/ini_parser.hpp new file mode 100644 index 0000000..a3a0f53 --- /dev/null +++ b/includes/ini_parser.hpp @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2013-2015 Denilson das Mercês Amorim + * Copyright (c) 2017 ThirteenAG + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#ifndef LINB_INI_PARSER_HPP +#define LINB_INI_PARSER_HPP + +/* + * STL-like INI Container + */ + +#include // for std::string +#include // for std::map +#include // for std::FILE +#include // for std::find_if +#include // for std::function +#include // for std::vector +#include +#include + +namespace linb +{ + template< + class CharT = char, /* Not compatible with other type here, since we're using C streams */ + class StringType = std::basic_string, + class KeyContainer = std::map, + class SectionContainer = std::map + > class basic_ini + { + public: + typedef CharT char_type; + typedef StringType string_type; + typedef KeyContainer key_container; + typedef SectionContainer section_container; + + // Typedef container values types + typedef typename section_container::value_type value_type; + typedef typename section_container::key_type key_type; + typedef typename section_container::mapped_type mapped_type; + + // Typedef common types + typedef typename section_container::size_type size_type; + typedef typename section_container::difference_type difference_type; + + // Typedef iterators + typedef typename section_container::iterator iterator; + typedef typename section_container::const_iterator const_iterator; + typedef typename section_container::reverse_iterator reverse_iterator; + typedef typename section_container::const_reverse_iterator const_reverse_iterator; + + // typedef References and pointers + typedef typename section_container::reference reference; + typedef typename section_container::const_reference const_reference; + typedef typename section_container::pointer pointer; + typedef typename section_container::const_pointer const_pointer; + + private: + section_container data; + + public: + + basic_ini() + { } + + basic_ini(const char_type* filename) + { this->read_file(filename); } + + /* Iterator methods */ + iterator begin() + { return data.begin(); } + const_iterator begin() const + { return data.begin(); } + iterator end() + { return data.end(); } + const_iterator end() const + { return data.end(); } + const_iterator cbegin() const + { return data.cbegin(); } + const_iterator cend() const + { return data.cend(); } + + /* Reverse iterator methods */ + reverse_iterator rbegin() + { return data.rbegin(); } + const_reverse_iterator rbegin() const + { return data.rbegin(); } + reverse_iterator rend() + { return data.rend(); } + const_reverse_iterator rend() const + { return data.rend(); } + const_reverse_iterator crbegin() const + { return data.crbegin(); } + const_reverse_iterator crend() const + { return data.crend(); } + + /* Acessing index methods */ + mapped_type& operator[](const string_type& sect) + { return data[sect]; } + mapped_type& operator[](string_type&& sect) + { return data[std::forward(sect)]; } + mapped_type& at( const string_type& sect) + { return data.at(sect); } + const mapped_type& at(const string_type& sect) const + { return data.at(sect); } + + /* Capacity information */ + bool empty() const + { return data.empty(); } + size_type size() const + { return data.size(); } + size_type max_size() const + { return data.max_size(); } + + /* Modifiers */ + void clear() + { return data.clear(); } + + /* Lookup */ + size_type count(const string_type& sect) + { return data.count(sect); } + iterator find(const string_type& sect) + { return data.find(sect); } + + /* Gets a value from the specified section & key, default_value is returned if the sect & key doesn't exist */ + string_type get(const string_type& sect, const key_type& key, const string_type& default_value) + { + auto it = this->find(sect); + if(it != this->end()) + { + auto itv = it->second.find(key); + if(itv != it->second.end()) + return itv->second; + } + return default_value; + } + + /* Sets the value of a value in the ini */ + void set(const string_type& sect, const key_type& key, const string_type& value) + { + (*this)[sect][key] = value; // no emplace since overwrite! + } + + /* Too lazy to continue this container... If you need more methods, just add it */ + + + bool read_file(std::stringstream& ini_mem) + { + if(ini_mem.rdbuf()->in_avail()) + { + key_container* keys = nullptr; + string_type line; + string_type key; + string_type value; + string_type null_string; + size_type pos; + + // Trims an string + auto trim = [](string_type& s, bool trimLeft, bool trimRight) -> string_type& + { + if(s.size()) + { + // Ignore UTF-8 BOM + while(s.size() >= 3 && s[0] == (char)(0xEF) && s[1] == (char)(0xBB) && s[2] == (char)(0xBF)) + s.erase(s.begin(), s.begin() + 3); + + if(trimLeft) + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::function(::isspace)))); + if(trimRight) + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::function(::isspace))).base(), s.end()); + } + return s; + }; + + // Start parsing + while (std::getline(ini_mem, line)) + { + // Find comment and remove anything after it from the line + if((pos = line.find_first_of(';')) != line.npos) + line.erase(pos); + + if ((pos = line.rfind(" //")) != line.npos) + line.erase(pos); + + // Trim the string, and if it gets empty, skip this line + if(trim(line, true, true).empty()) + continue; + + // Find section name + if(line.front() == '[' && line.back() == ']') + { + pos = line.length() - 1; //line.find_first_of(']'); + if(pos != line.npos) + { + trim(key.assign(line, 1, pos-1), true, true); + keys = &data[std::move(key)]; // Create section + } + else + keys = nullptr; + } + else + { + // Find key and value positions + pos = line.find_first_of('='); + if(pos == line.npos) + { + // There's only the key + key = line; // No need for trim, line is already trimmed + value.clear(); + } + else + { + // There's the key and the value + trim(key.assign(line, 0, pos), false, true); // trim the right + trim(value.assign(line, pos + 1, line.npos), true, false); // trim the left + } + + // Put the key/value into the current keys object, or into the section "" if no section has been found + #if __cplusplus >= 201103L || _MSC_VER >= 1800 + (keys ? *keys : data[null_string]).emplace(std::move(key), std::move(value)); + #else + (keys ? *keys : data[null_string])[key] = value; + key.clear(); value.clear(); + #endif + } + } + + return true; + } + return false; + } + + bool read_file(const char_type* filename) + { + std::ifstream file(filename, std::ios::in); + if (file.is_open()) + { + std::stringstream ss; + ss << file.rdbuf(); + file.close(); + return read_file(ss); + } + return false; + } + + /* + * Dumps the content of this container into an ini file + */ + bool write_file(const char_type* filename) + { + if(FILE* f = fopen(filename, "w")) + { + bool first = true; + for(auto& sec : this->data) + { + fprintf(f, first? "[%s]\n" : "\n[%s]\n", sec.first.c_str()); + first = false; + for(auto& kv : sec.second) + { + if(kv.second.empty()) + fprintf(f, "%s\n", kv.first.c_str()); + else + fprintf(f, "%s = %s\n", kv.first.c_str(), kv.second.c_str()); + } + } + fclose(f); + return true; + } + return false; + } + + + /* + */ + bool load_file(const char_type* filename) + { + return read_file(filename); + } + + bool load_file(const StringType& filename) + { + return load_file(filename.c_str()); + } + + bool load_file(std::stringstream& filename) + { + return read_file(filename); + } + + bool write_file(const StringType& filename) + { + return write_file(filename.c_str()); + } + }; + + + /* Use default basic_ini + * + * Limitations: + * * Not unicode aware + * * Case sensitive + * * Sections must have unique keys + */ + typedef basic_ini<> ini; +} + +#endif + diff --git a/includes/injector/assembly.hpp b/includes/injector/assembly.hpp new file mode 100644 index 0000000..69d418a --- /dev/null +++ b/includes/injector/assembly.hpp @@ -0,0 +1,178 @@ +/* + * Injectors - Useful Assembly Stuff + * + * Copyright (C) 2012-2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once + +// This header is very restrict about compiler and architecture +#ifndef _MSC_VER // MSVC is much more flexible when we're talking about inline assembly +#error Cannot use this header in another compiler other than MSVC +#endif +#ifndef _M_IX86 +#error Supported only in x86 +#endif + +// +#include "injector.hpp" + +namespace injector +{ + struct reg_pack + { + // The ordering is very important, don't change + // The first field is the last to be pushed and first to be poped + + // PUSHFD / POPFD + uint32_t ef; + + // PUSHAD/POPAD -- must be the lastest fields (because of esp) + union + { + uint32_t arr[8]; + struct { uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; }; + }; + + enum reg_name { + reg_edi, reg_esi, reg_ebp, reg_esp, reg_ebx, reg_edx, reg_ecx, reg_eax + }; + + enum ef_flag { + carry_flag = 0, parity_flag = 2, adjust_flag = 4, zero_flag = 6, sign_flag = 7, + direction_flag = 10, overflow_flag = 11 + }; + + uint32_t& operator[](size_t i) + { return this->arr[i]; } + const uint32_t& operator[](size_t i) const + { return this->arr[i]; } + + template // bit starts from 0, use ef_flag enum + bool flag() + { + return (this->ef & (1 << bit)) != 0; + } + + bool jnb() + { + return flag() == false; + } + }; + + // Lowest level stuff (actual assembly) goes on the following namespace + // PRIVATE! Skip this, not interesting for you. + namespace injector_asm + { + // Wrapper functor, so the assembly can use some templating + template + struct wrapper + { + static void call(reg_pack* regs) + { + T fun; fun(*regs); + } + }; + + // Constructs a reg_pack and calls the wrapper functor + template // where W is of type wrapper + inline void __declspec(naked) make_reg_pack_and_call() + { + _asm + { + // Construct the reg_pack structure on the stack + pushad // Pushes general purposes registers to reg_pack + add [esp+12], 4 // Add 4 to reg_pack::esp 'cuz of our return pointer, let it be as before this func is called + pushfd // Pushes EFLAGS to reg_pack + + // Call wrapper sending reg_pack as parameter + push esp + call W::call + add esp, 4 + + // Destructs the reg_pack from the stack + sub [esp+12+4], 4 // Fix reg_pack::esp before popping it (doesn't make a difference though) (+4 because eflags) + popfd // Warning: Do not use any instruction that changes EFLAGS after this (-> sub affects EF!! <-) + popad + + // Back to normal flow + ret + } + } + }; + + + /* + * MakeInline + * Makes inline assembly (but not assembly, an actual functor of type FuncT) at address + */ + template + void MakeInline(memory_pointer_tr at) + { + typedef injector_asm::wrapper functor; + if(false) functor::call(nullptr); // To instantiate the template, if not done _asm will fail + MakeCALL(at, injector_asm::make_reg_pack_and_call); + } + + /* + * MakeInline + * Same as above, but it NOPs everything between at and end (exclusive), then performs MakeInline + */ + template + void MakeInline(memory_pointer_tr at, memory_pointer_tr end) + { + MakeRangedNOP(at, end); + MakeInline(at); + } + + /* + * MakeInline + * Same as above, but (at,end) are template parameters. + * On this case the functor can be passed as argument since there will be one func instance for each at,end not just for each FuncT + */ + template + void MakeInline(FuncT func) + { + static std::unique_ptr static_func; + static_func.reset(new FuncT(std::move(func))); + + // Encapsulates the call to static_func + struct Caps + { + void operator()(reg_pack& regs) + { (*static_func)(regs); } + }; + + // Does the actual MakeInline + return MakeInline(lazy_pointer::get(), lazy_pointer::get()); + } + + /* + * MakeInline + * Same as above, but (end) is calculated by the length of a call instruction + */ + template + void MakeInline(FuncT func) + { + return MakeInline(func); + } +}; diff --git a/includes/injector/calling.hpp b/includes/injector/calling.hpp new file mode 100644 index 0000000..0a1dd62 --- /dev/null +++ b/includes/injector/calling.hpp @@ -0,0 +1,127 @@ +/* + * Injectors - Function Calls Using Variadic Templates + * + * Copyright (C) 2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once +#include "injector.hpp" +#include +#include + +#if __cplusplus >= 201103L || _MSC_VER >= 1800 // MSVC 2013 +#else +#error "This feature is not supported on this compiler" +#endif + +namespace injector +{ + template + struct cstd; + + template + struct cstd + { + // Call function at @p returning @Ret with args @Args + static Ret call(memory_pointer_tr p, Args... a) + { + auto fn = (Ret(*)(Args...)) p.get(); + return fn(std::forward(a)...); + } + + template // Uses lazy pointer + static Ret call(Args... a) + { + return call(lazy_pointer::get(), std::forward(a)...); + } + }; + + template + struct stdcall; + + template + struct stdcall + { + // Call function at @p returning @Ret with args @Args + static Ret call(memory_pointer_tr p, Args... a) + { + auto fn = (Ret(__stdcall *)(Args...)) p.get(); + return fn(std::forward(a)...); + } + + template // Uses lazy pointer + static Ret call(Args... a) + { + return call(lazy_pointer::get(), std::forward(a)...); + } + }; + + template + struct fastcall; + + template + struct fastcall + { + // Call function at @p returning @Ret with args @Args + static Ret call(memory_pointer_tr p, Args... a) + { + auto fn = (Ret(__fastcall *)(Args...)) p.get();; + return fn(std::forward(a)...); + } + + template // Uses lazy pointer + static Ret call(Args... a) + { + return call(lazy_pointer::get(), std::forward(a)...); + } + }; + + template + struct thiscall; + + template + struct thiscall + { + // Call function at @p returning @Ret with args @Args + static Ret call(memory_pointer_tr p, Args... a) + { + auto fn = (Ret(__thiscall *)(Args...)) p.get(); + return fn(std::forward(a)...); + } + + // Call function at the index @i from the vtable of the object @a[0] + template + static Ret vtbl(Args... a) + { + auto obj = raw_ptr(std::get<0>(std::forward_as_tuple(a...))); + auto p = raw_ptr( (*obj.template get()) [i] ); + return call(p, std::forward(a)...); + } + + template // Uses lazy pointer + static Ret call(Args... a) + { + return call(lazy_pointer::get(), std::forward(a)...); + } + }; +} + diff --git a/includes/injector/gvm/gvm.hpp b/includes/injector/gvm/gvm.hpp new file mode 100644 index 0000000..27a0562 --- /dev/null +++ b/includes/injector/gvm/gvm.hpp @@ -0,0 +1,229 @@ +/* + * Injectors - Base Header + * + * Copyright (C) 2012-2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once +#include +#include +#include + +namespace injector +{ + +#if 1 // GVM and Address Translator, Not very interesting for the users, so skip reading those... + +/* + * game_version_manager + * Detects the game, the game version and the game region + * This assumes the executable is decrypted, so, Silent's ASI Loader is recommended. + */ +#ifndef INJECTOR_OWN_GVM +#ifndef INJECTOR_GVM_DUMMY +class game_version_manager +{ + public: + // Set this if you would like that MessagesBox contain PluginName as caption + const char* PluginName; + + private: + char game, region, major, minor, majorRevision, minorRevision, cracker, steam; + + public: + game_version_manager() + { + #ifdef INJECTOR_GVM_PLUGIN_NAME + PluginName = INJECTOR_GVM_PLUGIN_NAME; + #else + PluginName = "Unknown Plugin Name"; + #endif + + this->Clear(); + } + + + // Clear any information about game version + void Clear() + { + game = region = major = minor = majorRevision = minorRevision = cracker = steam = 0; + } + + // Checks if I don't know the game we are attached to + bool IsUnknown() { return game == 0; } + // Checks if this is the steam version + bool IsSteam() { return steam != 0; } + // Gets the game we are attached to (0, '3', 'V', 'S', 'I', 'E') + char GetGame() { return game; } + // Gets the region from the game we are attached to (0, 'U', 'E'); + char GetRegion() { return region; } + // Get major and minor version of the game (e.g. [major = 1, minor = 0] = 1.0) + int GetMajorVersion() { return major; } + int GetMinorVersion() { return minor; } + int GetMajorRevisionVersion() { return majorRevision; } + int GetMinorRevisionVersion() { return minorRevision; } + + bool IsHoodlum() { return cracker == 'H'; } + + // Region conditions + bool IsUS() { return region == 'U'; } + bool IsEU() { return region == 'E'; } + + // Game Conditions + bool IsIII() { return game == '3'; } + bool IsVC () { return game == 'V'; } + bool IsSA () { return game == 'S'; } + bool IsIV () { return game == 'I'; } + bool IsEFLC(){ return game == 'E'; } + + // Detects game, region and version; returns false if could not detect it + bool Detect(); + + // Gets the game version as text, the buffer must contain at least 32 bytes of space. + char* GetVersionText(char* buffer) + { + if(this->IsUnknown()) + { + strcpy(buffer, "UNKNOWN GAME"); + return buffer; + } + + const char* g = this->IsIII() ? "III" : this->IsVC() ? "VC" : this->IsSA() ? "SA" : this->IsIV() ? "IV" : this->IsEFLC() ? "EFLC" : "UNK"; + const char* r = this->IsUS()? "US" : this->IsEU()? "EURO" : "UNK_REGION"; + const char* s = this->IsSteam()? "Steam" : ""; + sprintf(buffer, "GTA %s %d.%d.%d.%d %s%s", g, major, minor, majorRevision, minorRevision, r, s); + return buffer; + } + + + public: + // Raises a error saying that you could not detect the game version + void RaiseCouldNotDetect() + { + MessageBoxA(0, + "Could not detect the game version\nContact the mod creator!", + PluginName, MB_ICONERROR + ); + } + + // Raises a error saying that the exe version is incompatible (and output the exe name) + void RaiseIncompatibleVersion() + { + char buf[128], v[32]; + sprintf(buf, + "An incompatible exe version has been detected! (%s)\nContact the mod creator!", + GetVersionText(v) + ); + MessageBoxA(0, buf, PluginName, MB_ICONERROR); + } +}; +#else // INJECTOR_GVM_DUMMY +class game_version_manager +{ + public: + bool Detect() { return true; } +}; +#endif // INJECTOR_GVM_DUMMY +#endif // INJECTOR_OWN_GVM + + +/* + * address_manager + * Address translator from 1.0 executables to other executables offsets + * Inherits from game_version_manager ;) + */ +class address_manager : public game_version_manager +{ + private: + address_manager() + { + this->Detect(); + } + + // You could implement your translator for the address your plugin uses + // If not implemented, the translator won't translate anything, just return the samething as before + #ifdef INJECTOR_GVM_HAS_TRANSLATOR + void* translator(void* p); + #else + void* translator(void* p) { return p; } + #endif + + public: + // Translates address p to the running executable pointer + void* translate(void* p) + { + return translator(p); + } + + + public: + // Address manager singleton + static address_manager& singleton() + { + static address_manager m; + return m; + } + + // Static version of translate() + static void* translate_address(void* p) + { + return singleton().translate(p); + } + + // + static void set_name(const char* modname) + { + singleton().PluginName = modname; + } + + public: + // Functors for memory translation: + + // Translates aslr translator + struct fn_mem_translator_aslr + { + void* operator()(void* p) const + { + static uintptr_t module = (uintptr_t)GetModuleHandle(NULL); + return (void*)((uintptr_t)(p)-(0x400000 - module)); + } + }; + + // Translates nothing translator + struct fn_mem_translator_nop + { + void* operator()(void* p) const + { return p; } + }; + + // Real translator + struct fn_mem_translator + { + void* operator()(void* p) const + { return translate_address(p); } + }; +}; + +#endif // #if 1 + + +} \ No newline at end of file diff --git a/includes/injector/gvm/translator.hpp b/includes/injector/gvm/translator.hpp new file mode 100644 index 0000000..c6ac430 --- /dev/null +++ b/includes/injector/gvm/translator.hpp @@ -0,0 +1,203 @@ +/* + * Injectors - Address Translation Management + * + * Copyright (C) 2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once + +#if !defined(INJECTOR_GVM_HAS_TRANSLATOR) +#error Missing INJECTOR_GVM_HAS_TRANSLATOR on compiler definitions +#endif + +/* + * This is a quick solution for address translations if you're too lazy to implement a proper address_manager::translator by yourself + * So, just call address_translator_manager::singleton().translate(p) from your address_manager::translator and that's it. + * It'll translate addresses based on 'address_translator' objects, when one gets constructed it turns into a possible translator. + * At the constructor of your derived 'address_translator' make the map object to have [addr_to_translate] = translated_addr; + * There's also the virtual method 'fallback' that will get called when the translation wasn't possible, you can do some fallback stuff here + * (such as return the pointer as is or output a error message) + */ + +#include "../injector.hpp" +#include +#include +#include + +namespace injector +{ + /* + * address_translator + * Base for an address translator + */ + class address_translator + { + private: + bool enabled; + void add(); + void remove(); + + protected: + friend class address_translator_manager; + std::map map; + + public: + address_translator() : enabled(true) + { + // Must have bounds filled with min ptr and max ptr to have search working properly + map.insert(std::make_pair(raw_ptr(0x00000000u), raw_ptr(0x00000000u))); + map.insert(std::make_pair(raw_ptr(0xffffffffu), raw_ptr(0xffffffffu))); + add(); + } + + ~address_translator() + { + remove(); + } + + virtual void* fallback(void*) const + { + return nullptr; + } + + + // Enables or disables this translator + void enable(bool enable_it) + { + if(enable_it) this->enable(); + else this->disable(); + } + + // Enables this translator + void enable() + { + this->enabled = true; + } + + // Disables this translator + void disable() + { + this->enabled = false; + } + + // Checks if this translator is enabled + bool is_enabled() const + { + return enabled; + } + }; + + /* + * address_translator_manager + * Manages the address_translator objects + */ + class address_translator_manager + { + protected: + friend class address_manager; + friend class address_translator; + + std::list translators; + + void add(const address_translator& t) + { + translators.push_front(&t); + } + + void remove(const address_translator& t) + { + translators.remove(&t); + } + + public: + // Translates the address p + void* translator(void* p); + + // Singleton object + static address_translator_manager& singleton() + { + static address_translator_manager mgr; + return mgr; + } + }; + + + + inline void* address_translator_manager::translator(void* p_) + { + static const size_t max_ptr_dist = 7; + + // Tries to find an address in a translator map + auto try_map = [](const std::map& map, memory_pointer_raw p) -> memory_pointer_raw + { + memory_pointer_raw result = nullptr; + + // Find first element in the map that is greater than or equal to p + auto it = map.lower_bound(p); + if(it != map.end()) + { + // If it's not exactly the address, get back one position on the table + if(it->first != p) --it; + + auto diff = (p - it->first).as_int(); // What's the difference between p and that address? + if(diff <= max_ptr_dist) // Could we live with this difference in hands? + result = it->second + raw_ptr(diff); // Yes, we can! + } + + return result; + }; + + + // + memory_pointer_raw result = nullptr; + + // Try to find translation for this pointer + auto& mgr = address_translator_manager::singleton().translators; + for(auto it = mgr.begin(); result == nullptr && it != mgr.end(); ++it) + { + auto& t = **it; + if(t.is_enabled()) result = try_map(t.map, p_); + } + + // If we couldn't translate the address, notify and try to fallback + if(result.is_null()) + { + for(auto it = mgr.begin(); result == nullptr && it != mgr.end(); ++it) + { + auto& t = **it; + if(t.is_enabled()) result = t.fallback(p_); + } + } + + return result.get(); + } + + inline void address_translator::add() + { + address_translator_manager::singleton().add(*this); + } + + inline void address_translator::remove() + { + address_translator_manager::singleton().remove(*this); + } +} diff --git a/includes/injector/hooking.hpp b/includes/injector/hooking.hpp new file mode 100644 index 0000000..ec7d797 --- /dev/null +++ b/includes/injector/hooking.hpp @@ -0,0 +1,687 @@ +/* + * Injectors - Classes for making your hooking life easy + * + * Copyright (C) 2013-2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once +#include "injector.hpp" +#include +#include +#include // for std::shared_ptr +#include + +namespace injector +{ + /* + * scoped_base + * Base for any scoped hooking type + * !!!! NOTICE !!!! --> Any derived which implements/reimplements restore() should implement a destructor calling it + */ + class scoped_base + { + public: + virtual ~scoped_base() {} + virtual void restore() = 0; + }; + + /* + * scoped_basic + * Base for scoped types which will need a buffer to save/restore stuff + */ + template // TODO specialize bufsize=0 to be dynamic + class scoped_basic : public scoped_base + { + private: + uint8_t buf[bufsize];// Saved content + memory_pointer_raw addr; // Data saved from this address + size_t size; // Size saved + bool saved; // Something saved? + bool vp; // Virtual protect? + + public: + + static const bool is_dynamic = false; + + // Restore the previosly saved data + // Problems may arise if someone else hooked the same place using the same method + virtual void restore() + { + #ifndef INJECTOR_SCOPED_NOSAVE_NORESTORE + if(this->saved) + { + WriteMemoryRaw(this->addr, this->buf, this->size, this->vp); + this->saved = false; + } + #endif + } + + // Save buffer at @addr with @size and virtual protect @vp + virtual void save(memory_pointer_tr addr, size_t size, bool vp) + { + #ifndef INJECTOR_SCOPED_NOSAVE_NORESTORE + assert(size <= bufsize); // Debug Safeness + this->restore(); // Restore anything we have saved + this->saved = true; // Mark that we have data save + this->addr = addr.get(); // Save address + this->size = size; // Save size + this->vp = vp; // Save virtual protect + ReadMemoryRaw(addr, buf, size, vp); // Save buffer + #endif + } + + public: + // Constructor, initialises + scoped_basic() : saved(false) + {} + + ~scoped_basic() + { + this->restore(); + } + + // No copy construction, we can't do this! Sure we can move construct :) + scoped_basic(const scoped_basic&) = delete; + scoped_basic(scoped_basic&& rhs) + { + *this = std::move(rhs); + } + + scoped_basic& operator=(const scoped_basic& rhs) = delete; + scoped_basic& operator=(scoped_basic&& rhs) + { + if(this->saved = rhs.saved) + { + assert(bufsize >= rhs.size); + + this->addr = rhs.addr; + this->size = rhs.size; + this->vp = rhs.vp; + memcpy(buf, rhs.buf, rhs.size); + + rhs.saved = false; + } + return *this; + } + }; + + /* + * RAII wrapper for memory writes + * Can save only basic and POD types + */ + template + class scoped_write : public scoped_basic + { + public: + // Save buffer at @addr with @size and virtual protect @vp and then overwrite it with @value + void write(memory_pointer_tr addr, void* value, size_t size, bool vp) + { + this->save(addr, size, vp); + return WriteMemoryRaw(addr, value, size, vp); + } + + // Save buffer at @addr with size sizeof(@value) and virtual protect @vp and then overwrite it with @value + template + void write(memory_pointer_tr addr, T value, bool vp = false) + { + this->save(addr, sizeof(T), vp); + return WriteMemory(addr, value, vp); + } + + // Constructors, move constructors, assigment operators........ + scoped_write() = default; + scoped_write(const scoped_write&) = delete; + scoped_write(scoped_write&& rhs) : scoped_basic(std::move(rhs)) {} + scoped_write& operator=(const scoped_write& rhs) = delete; + scoped_write& operator=(scoped_write&& rhs) + { scoped_basic::operator=(std::move(rhs)); return *this; } + }; + + /* + * RAII wrapper for filling + */ + template + class scoped_fill : public scoped_basic + { + public: + // Fills memory at @addr with value @value and size @size and virtual protect @vp + void fill(memory_pointer_tr addr, uint8_t value, size_t size, bool vp) + { + this->save(addr, size, vp); + return MemoryFill(addr, value, size, vp); + } + + // Constructors, move constructors, assigment operators........ + scoped_fill() = default; + scoped_fill(const scoped_fill&) = delete; + scoped_fill(scoped_fill&& rhs) : scoped_basic(std::move(rhs)) {} + scoped_fill& operator=(const scoped_fill& rhs) = delete; + scoped_fill& operator=(scoped_fill&& rhs) + { scoped_basic::operator=(std::move(rhs)); return *this; } + + scoped_fill(memory_pointer_tr addr, uint8_t value, size_t size, bool vp) + { fill(addr, value, vp); } + }; + + /* + * RAII wrapper for nopping + */ + template + class scoped_nop : public scoped_basic + { + public: + // Makes NOP at @addr with value @value and size @size and virtual protect @vp + void make_nop(memory_pointer_tr addr, size_t size = 1, bool vp = true) + { + this->save(addr, size, vp); + return MakeNOP(addr, size, vp); + } + + // Constructors, move constructors, assigment operators........ + scoped_nop() = default; + scoped_nop(const scoped_nop&) = delete; + scoped_nop(scoped_nop&& rhs) : scoped_basic(std::move(rhs)) {} + scoped_nop& operator=(const scoped_nop& rhs) = delete; + scoped_nop& operator=(scoped_nop&& rhs) + { scoped_basic::operator=(std::move(rhs)); return *this; } + + scoped_nop(memory_pointer_tr addr, size_t size = 1, bool vp = true) + { make_nop(addr, size, vp); } + }; + + /* + * RAII wrapper for MakeJMP + */ + class scoped_jmp : public scoped_basic<5> + { + public: + // Makes NOP at @addr with value @value and size @size and virtual protect @vp + memory_pointer_raw make_jmp(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) + { + this->save(at, 5, vp); + return MakeJMP(at, dest, vp); + } + + // Constructors, move constructors, assigment operators........ + scoped_jmp() = default; + scoped_jmp(const scoped_jmp&) = delete; + scoped_jmp(scoped_jmp&& rhs) : scoped_basic<5>(std::move(rhs)) {} + scoped_jmp& operator=(const scoped_jmp& rhs) = delete; + scoped_jmp& operator=(scoped_jmp&& rhs) + { scoped_basic<5>::operator=(std::move(rhs)); return *this; } + + scoped_jmp(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) + { make_jmp(at, dest, vp); } + }; + + /* + * RAII wrapper for MakeCALL + */ + class scoped_call : public scoped_basic<5> + { + public: + // Makes NOP at @addr with value @value and size @size and virtual protect @vp + memory_pointer_raw make_call(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) + { + this->save(at, 5, vp); + return MakeCALL(at, dest, vp); + } + + // Constructors, move constructors, assigment operators........ + scoped_call() = default; + scoped_call(const scoped_call&) = delete; + scoped_call(scoped_call&& rhs) : scoped_basic<5>(std::move(rhs)) {} + scoped_call& operator=(const scoped_call& rhs) = delete; + scoped_call& operator=(scoped_call&& rhs) + { scoped_basic<5>::operator=(std::move(rhs)); return *this; } + + scoped_call(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) + { make_call(at, dest, vp); } + }; + + +#if __cplusplus >= 201103L || _MSC_VER >= 1800 // C++11 or MSVC 2013 required for variadic templates + + /* + * function_hooker_manager + * Manages many function_hookers that points to the same address + * The need for this function arises because otherwise we would only be able to allow one hook per address using function_hookers + * This manager takes care of the amount of hooks placed in a particular address, calls the hooks and unhooks when necessary. + */ + template + class function_hooker_manager : protected scoped_call + { + private: + using func_type_raw = typename ToManage::func_type_raw; + using func_type = typename ToManage::func_type; + using functor_type = typename ToManage::functor_type; + using assoc_type = std::list>; + + // Only construction is allowed... by myself ofcourse... + function_hooker_manager() = default; + function_hooker_manager(const function_hooker_manager&) = delete; + function_hooker_manager(function_hooker_manager&&) = delete; + + // + func_type_raw original; // Pointer to the original function we've replaced + assoc_type assoc; // Association between owners of a hook and the hook (map) + bool has_hooked = false; // Is the hook already in place? + + // Find assoc iterator for the content owned by 'owned' + typename assoc_type::iterator find_assoc(const ToManage& owner) + { + for(auto it = assoc.begin(); it != assoc.end(); ++it) + if(it->first == &owner) return it; + return assoc.end(); + } + + // Adds a new item to the association map (or override if already in the map) + void add(const ToManage& hooker, functor_type functor) + { + auto it = find_assoc(hooker); + if(it != assoc.end()) + it->second = std::move(functor); + else + assoc.emplace_back(&hooker, std::move(functor)); + } + + public: + // Forwards the call to all the installed hooks + static Ret call_hooks(Args&... args) + { + auto& manager = *instance(); + + if(manager.assoc.size() == 0) // This may be uncommon but may happen (?), no hook installed + return manager.original(args...); + + // Functor for the original call + func_type original = [&manager](Args... args) -> Ret { + return manager.original(args...); + }; + + if(manager.assoc.size() == 1) + { + // We have only one hook, just use it directly no need to go further in complexity + auto& functor = manager.assoc.begin()->second; + return functor(std::move(original), args...); + } + else + { + // Build a serie of functors which captures the previous functor sending it to the next functor, + // that's what would happen if the hooks took place independent of the template staticness (AAAAAAA) + func_type next = std::move(original); + for(auto it = manager.assoc.begin(); it != manager.assoc.end(); ++it) + { + auto& functor = it->second; + next = [functor, next](Args... args) -> Ret + { + return functor(next, args...); + }; + } + return next(args...); + } + } + + public: + + // Installs a hook associated with the function_hooker 'hooker' which would call the specified 'functor' + // We need an auxiliar function pointer 'ptr' (to abstract calling conventions) which should forward itself to ^call_hooks + void make_call(const ToManage& hooker, functor_type functor, memory_pointer_raw ptr) + { + this->add(hooker, std::move(functor)); + + // Make sure we only hook this address for the manager once + if(!this->has_hooked) + { + // (the following cast is needed for __thiscall functions) + this->original = (func_type_raw) (void*) scoped_call::make_call(hooker.addr, ptr).get(); + this->has_hooked = true; + } + } + + // Restores the state of the call we've replaced in the game code + // All installed hooks gets uninstalled after this + void restore() + { + if(this->has_hooked) + { + this->has_hooked = false; + this->assoc.clear(); + return scoped_call::restore(); + } + } + + // Replaces the hook associated with 'from' to be associated with 'to' + // After this call the 'from' object has no association in this manager + void replace(const ToManage& from, const ToManage& to) + { + auto it = find_assoc(from); + if(it != assoc.end()) + { + auto functor = std::move(it->second); + assoc.erase(it); + this->add(to, std::move(functor)); + } + } + + // Removes the hook associated with the specified 'hooker' + // If the number of hooks reaches zero after the remotion, a restore will take place + void remove(const ToManage& hooker) + { + auto it = find_assoc(hooker); + if(it != assoc.end()) + { + assoc.erase(it); + if(assoc.size() == 0) this->restore(); + } + } + + // The instance of this specific manager + // This work as a shared pointer to avoid the static destruction of the manager even when a static function_hooker + // still wants to use it. + static std::shared_ptr instance() + { + static auto fm_ptr = std::shared_ptr(new function_hooker_manager()); + return fm_ptr; + } + + }; + + + /* + * function_hooker_base + * Base for any function_hooker, this class manages the relationship with the function hooker manager + */ + template + class function_hooker_base : public scoped_base + { + public: + static const uintptr_t addr = addr1; + + using func_type_raw = FuncType; + using func_type = std::function; + using functor_type = std::function; + using manager_type = function_hooker_manager; + + public: + // Constructors, move constructors, assigment operators........ + function_hooker_base(const function_hooker_base&) = delete; + function_hooker_base& operator=(const function_hooker_base& rhs) = delete; + + function_hooker_base() : manager(manager_type::instance()) + {} + + // + virtual ~function_hooker_base() + { + this->restore(); + } + + // The move constructor should do a replace in the manager + function_hooker_base(function_hooker_base&& rhs) + : scoped_base(std::move(rhs)), has_call(rhs.has_call), + manager(rhs.manager) // (don't move the manager!, every function_hooker should own one) + { + manager->replace(rhs, *this); + } + + // The move assignment operator should also do a replace in the manager + function_hooker_base& operator=(function_hooker_base&& rhs) + { + scoped_base::operator=(std::move(rhs)); + manager->replace(rhs, *this); + this->has_call = rhs.has_call; + this->manager = rhs.manager; // (don't move the manager! every function_hooker should own one) + return *this; + } + + // Deriveds should implement a proper make_call (yeah it's virtual so derived-deriveds can do some fest) + virtual void make_call(functor_type functor) = 0; + + // Restores the state of the call we've replaced in the game code + virtual void restore() + { + this->has_call = false; + manager->remove(*this); + } + + // Checkers whether a hook is installed + bool has_hooked() + { + return this->has_call; + } + + private: + bool has_call = false; // Has a hook installed? + std::shared_ptr manager; // **EVERY** function_hooker should have a ownership over it's manager_type + // this prevents the static destruction of the manager_type while it may be still needed. + + protected: // Forwarders to the function hooker manager + + void make_call(functor_type functor, memory_pointer_raw ptr) + { + this->has_call = true; + manager->make_call(*this, std::move(functor), ptr); + } + + static Ret call_hooks(Args&... a) + { + return manager_type::call_hooks(a...); + } + }; + + + + + /* + * function_hooker + * For standard conventions (usually __cdecl) + */ + template + struct function_hooker; + + template + class function_hooker + : public function_hooker_base + { + private: + using base = function_hooker_base; + + // The hook caller + static Ret call(Args... a) + { + return base::call_hooks(a...); + } + + public: + // Constructors, move constructors, assigment operators........ + function_hooker() = default; + function_hooker(const function_hooker&) = delete; + function_hooker(function_hooker&& rhs) : base(std::move(rhs)) {} + function_hooker& operator=(const function_hooker& rhs) = delete; + function_hooker& operator=(function_hooker&& rhs) + { base::operator=(std::move(rhs)); return *this; } + + // Makes the hook + void make_call(typename base::functor_type functor) + { + return base::make_call(std::move(functor), raw_ptr(call)); + } + }; + + + /* + * function_hooker_stdcall + * For stdcall conventions (__stdcall) + */ + template + struct function_hooker_stdcall; + + template + struct function_hooker_stdcall + : public function_hooker_base + { + private: + using base = function_hooker_base; + + // The hook caller + static Ret __stdcall call(Args... a) + { + return base::call_hooks(a...); + } + + public: + // Constructors, move constructors, assigment operators........ + function_hooker_stdcall() = default; + function_hooker_stdcall(const function_hooker_stdcall&) = delete; + function_hooker_stdcall(function_hooker_stdcall&& rhs) : base(std::move(rhs)) {} + function_hooker_stdcall& operator=(const function_hooker_stdcall& rhs) = delete; + function_hooker_stdcall& operator=(function_hooker_stdcall&& rhs) + { base::operator=(std::move(rhs)); return *this; } + + // Makes the hook + void make_call(typename base::functor_type functor) + { + return base::make_call(std::move(functor), raw_ptr(call)); + } + }; + + + /* + * function_hooker_fastcall + * For fastcall conventions (__fastcall) + */ + template + struct function_hooker_fastcall; + + template + struct function_hooker_fastcall + : public function_hooker_base + { + private: + using base = function_hooker_base; + + // The hook caller + static Ret __fastcall call(Args... a) + { + return base::call_hooks(a...); + } + + public: + // Constructors, move constructors, assigment operators........ + function_hooker_fastcall() = default; + function_hooker_fastcall(const function_hooker_fastcall&) = delete; + function_hooker_fastcall(function_hooker_fastcall&& rhs) : base(std::move(rhs)) {} + function_hooker_fastcall& operator=(const function_hooker_fastcall& rhs) = delete; + function_hooker_fastcall& operator=(function_hooker_fastcall&& rhs) + { base::operator=(std::move(rhs)); return *this; } + + // Makes the hook + void make_call(typename base::functor_type functor) + { + return base::make_call(std::move(functor), raw_ptr(call)); + } + }; + + + /* + * function_hooker_thiscall + * For thiscall conventions (__thiscall, class methods) + */ + template + struct function_hooker_thiscall; + + template + struct function_hooker_thiscall + : public function_hooker_base + { + private: + using base = function_hooker_base; + + // The hook caller + static Ret __thiscall call(Args... a) + { + return base::call_hooks(a...); + } + + public: + // Constructors, move constructors, assigment operators........ + function_hooker_thiscall() = default; + function_hooker_thiscall(const function_hooker_thiscall&) = delete; + function_hooker_thiscall(function_hooker_thiscall&& rhs) : base(std::move(rhs)) {} + function_hooker_thiscall& operator=(const function_hooker_thiscall& rhs) = delete; + function_hooker_thiscall& operator=(function_hooker_thiscall&& rhs) + { base::operator=(std::move(rhs)); return *this; } + + // Makes the hook + void make_call(typename base::functor_type functor) + { + return base::make_call(std::move(functor), raw_ptr(call)); + } + }; + + + + /******************* HELPERS ******************/ + + /* + * Adds a hook to be alive for the entire program lifetime + * That means the hook received will be alive until the program dies. + * Note: Parameter must be a rvalue + */ + template inline + T& add_static_hook(T&& hooker) + { + static std::list a; + return *a.emplace(a.end(), std::move(hooker)); + } + + /* + * Makes a hook which is alive until it gets out of scope + * 'T' must be any function_hooker object + */ + template inline + T make_function_hook(F functor) + { + T a; + a.make_call(std::move(functor)); + return a; + } + + /* + * Makes a hook which is alive for the entire lifetime of this program + * 'T' must be any function_hooker object + */ + template inline + T& make_static_hook(F functor) + { + return add_static_hook(make_function_hook(std::move(functor))); + } + + + // TODO when we have access to C++14 add a make_function_hook, make_stdcall_function_hook, and so on + // the problem behind implement it with C++11 is that lambdas cannot be generic and the first param of a hook is a functor pointing + // to the previous call pointer + +#endif + +} diff --git a/includes/injector/injector.hpp b/includes/injector/injector.hpp new file mode 100644 index 0000000..ed13fcc --- /dev/null +++ b/includes/injector/injector.hpp @@ -0,0 +1,749 @@ +/* + * Injectors - Base Header + * + * Copyright (C) 2012-2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once +#define INJECTOR_HAS_INJECTOR_HPP +#include +#include +#include +#include "gvm/gvm.hpp" +/* + The following macros (#define) are relevant on this header: + + INJECTOR_GVM_HAS_TRANSLATOR + If defined, the user should provide their own address_manager::translator function. + That function is responssible for translating a void pointer (that mayn't be an actual pointer) into an actual address. + The meaning of that void pointer will be made by YOU when you send it to the functions that receive pointers on this library. + The default translator does nothing but returns that void pointer as the address. + + INJECTOR_GVM_OWN_DETECT + If defined, the user should provide it's own game detection function thought game_version_manager::Detect + By default it provide an good detection for the Grand Theft Auto series. + + INJECTOR_GVM_PLUGIN_NAME + If this is defined, it will be used as the plugin name used at error messages. + By default it will use ""Unknown Plugin Name" + + INJECTOR_GVM_DUMMY + If defined, the game_version_manager will be a dummy object + By default it provides a nice gvm for Grand Theft Auto series + + INJECTOR_OWN_GVM + If defined, the game_version_manager should be implemented by the user before including this library. + By default it provides a nice gvm for Grand Theft Auto series +*/ +#include "gvm/gvm.hpp" + + + +namespace injector +{ + + +/* + * auto_pointer + * Casts itself to another pointer type in the lhs + */ +union auto_pointer +{ + protected: + friend union memory_pointer_tr; + template friend union basic_memory_pointer; + + void* p; + uintptr_t a; + + public: + auto_pointer() : p(0) {} + auto_pointer(const auto_pointer& x) : p(x.p) {} + explicit auto_pointer(void* x) : p(x) {} + explicit auto_pointer(uint32_t x) : a(x) {} + + bool is_null() const { return this->p != nullptr; } + + #if __cplusplus >= 201103L || _MSC_VER >= 1800 + explicit operator bool() const { return is_null(); } + #endif + + auto_pointer get() const { return *this; } + template T* get() const { return (T*) this->p; } + template T* get_raw() const { return (T*) this->p; } + + template + operator T*() const { return reinterpret_cast(p); } +}; + +/* + * basic_memory_pointer + * A memory pointer class that is capable of many operations, including address translation + * MemTranslator is the translator functor + */ +template +union basic_memory_pointer +{ + protected: + void* p; + uintptr_t a; + + // Translates address p to the running executable pointer + static auto_pointer memory_translate(void* p) + { + return auto_pointer(MemTranslator()(p)); + } + + public: + basic_memory_pointer() : p(nullptr) {} + basic_memory_pointer(std::nullptr_t) : p(nullptr) {} + basic_memory_pointer(uintptr_t x) : a(x) {} + basic_memory_pointer(const auto_pointer& x) : p(x.p) {} + basic_memory_pointer(const basic_memory_pointer& rhs) : p(rhs.p) {} + + template + basic_memory_pointer(T* x) : p((void*)x) {} + + + + + // Gets the translated pointer (plus automatic casting to lhs) + auto_pointer get() const { return memory_translate(p); } + + // Gets the translated pointer (casted to T*) + template T* get() const { return get(); } + + // Gets the raw pointer, without translation (casted to T*) + template T* get_raw() const { return auto_pointer(p); } + + // This type can get assigned from void* and uintptr_t + basic_memory_pointer& operator=(void* x) { return p = x, *this; } + basic_memory_pointer& operator=(uintptr_t x) { return a = x, *this; } + + /* Arithmetic */ + basic_memory_pointer operator+(const basic_memory_pointer& rhs) const + { return basic_memory_pointer(this->a + rhs.a); } + + basic_memory_pointer operator-(const basic_memory_pointer& rhs) const + { return basic_memory_pointer(this->a - rhs.a); } + + basic_memory_pointer operator*(const basic_memory_pointer& rhs) const + { return basic_memory_pointer(this->a * rhs.a); } + + basic_memory_pointer operator/(const basic_memory_pointer& rhs) const + { return basic_memory_pointer(this->a / rhs.a); } + + + /* Comparision */ + bool operator==(const basic_memory_pointer& rhs) const + { return this->a == rhs.a; } + + bool operator!=(const basic_memory_pointer& rhs) const + { return this->a != rhs.a; } + + bool operator<(const basic_memory_pointer& rhs) const + { return this->a < rhs.a; } + + bool operator<=(const basic_memory_pointer& rhs) const + { return this->a <= rhs.a; } + + bool operator>(const basic_memory_pointer& rhs) const + { return this->a > rhs.a; } + + bool operator>=(const basic_memory_pointer& rhs) const + { return this->a >=rhs.a; } + + bool is_null() const { return this->p == nullptr; } + uintptr_t as_int() const { return this->a; } // does not perform translation + + + +#if __cplusplus >= 201103L || _MSC_VER >= 1800 // MSVC 2013 + /* Conversion to other types */ + explicit operator uintptr_t() const + { return this->a; } // does not perform translation + explicit operator bool() const + { return this->p != nullptr; } +#else + //operator bool() -------------- Causes casting problems because of implicitness, use !is_null() + //{ return this->p != nullptr; } +#endif + +}; + + // Typedefs including memory translator for the above type +typedef basic_memory_pointer memory_pointer; +typedef basic_memory_pointer memory_pointer_raw; +typedef basic_memory_pointer memory_pointer_aslr; + + + +/* + * memory_pointer_tr + * Stores a basic_memory_pointer as a raw pointer from translated pointer + */ +union memory_pointer_tr +{ + protected: + void* p; + uintptr_t a; + + public: + template + memory_pointer_tr(const basic_memory_pointer& ptr) + : p(ptr.get()) + {} // Constructs from a basic_memory_pointer + + memory_pointer_tr(const auto_pointer& ptr) + : p(ptr.p) + {} // Constructs from a auto_pointer, probably comming from basic_memory_pointer::get + + memory_pointer_tr(const memory_pointer_tr& rhs) + : p(rhs.p) + {} // Constructs from my own type, copy constructor + + memory_pointer_tr(uintptr_t x) + : p(memory_pointer(x).get()) + {} // Constructs from a integer, translating the address + + memory_pointer_tr(void* x) + : p(memory_pointer(x).get()) + {} // Constructs from a void pointer, translating the address + + // Just to be method-compatible with basic_memory_pointer ... + auto_pointer get() { return auto_pointer(p); } + template T* get() { return get(); } + template T* get_raw() { return get(); } + + memory_pointer_tr operator+(const uintptr_t& rhs) const + { return memory_pointer_raw(this->a + rhs); } + + memory_pointer_tr operator-(const uintptr_t& rhs) const + { return memory_pointer_raw(this->a - rhs); } + + memory_pointer_tr operator*(const uintptr_t& rhs) const + { return memory_pointer_raw(this->a * rhs); } + + memory_pointer_tr operator/(const uintptr_t& rhs) const + { return memory_pointer_raw(this->a / rhs); } + + bool is_null() const { return this->p == nullptr; } + uintptr_t as_int() const { return this->a; } + +#if __cplusplus >= 201103L + explicit operator uintptr_t() const + { return this->a; } +#else +#endif + +}; + + + + + + + +/* + * ProtectMemory + * Makes the address @addr have a protection of @protection + */ +inline bool ProtectMemory(memory_pointer_tr addr, size_t size, DWORD protection) +{ + return VirtualProtect(addr.get(), size, protection, &protection) != 0; +} + +/* + * UnprotectMemory + * Unprotect the memory at @addr with size @size so it have all accesses (execute, read and write) + * Returns the old protection to out_oldprotect + */ +inline bool UnprotectMemory(memory_pointer_tr addr, size_t size, DWORD& out_oldprotect) +{ + return VirtualProtect(addr.get(), size, PAGE_EXECUTE_READWRITE, &out_oldprotect) != 0; +} + +/* + * scoped_unprotect + * RAII wrapper for UnprotectMemory + * On construction unprotects the memory, on destruction reprotects the memory + */ +struct scoped_unprotect +{ + memory_pointer_raw addr; + size_t size; + DWORD dwOldProtect; + bool bUnprotected; + + scoped_unprotect(memory_pointer_tr addr, size_t size) + { + if(size == 0) bUnprotected = false; + else bUnprotected = UnprotectMemory(this->addr = addr.get(), this->size = size, dwOldProtect); + } + + ~scoped_unprotect() + { + if(bUnprotected) ProtectMemory(this->addr.get(), this->size, this->dwOldProtect); + } +}; + + + + + + + + +/* + * WriteMemoryRaw + * Writes into memory @addr the content of @value with a sizeof @size + * Does memory unprotection if @vp is true + */ +inline void WriteMemoryRaw(memory_pointer_tr addr, void* value, size_t size, bool vp) +{ + scoped_unprotect xprotect(addr, vp? size : 0); + memcpy(addr.get(), value, size); +} + +/* + * ReadMemoryRaw + * Reads the memory at @addr with a sizeof @size into address @ret + * Does memory unprotection if @vp is true + */ +inline void ReadMemoryRaw(memory_pointer_tr addr, void* ret, size_t size, bool vp) +{ + scoped_unprotect xprotect(addr, vp? size : 0); + memcpy(ret, addr.get(), size); +} + +/* + * MemoryFill + * Fills the memory at @addr with the byte @value doing it @size times + * Does memory unprotection if @vp is true + */ +inline void MemoryFill(memory_pointer_tr addr, uint8_t value, size_t size, bool vp) +{ + scoped_unprotect xprotect(addr, vp? size : 0); + memset(addr.get(), value, size); +} + +/* + * WriteObject + * Assigns the object @value into the same object type at @addr + * Does memory unprotection if @vp is true + */ +template +inline T& WriteObject(memory_pointer_tr addr, const T& value, bool vp = false) +{ + scoped_unprotect xprotect(addr, vp? sizeof(value) : 0); + return (*addr.get() = value); +} + +/* + * ReadObject + * Assigns the object @value with the value of the same object type at @addr + * Does memory unprotection if @vp is true + */ +template +inline T& ReadObject(memory_pointer_tr addr, T& value, bool vp = false) +{ + scoped_unprotect xprotect(addr, vp? sizeof(value) : 0); + return (value = *addr.get()); +} + + +/* + * WriteMemory + * Writes the object of type T into the address @addr + * Does memory unprotection if @vp is true + */ +template +inline void WriteMemory(memory_pointer_tr addr, T value, bool vp = false) +{ + WriteObject(addr, value, vp); +} + +/* + * ReadMemory + * Reads the object type T at address @addr + * Does memory unprotection if @vp is true + */ +template +inline T ReadMemory(memory_pointer_tr addr, bool vp = false) +{ + T value; + return ReadObject(addr, value, vp); +} + +/* + * AdjustPointer + * Searches in the range [@addr, @addr + @max_search] for a pointer in the range [@default_base, @default_end] and replaces + * it with the proper offset in the pointer @replacement_base. + * Does memory unprotection if @vp is true. + */ + inline memory_pointer_raw AdjustPointer(memory_pointer_tr addr, + memory_pointer_raw replacement_base, memory_pointer_tr default_base, memory_pointer_tr default_end, + size_t max_search = 8, bool vp = true) + { + scoped_unprotect xprotect(addr, vp? max_search + sizeof(void*) : 0); + for(size_t i = 0; i < max_search; ++i) + { + memory_pointer_raw ptr = ReadMemory(addr + i); + if(ptr >= default_base.get() && ptr <= default_end.get()) + { + auto result = replacement_base + (ptr - default_base.get()); + WriteMemory(addr + i, result.get()); + return result; + } + } + return nullptr; + } + + + + + + +/* + * GetAbsoluteOffset + * Gets absolute address based on relative offset @rel_value from instruction that ends at @end_of_instruction + */ +inline memory_pointer_raw GetAbsoluteOffset(int rel_value, memory_pointer_tr end_of_instruction) +{ + return end_of_instruction.get() + rel_value; +} + +/* + * GetRelativeOffset + * Gets relative offset based on absolute address @abs_value for instruction that ends at @end_of_instruction + */ +inline int GetRelativeOffset(memory_pointer_tr abs_value, memory_pointer_tr end_of_instruction) +{ + return uintptr_t(abs_value.get() - end_of_instruction.get()); +} + +/* + * ReadRelativeOffset + * Reads relative offset from address @at + */ +inline memory_pointer_raw ReadRelativeOffset(memory_pointer_tr at, size_t sizeof_addr = 4, bool vp = true) +{ + switch(sizeof_addr) + { + case 1: return (GetAbsoluteOffset(ReadMemory (at, vp), at+sizeof_addr)); + case 2: return (GetAbsoluteOffset(ReadMemory(at, vp), at+sizeof_addr)); + case 4: return (GetAbsoluteOffset(ReadMemory(at, vp), at+sizeof_addr)); + } + return nullptr; +} + +/* + * MakeRelativeOffset + * Writes relative offset into @at based on absolute destination @dest + */ +inline void MakeRelativeOffset(memory_pointer_tr at, memory_pointer_tr dest, size_t sizeof_addr = 4, bool vp = true) +{ + switch(sizeof_addr) + { + case 1: WriteMemory (at, static_cast (GetRelativeOffset(dest, at+sizeof_addr)), vp); + case 2: WriteMemory(at, static_cast(GetRelativeOffset(dest, at+sizeof_addr)), vp); + case 4: WriteMemory(at, static_cast(GetRelativeOffset(dest, at+sizeof_addr)), vp); + } +} + +/* + * GetBranchDestination + * Gets the destination of a branch instruction at address @at + * *** Works only with JMP and CALL for now *** + */ +inline memory_pointer_raw GetBranchDestination(memory_pointer_tr at, bool vp = true) +{ + switch(ReadMemory(at, vp)) + { + // We need to handle other instructions (and prefixes) later... + case 0xE8: // call rel + case 0xE9: // jmp rel + return ReadRelativeOffset(at + 1, 4, vp); + + case 0xFF: + switch(ReadMemory(at + 1, vp)) + { + case 0x15: // call dword ptr [addr] + case 0x25: // jmp dword ptr [addr] + return *(ReadMemory(at + 2, vp)); + } + break; + } + return nullptr; +} + +/* + * MakeJMP + * Creates a JMP instruction at address @at that jumps into address @dest + * If there was already a branch instruction there, returns the previosly destination of the branch + */ +inline memory_pointer_raw MakeJMP(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) +{ + auto p = GetBranchDestination(at, vp); + WriteMemory(at, 0xE9, vp); + MakeRelativeOffset(at+1, dest, 4, vp); + return p; +} + +/* + * MakeCALL + * Creates a CALL instruction at address @at that jumps into address @dest + * If there was already a branch instruction there, returns the previosly destination of the branch + */ +inline memory_pointer_raw MakeCALL(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) +{ + auto p = GetBranchDestination(at, vp); + WriteMemory(at, 0xE8, vp); + MakeRelativeOffset(at+1, dest, 4, vp); + return p; +} + +/* + * MakeJA + * Creates a JA instruction at address @at that jumps if above into address @dest + * If there was already a branch instruction there, returns the previosly destination of the branch + */ +inline void MakeJA(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) +{ + WriteMemory(at, 0x87F0, vp); + MakeRelativeOffset(at+2, dest, 4, vp); +} + +/* + * MakeNOP + * Creates a bunch of NOP instructions at address @at + */ +inline void MakeNOP(memory_pointer_tr at, size_t count = 1, bool vp = true) +{ + MemoryFill(at, 0x90, count, vp); +} + +/* + * MakeRangedNOP + * Creates a bunch of NOP instructions at address @at until address @until + */ +inline void MakeRangedNOP(memory_pointer_tr at, memory_pointer_tr until, bool vp = true) +{ + return MakeNOP(at, size_t(until.get_raw() - at.get_raw()), vp); +} + + +/* + * MakeRET + * Creates a RET instruction at address @at popping @pop values from the stack + * If @pop is equal to 0 it will use the 1 byte form of the instruction + */ +inline void MakeRET(memory_pointer_tr at, uint16_t pop = 0, bool vp = true) +{ + WriteMemory(at, pop? 0xC2 : 0xC3, vp); + if(pop) WriteMemory(at+1, pop, vp); +} + + + + + +/* + * lazy_pointer + * Lazy pointer, where it's final value will get evaluated only once when finally needed. + */ + template + struct lazy_pointer + { + public: + // Returns the final raw pointer + static auto_pointer get() + { + return xget().get(); + } + + template + static T* get() + { + return get().get(); + } + + private: + // Returns the final pointer + static memory_pointer_raw xget() + { + static void* ptr = nullptr; + if(!ptr) ptr = memory_pointer(addr).get(); + return memory_pointer_raw(ptr); + } +}; + + /* + * lazy_object + * Lazy object, where it's final object will get evaluated only once when finally needed. + */ + template + struct lazy_object + { + static T& get() + { + static T data; + static bool has_data = false; + if(!has_data) + { + ReadObject(addr, data, true); + has_data = true; + } + return data; + } + }; + + + /* + Helpers + */ + + template +inline memory_pointer mem_ptr(T p) +{ + return memory_pointer(p); +} + +template +inline memory_pointer_raw raw_ptr(T p) +{ + return memory_pointer_raw(p); +} + +template +inline memory_pointer_raw raw_ptr(basic_memory_pointer p) +{ + return raw_ptr(p.get()); +} + +template +inline memory_pointer_raw lazy_ptr() +{ + return lazy_pointer::get(); +} + +template +inline memory_pointer_aslr aslr_ptr(T p) +{ + return memory_pointer_aslr(p); +} + + + + + + +#ifndef INJECTOR_GVM_OWN_DETECT // Should we implement our detection method? + +// Detects game, region and version; returns false if could not detect it +inline bool game_version_manager::Detect() +{ + // Cleanup data + this->Clear(); + + // Find NT header + uintptr_t base = (uintptr_t) GetModuleHandleA(NULL); + IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)(base); + IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(base + dos->e_lfanew); + + // Look for game and version thought the entry-point + // Thanks to Silent for many of the entry point offsets + switch (base + nt->OptionalHeader.AddressOfEntryPoint + (0x400000 - base)) + { + case 0x5C1E70: // GTA III 1.0 + game = '3', major = 1, minor = 0, region = 0, steam = false; + return true; + + case 0x5C2130: // GTA III 1.1 + game = '3', major = 1, minor = 1, region = 0, steam = false; + return true; + + case 0x5C6FD0: // GTA III 1.1 (Cracked Steam Version) + case 0x9912ED: // GTA III 1.1 (Encrypted Steam Version) + game = '3', major = 1, minor = 1, region = 0, steam = true; + return true; + + case 0x667BF0: // GTA VC 1.0 + game = 'V', major = 1, minor = 0, region = 0, steam = false; + return true; + + case 0x667C40: // GTA VC 1.1 + game = 'V', major = 1, minor = 1, region = 0, steam = false; + return true; + + case 0x666BA0: // GTA VC 1.1 (Cracked Steam Version) + case 0xA402ED: // GTA VC 1.1 (Encrypted Steam Version) + game = 'V', major = 1, minor = 1, region = 0, steam = true; + return true; + + case 0x82457C: // GTA SA 1.0 US Cracked + case 0x824570: // GTA SA 1.0 US Compact + game = 'S', major = 1, minor = 0, region = 'U', steam = false; + cracker = injector::ReadMemory(raw_ptr(0x406A20), true) == 0xE9? 'H' : 0; + return true; + + case 0x8245BC: // GTA SA 1.0 EU Cracked (??????) + case 0x8245B0: // GTA SA 1.0 EU Cracked + game = 'S', major = 1, minor = 0, region = 'E', steam = false; + cracker = injector::ReadMemory(raw_ptr(0x406A20), true) == 0xE9? 'H' : 0; // just to say 'securom' + return true; + + case 0x8252FC: // GTA SA 1.1 US Cracked + game = 'S', major = 1, minor = 1, region = 'U', steam = false; + return true; + + case 0x82533C: // GTA SA 1.1 EU Cracked + game = 'S', major = 1, minor = 1, region = 'E', steam = false; + return true; + + case 0x85EC4A: // GTA SA 3.0 (Cracked Steam Version) + case 0xD3C3DB: // GTA SA 3.0 (Encrypted Steam Version) + game = 'S', major = 3, minor = 0, region = 0, steam = true; + return true; + + case 0xC965AD: // GTA IV 1.0.0.4 US + game = 'I', major = 1, minor = 0, majorRevision = 0, minorRevision = 4, region = 'U', steam = false; + return true; + + case 0xD0D011: // GTA IV 1.0.0.7 US + game = 'I', major = 1, minor = 0, majorRevision = 0, minorRevision = 7, region = 'U', steam = false; + return true; + + case 0xD0AF06: // GTA EFLC 1.1.2.0 US + game = 'E', major = 1, minor = 1, majorRevision = 2, minorRevision = 0, region = 'U', steam = false; + return true; + + default: + return false; + } +} + +#endif + + +} // namespace + diff --git a/includes/injector/utility.hpp b/includes/injector/utility.hpp new file mode 100644 index 0000000..0a5eafc --- /dev/null +++ b/includes/injector/utility.hpp @@ -0,0 +1,56 @@ +/* + * Injectors - Utility / Helpers + * + * Copyright (C) 2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once + +namespace injector +{ + template + T return_value() + { + return value; + } + + template + void* force_ptr(const T& fun) + { + auto ptr = fun; + return *(void**)&ptr; + } + + + // Helper structure to help calling back what was there before a hook + // e.g. hb.fun = MakeCALL(0x0, raw_ptr(my_hook)); + template + struct hook_back + { + typedef FuncType func_type; + + func_type fun; + + hook_back() : fun(nullptr) + {} + }; +}; diff --git a/includes/stdafx.h b/includes/stdafx.h new file mode 100644 index 0000000..f3a0737 --- /dev/null +++ b/includes/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + + + +// TODO: reference additional headers your program requires here