From 295eceece371b516e771de93b6127bf728999483 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 26 Jul 2014 19:24:00 -0400 Subject: initial commit so far source analysis is working. =) --- .hgignore | 9 + build.gradle | 61 ++ libs/procyon-decompiler-0.5.25.jar | Bin 0 -> 1808653 bytes license.txt | 674 +++++++++++++++++++++ src/cuchaz/enigma/ClassFile.java | 45 ++ src/cuchaz/enigma/Deobfuscator.java | 109 ++++ src/cuchaz/enigma/Main.java | 63 ++ src/cuchaz/enigma/Util.java | 65 ++ src/cuchaz/enigma/analysis/Analyzer.java | 252 ++++++++ src/cuchaz/enigma/analysis/ClassNameIndex.java | 19 + .../enigma/analysis/JavaSourceFromString.java | 31 + src/cuchaz/enigma/analysis/Lexer.java | 41 ++ src/cuchaz/enigma/analysis/SourceIndex.java | 57 ++ src/cuchaz/enigma/analysis/SourcedAst.java | 111 ++++ src/cuchaz/enigma/gui/BoxHighlightPainter.java | 54 ++ src/cuchaz/enigma/gui/ClassSelectionHandler.java | 18 + src/cuchaz/enigma/gui/Gui.java | 163 +++++ .../gui/ObfuscatedClassListCellRenderer.java | 40 ++ src/cuchaz/enigma/gui/SourceFormatter.java | 62 ++ src/cuchaz/enigma/mapping/ArgumentEntry.java | 88 +++ src/cuchaz/enigma/mapping/ClassEntry.java | 67 ++ src/cuchaz/enigma/mapping/FieldEntry.java | 76 +++ src/cuchaz/enigma/mapping/MethodEntry.java | 88 +++ 23 files changed, 2193 insertions(+) create mode 100644 .hgignore create mode 100644 build.gradle create mode 100644 libs/procyon-decompiler-0.5.25.jar create mode 100644 license.txt create mode 100644 src/cuchaz/enigma/ClassFile.java create mode 100644 src/cuchaz/enigma/Deobfuscator.java create mode 100644 src/cuchaz/enigma/Main.java create mode 100644 src/cuchaz/enigma/Util.java create mode 100644 src/cuchaz/enigma/analysis/Analyzer.java create mode 100644 src/cuchaz/enigma/analysis/ClassNameIndex.java create mode 100644 src/cuchaz/enigma/analysis/JavaSourceFromString.java create mode 100644 src/cuchaz/enigma/analysis/Lexer.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndex.java create mode 100644 src/cuchaz/enigma/analysis/SourcedAst.java create mode 100644 src/cuchaz/enigma/gui/BoxHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/ClassSelectionHandler.java create mode 100644 src/cuchaz/enigma/gui/Gui.java create mode 100644 src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java create mode 100644 src/cuchaz/enigma/gui/SourceFormatter.java create mode 100644 src/cuchaz/enigma/mapping/ArgumentEntry.java create mode 100644 src/cuchaz/enigma/mapping/ClassEntry.java create mode 100644 src/cuchaz/enigma/mapping/FieldEntry.java create mode 100644 src/cuchaz/enigma/mapping/MethodEntry.java diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..7e666d25 --- /dev/null +++ b/.hgignore @@ -0,0 +1,9 @@ + +syntax: regexp +^\.classpath$ +syntax: regexp +^\.project$ +syntax: regexp +^\.gradle$ +syntax: regexp +^\.settings$ \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..f95f1052 --- /dev/null +++ b/build.gradle @@ -0,0 +1,61 @@ + +apply plugin: "java" +apply plugin: "eclipse" +apply plugin: "maven" + +sourceCompatibility = 1.6 +targetCompatibility = 1.6 + +group = "com.cuchazinteractive" +archivesBaseName = "enigma" +version = "0.1" + +sourceSets +{ + main + { + java + { + srcDir "src" + } + resources + { + srcDir "assets" + } + } + test + { + java + { + srcDir "test" + } + resources + { + srcDir "assets" + } + } +} + +repositories +{ + mavenCentral() +} + +dependencies +{ + compile files( "${System.properties['java.home']}/../lib/tools.jar" ) + compile fileTree( dir: "libs", include: "*.jar" ) + compile "de.sciss:jsyntaxpane:1.0.0" + compile "com.google.guava:guava:17.0" + compile "org.javassist:javassist:3.18.1-GA" + + testCompile "junit:junit:4.11" +} + +uploadArchives +{ + repositories.mavenDeployer + { + repository( url: "file://${project.projectDir}/../maven" ) + } +} diff --git a/libs/procyon-decompiler-0.5.25.jar b/libs/procyon-decompiler-0.5.25.jar new file mode 100644 index 00000000..04dda91d Binary files /dev/null and b/libs/procyon-decompiler-0.5.25.jar differ diff --git a/license.txt b/license.txt new file mode 100644 index 00000000..20d40b6b --- /dev/null +++ b/license.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/src/cuchaz/enigma/ClassFile.java b/src/cuchaz/enigma/ClassFile.java new file mode 100644 index 00000000..221a119e --- /dev/null +++ b/src/cuchaz/enigma/ClassFile.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.util.regex.Pattern; + +public class ClassFile +{ + private static Pattern m_obfuscatedClassPattern; + + static + { + m_obfuscatedClassPattern = Pattern.compile( "^[a-z]+$" ); + } + + private String m_name; + + public ClassFile( String name ) + { + m_name = name; + } + + public String getName( ) + { + return m_name; + } + + public boolean isObfuscated( ) + { + return m_obfuscatedClassPattern.matcher( m_name ).matches(); + } + + public String getPath( ) + { + return m_name.replace( ".", "/" ) + ".class"; + } +} diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java new file mode 100644 index 00000000..83a21719 --- /dev/null +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import com.strobel.assembler.metadata.JarTypeLoader; +import com.strobel.decompiler.Decompiler; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; + +public class Deobfuscator +{ + private File m_file; + private JarFile m_jar; + private DecompilerSettings m_settings; + + private static Comparator m_obfuscatedClassSorter; + + static + { + m_obfuscatedClassSorter = new Comparator( ) + { + @Override + public int compare( ClassFile a, ClassFile b ) + { + if( a.getName().length() != b.getName().length() ) + { + return a.getName().length() - b.getName().length(); + } + + return a.getName().compareTo( b.getName() ); + } + }; + } + + public Deobfuscator( File file ) + throws IOException + { + m_file = file; + m_jar = new JarFile( m_file ); + m_settings = DecompilerSettings.javaDefaults(); + m_settings.setTypeLoader( new JarTypeLoader( m_jar ) ); + m_settings.setForceExplicitImports( true ); + m_settings.setShowSyntheticMembers( true ); + } + + public List getObfuscatedClasses( ) + { + List classes = new ArrayList(); + Enumeration entries = m_jar.entries(); + while( entries.hasMoreElements() ) + { + JarEntry entry = entries.nextElement(); + + // get the class name + String className = toClassName( entry.getName() ); + if( className == null ) + { + continue; + } + + ClassFile classFile = new ClassFile( className ); + if( classFile.isObfuscated() ) + { + classes.add( classFile ); + } + } + Collections.sort( classes, m_obfuscatedClassSorter ); + return classes; + } + + // TODO: could go somewhere more generic + private static String toClassName( String fileName ) + { + final String suffix = ".class"; + + if( !fileName.endsWith( suffix ) ) + { + return null; + } + + return fileName.substring( 0, fileName.length() - suffix.length() ).replace( "/", "." ); + } + + public String getSource( final ClassFile classFile ) + { + StringWriter buf = new StringWriter(); + Decompiler.decompile( classFile.getName(), new PlainTextOutput( buf ), m_settings ); + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java new file mode 100644 index 00000000..e08c16ed --- /dev/null +++ b/src/cuchaz/enigma/Main.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.File; + +import cuchaz.enigma.analysis.Analyzer; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.gui.ClassSelectionHandler; +import cuchaz.enigma.gui.Gui; + +public class Main +{ + public static void main( String[] args ) + throws Exception + { + startGui(); + } + + private static void startGui( ) + throws Exception + { + final Gui gui = new Gui(); + + // settings + final File jarFile = new File( "/home/jeff/.minecraft/versions/1.7.10/1.7.10.jar" ); + gui.setTitle( jarFile.getName() ); + + // init the deobfuscator + final Deobfuscator deobfuscator = new Deobfuscator( jarFile ); + gui.setObfClasses( deobfuscator.getObfuscatedClasses() ); + + // handle events + gui.setClassSelectionHandler( new ClassSelectionHandler( ) + { + @Override + public void classSelected( final ClassFile classFile ) + { + gui.setSource( "(deobfuscating...)" ); + + // run the deobfuscator in a separate thread so we don't block the GUI event queue + new Thread( ) + { + @Override + public void run( ) + { + String source = deobfuscator.getSource( classFile ); + SourceIndex index = Analyzer.analyze( classFile.getName(), source ); + gui.setSource( source, index ); + } + }.start(); + } + } ); + } +} diff --git a/src/cuchaz/enigma/Util.java b/src/cuchaz/enigma/Util.java new file mode 100644 index 00000000..c51eb621 --- /dev/null +++ b/src/cuchaz/enigma/Util.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.Closeable; +import java.io.IOException; +import java.util.jar.JarFile; + + +public class Util +{ + public static int combineHashesOrdered( Object ... objs ) + { + final int prime = 67; + int result = 1; + for( Object obj : objs ) + { + result *= prime; + if( obj != null ) + { + result += obj.hashCode(); + } + } + return result; + } + + public static void closeQuietly( Closeable closeable ) + { + if( closeable != null ) + { + try + { + closeable.close(); + } + catch( IOException ex ) + { + // just ignore any further exceptions + } + } + } + + public static void closeQuietly( JarFile jarFile ) + { + // silly library should implement Closeable... + if( jarFile != null ) + { + try + { + jarFile.close(); + } + catch( IOException ex ) + { + // just ignore any further exceptions + } + } + } +} diff --git a/src/cuchaz/enigma/analysis/Analyzer.java b/src/cuchaz/enigma/analysis/Analyzer.java new file mode 100644 index 00000000..1cdabe75 --- /dev/null +++ b/src/cuchaz/enigma/analysis/Analyzer.java @@ -0,0 +1,252 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.IOException; +import java.util.Arrays; + +import javax.tools.JavaCompiler; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + +import jsyntaxpane.Token; +import jsyntaxpane.TokenType; + +import com.sun.source.tree.ArrayTypeTree; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.PrimitiveTypeTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.JavacTask; +import com.sun.source.util.TreeScanner; +import com.sun.source.util.Trees; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +class TreeVisitor extends TreeScanner +{ + private SourceIndex m_index; + + public TreeVisitor( SourceIndex index ) + { + m_index = index; + } + + @Override + public CompilationUnitTree visitClass( ClassTree classTree, SourcedAst ast ) + { + ClassEntry classEntry = indexClass( classTree, ast ); + + // look at the class members + for( Tree memberTree : classTree.getMembers() ) + { + if( memberTree.getKind() == Kind.VARIABLE ) + { + indexField( (VariableTree)memberTree, ast, classEntry ); + } + else if( memberTree.getKind() == Kind.METHOD ) + { + MethodTree methodTree = (MethodTree)memberTree; + MethodEntry methodEntry = indexMethod( methodTree, ast, classEntry ); + + // look at method arguments + int argNum = 0; + for( VariableTree variableTree : methodTree.getParameters() ) + { + indexArgument( variableTree, ast, methodEntry, argNum++ ); + } + } + } + + return super.visitClass( classTree, ast ); + } + + private ClassEntry indexClass( ClassTree classTree, SourcedAst ast ) + { + // build the entry + ClassEntry entry = new ClassEntry( ast.getFullClassName( classTree.getSimpleName().toString() ) ); + + // lex the source at this tree node + for( Token token : new Lexer( ast.getSource( classTree ).toString() ) ) + { + // scan until we get the first identifier + if( token.type == TokenType.IDENTIFIER ) + { + m_index.add( entry, offsetToken( token, ast.getStart( classTree ) ) ); + break; + } + } + + return entry; + } + + private FieldEntry indexField( VariableTree variableTree, SourcedAst ast, ClassEntry classEntry ) + { + // build the entry + FieldEntry entry = new FieldEntry( classEntry, variableTree.getName().toString() ); + + // lex the source at this tree node + Lexer lexer = new Lexer( ast.getSource( variableTree ).toString() ); + for( Token token : lexer ) + { + // scan until we find an identifier that matches the field name + if( token.type == TokenType.IDENTIFIER && lexer.getText( token ).equals( entry.getName() ) ) + { + m_index.add( entry, offsetToken( token, ast.getStart( variableTree ) ) ); + break; + } + } + + return entry; + } + + private MethodEntry indexMethod( MethodTree methodTree, SourcedAst ast, ClassEntry classEntry ) + { + // build the entry + StringBuilder signature = new StringBuilder(); + signature.append( "(" ); + for( VariableTree variableTree : methodTree.getParameters() ) + { + signature.append( toJvmType( variableTree.getType(), ast ) ); + } + signature.append( ")" ); + if( methodTree.getReturnType() != null ) + { + signature.append( toJvmType( methodTree.getReturnType(), ast ) ); + } + else + { + signature.append( "V" ); + } + MethodEntry entry = new MethodEntry( classEntry, methodTree.getName().toString(), signature.toString() ); + + // lex the source at this tree node + Lexer lexer = new Lexer( ast.getSource( methodTree ).toString() ); + for( Token token : lexer ) + { + // scan until we find an identifier that matches the method name + if( token.type == TokenType.IDENTIFIER && lexer.getText( token ).equals( entry.getName() ) ) + { + m_index.add( entry, offsetToken( token, ast.getStart( methodTree ) ) ); + break; + } + } + + return entry; + } + + private void indexArgument( VariableTree variableTree, SourcedAst ast, MethodEntry methodEntry, int index ) + { + System.out.println( "\tFound argument: " + variableTree.getName() ); + + // build the entry + ArgumentEntry entry = new ArgumentEntry( methodEntry, index, variableTree.getName().toString() ); + + // lex the source at this tree node + Lexer lexer = new Lexer( ast.getSource( variableTree ).toString() ); + for( Token token : lexer ) + { + // scan until we find an identifier that matches the variable name + if( token.type == TokenType.IDENTIFIER && lexer.getText( token ).equals( entry.getName() ) ) + { + m_index.add( entry, offsetToken( token, ast.getStart( variableTree ) ) ); + break; + } + } + } + + private Token offsetToken( Token in, int offset ) + { + return new Token( in.type, in.start + offset, in.length ); + } + + private String toJvmType( Tree tree, SourcedAst ast ) + { + switch( tree.getKind() ) + { + case PRIMITIVE_TYPE: + { + PrimitiveTypeTree primitiveTypeTree = (PrimitiveTypeTree)tree; + switch( primitiveTypeTree.getPrimitiveTypeKind() ) + { + case BOOLEAN: return "Z"; + case BYTE: return "B"; + case CHAR: return "C"; + case DOUBLE: return "D"; + case FLOAT: return "F"; + case INT: return "I"; + case LONG: return "J"; + case SHORT: return "S"; + case VOID: return "V"; + + default: + throw new Error( "Unsupported primitive type: " + primitiveTypeTree.getPrimitiveTypeKind() ); + } + } + + case IDENTIFIER: + { + IdentifierTree identifierTree = (IdentifierTree)tree; + String className = identifierTree.getName().toString(); + className = ast.getFullClassName( className ); + return "L" + className.replace( ".", "/" ) + ";"; + } + + case ARRAY_TYPE: + { + ArrayTypeTree arrayTree = (ArrayTypeTree)tree; + return "[" + toJvmType( arrayTree.getType(), ast ); + } + + + default: + throw new Error( "Unsupported type kind: " + tree.getKind() ); + } + } +} + +public class Analyzer +{ + public static SourceIndex analyze( String className, String source ) + { + SourceIndex index = new SourceIndex(); + SourcedAst ast = getAst( className, source ); + ast.visit( new TreeVisitor( index ) ); + return index; + } + + private static SourcedAst getAst( String className, String source ) + { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null ); + JavaSourceFromString unit = new JavaSourceFromString( className, source ); + JavacTask task = (JavacTask)compiler.getTask( null, fileManager, null, null, null, Arrays.asList( unit ) ); + + try + { + return new SourcedAst( + task.parse().iterator().next(), + Trees.instance( task ) + ); + } + catch( IOException ex ) + { + throw new Error( ex ); + } + } +} diff --git a/src/cuchaz/enigma/analysis/ClassNameIndex.java b/src/cuchaz/enigma/analysis/ClassNameIndex.java new file mode 100644 index 00000000..ea3e2cae --- /dev/null +++ b/src/cuchaz/enigma/analysis/ClassNameIndex.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.util.TreeScanner; + +public class ClassNameIndex extends TreeScanner +{ + +} diff --git a/src/cuchaz/enigma/analysis/JavaSourceFromString.java b/src/cuchaz/enigma/analysis/JavaSourceFromString.java new file mode 100644 index 00000000..cf5c4c27 --- /dev/null +++ b/src/cuchaz/enigma/analysis/JavaSourceFromString.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.net.URI; + +import javax.tools.SimpleJavaFileObject; + +public class JavaSourceFromString extends SimpleJavaFileObject +{ + private final String m_source; + + JavaSourceFromString( String name, String source ) + { + super( URI.create( "string:///" + name.replace( '.', '/' ) + Kind.SOURCE.extension ), Kind.SOURCE ); + m_source = source; + } + + public CharSequence getCharContent( boolean ignoreEncodingErrors ) + { + return m_source; + } +} diff --git a/src/cuchaz/enigma/analysis/Lexer.java b/src/cuchaz/enigma/analysis/Lexer.java new file mode 100644 index 00000000..acb52bf8 --- /dev/null +++ b/src/cuchaz/enigma/analysis/Lexer.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.Iterator; + +import jsyntaxpane.SyntaxDocument; +import jsyntaxpane.Token; +import jsyntaxpane.lexers.JavaLexer; + +public class Lexer implements Iterable +{ + private SyntaxDocument m_doc; + private Iterator m_iter; + + public Lexer( String source ) + { + m_doc = new SyntaxDocument( new JavaLexer() ); + m_doc.append( source ); + m_iter = m_doc.getTokens( 0, m_doc.getLength() ); + } + + @Override + public Iterator iterator( ) + { + return m_iter; + } + + public String getText( Token token ) + { + return token.getString( m_doc ); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java new file mode 100644 index 00000000..a4b5329b --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import jsyntaxpane.Token; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + +public class SourceIndex implements Iterable> +{ + private BiMap m_entryToToken; + private BiMap m_tokenToEntry; + + public SourceIndex( ) + { + m_entryToToken = HashBiMap.create(); + m_tokenToEntry = m_entryToToken.inverse(); + } + + public void add( Object entry, Token token ) + { + m_entryToToken.put( entry, token ); + } + + public Iterator> iterator( ) + { + return m_entryToToken.entrySet().iterator(); + } + + public Set tokens( ) + { + return m_entryToToken.values(); + } + + public Object getEntry( Token token ) + { + return m_tokenToEntry.get( token ); + } + + public Object getToken( Object entry ) + { + return m_entryToToken.get( entry ); + } +} diff --git a/src/cuchaz/enigma/analysis/SourcedAst.java b/src/cuchaz/enigma/analysis/SourcedAst.java new file mode 100644 index 00000000..04c6f03b --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourcedAst.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.IOException; +import java.util.HashMap; + +import com.google.common.collect.Maps; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ImportTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.SourcePositions; +import com.sun.source.util.Trees; + +public class SourcedAst +{ + private CompilationUnitTree m_tree; + private Trees m_trees; + private SourcePositions m_positions; + private HashMap m_classNameIndex; + + public SourcedAst( CompilationUnitTree tree, Trees trees ) + { + m_tree = tree; + m_trees = trees; + m_positions = m_trees.getSourcePositions(); + m_classNameIndex = Maps.newHashMap(); + + // index all the class names + for( ImportTree importTree : m_tree.getImports() ) + { + // ignore static imports for now + if( importTree.isStatic() ) + { + continue; + } + + // get the full and simple class names + String fullName = importTree.getQualifiedIdentifier().toString(); + String simpleName = fullName; + String[] parts = fullName.split( "\\." ); + if( parts.length > 0 ) + { + simpleName = parts[parts.length - 1]; + } + + m_classNameIndex.put( simpleName, fullName ); + } + } + + public int getStart( Tree node ) + { + return (int)m_positions.getStartPosition( m_tree, node ); + } + + public int getEnd( Tree node ) + { + return (int)m_positions.getEndPosition( m_tree, node ); + } + + public int getLine( Tree node ) + { + return getLine( getStart( node ) ); + } + + public int getLine( int pos ) + { + return (int)m_tree.getLineMap().getLineNumber( pos ); + } + + public CharSequence getSource( ) + { + try + { + return m_tree.getSourceFile().getCharContent( true ); + } + catch( IOException ex ) + { + throw new Error( ex ); + } + } + + public CharSequence getSource( Tree node ) + { + return getSource().subSequence( getStart( node ), getEnd( node ) ); + } + + public void visit( TreeVisitor visitor ) + { + m_tree.accept( visitor, this ); + } + + public String getFullClassName( String simpleClassName ) + { + String fullClassName = m_classNameIndex.get( simpleClassName ); + if( fullClassName == null ) + { + // no mapping was found, the name is probably already fully-qualified + fullClassName = simpleClassName; + } + return fullClassName; + } +} diff --git a/src/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/cuchaz/enigma/gui/BoxHighlightPainter.java new file mode 100644 index 00000000..22db28b7 --- /dev/null +++ b/src/cuchaz/enigma/gui/BoxHighlightPainter.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; + +public class BoxHighlightPainter implements Highlighter.HighlightPainter +{ + private static final Color FillColor = new Color( 230, 230, 230 ); + private static final Color BorderColor = new Color( 100, 100, 100 ); + + @Override + public void paint( Graphics g, int start, int end, Shape shape, JTextComponent text ) + { + try + { + // determine the bounds of the text + Rectangle bounds = text.getUI().modelToView( text, start ).union( text.getUI().modelToView( text, end ) ); + + // adjust the box so it looks nice + bounds.x -= 2; + bounds.width += 2; + bounds.y += 1; + bounds.height -= 2; + + // fill the area + g.setColor( FillColor ); + g.fillRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); + + // draw a box around the area + g.setColor( BorderColor ); + g.drawRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); + } + catch( BadLocationException ex ) + { + throw new Error( ex ); + } + } +} diff --git a/src/cuchaz/enigma/gui/ClassSelectionHandler.java b/src/cuchaz/enigma/gui/ClassSelectionHandler.java new file mode 100644 index 00000000..a50cf6a3 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassSelectionHandler.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import cuchaz.enigma.ClassFile; + +public interface ClassSelectionHandler +{ + void classSelected( ClassFile classFile ); +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java new file mode 100644 index 00000000..e0d53d83 --- /dev/null +++ b/src/cuchaz/enigma/gui/Gui.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; +import java.util.Vector; + +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.ListSelectionModel; +import javax.swing.WindowConstants; +import javax.swing.text.BadLocationException; + +import jsyntaxpane.DefaultSyntaxKit; +import jsyntaxpane.Token; +import cuchaz.enigma.ClassFile; +import cuchaz.enigma.analysis.SourceIndex; + +public class Gui +{ + private static final String Name = "Enigma"; + + // controls + private JFrame m_frame; + private JList m_obfClasses; + private JList m_deobfClasses; + private JEditorPane m_editor; + + // handlers + private ClassSelectionHandler m_classSelectionHandler; + + private BoxHighlightPainter m_highlightPainter; + + public Gui( ) + { + // init frame + m_frame = new JFrame( Name ); + final Container pane = m_frame.getContentPane(); + pane.setLayout( new BorderLayout() ); + + // init obfuscated classes list + m_obfClasses = new JList(); + m_obfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + m_obfClasses.setLayoutOrientation( JList.VERTICAL ); + m_obfClasses.setCellRenderer( new ObfuscatedClassListCellRenderer() ); + m_obfClasses.addMouseListener( new MouseAdapter() + { + public void mouseClicked( MouseEvent event ) + { + if( event.getClickCount() == 2 ) + { + if( m_classSelectionHandler != null ) + { + ClassFile selected = m_obfClasses.getSelectedValue(); + if( selected != null ) + { + m_classSelectionHandler.classSelected( selected ); + } + } + } + } + } ); + JScrollPane obfScroller = new JScrollPane( m_obfClasses ); + JPanel obfPanel = new JPanel(); + obfPanel.setLayout( new BorderLayout() ); + obfPanel.add( new JLabel( "Obfuscated Classes" ), BorderLayout.NORTH ); + obfPanel.add( obfScroller, BorderLayout.CENTER ); + + // init deobfuscated classes list + m_deobfClasses = new JList(); + m_obfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + m_obfClasses.setLayoutOrientation( JList.VERTICAL ); + JScrollPane deobfScroller = new JScrollPane( m_deobfClasses ); + JPanel deobfPanel = new JPanel(); + deobfPanel.setLayout( new BorderLayout() ); + deobfPanel.add( new JLabel( "De-obfuscated Classes" ), BorderLayout.NORTH ); + deobfPanel.add( deobfScroller, BorderLayout.CENTER ); + + // init editor + DefaultSyntaxKit.initKit(); + m_editor = new JEditorPane(); + m_editor.setEditable( false ); + JScrollPane sourceScroller = new JScrollPane( m_editor ); + m_editor.setContentType( "text/java" ); + + // layout controls + JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); + JSplitPane splitMain = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, sourceScroller ); + pane.add( splitMain, BorderLayout.CENTER ); + + // show the frame + pane.doLayout(); + m_frame.setSize( 800, 600 ); + m_frame.setVisible( true ); + m_frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE ); + + // init handlers + m_classSelectionHandler = null; + + m_highlightPainter = new BoxHighlightPainter(); + } + + public void setTitle( String title ) + { + m_frame.setTitle( Name + " - " + title ); + } + + public void setObfClasses( List classes ) + { + m_obfClasses.setListData( new Vector( classes ) ); + } + + public void setSource( String source ) + { + setSource( source, null ); + } + + public void setSource( String source, SourceIndex index ) + { + m_editor.setText( source ); + + // remove any old highlighters + m_editor.getHighlighter().removeAllHighlights();; + + if( index != null ) + { + // color things based on the index + for( Token token : index.tokens() ) + { + try + { + m_editor.getHighlighter().addHighlight( token.start, token.end(), m_highlightPainter ); + } + catch( BadLocationException ex ) + { + throw new Error( ex ); + } + } + } + } + + public void setClassSelectionHandler( ClassSelectionHandler val ) + { + m_classSelectionHandler = val; + } +} diff --git a/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java b/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java new file mode 100644 index 00000000..0badb3b9 --- /dev/null +++ b/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Component; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +import cuchaz.enigma.ClassFile; + +public class ObfuscatedClassListCellRenderer implements ListCellRenderer +{ + private DefaultListCellRenderer m_defaultRenderer; + + public ObfuscatedClassListCellRenderer( ) + { + m_defaultRenderer = new DefaultListCellRenderer(); + } + + @Override + public Component getListCellRendererComponent( JList list, ClassFile classFile, int index, boolean isSelected, boolean hasFocus ) + { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, classFile, index, isSelected, hasFocus ); + + label.setText( classFile.getName() ); + + return label; + } +} diff --git a/src/cuchaz/enigma/gui/SourceFormatter.java b/src/cuchaz/enigma/gui/SourceFormatter.java new file mode 100644 index 00000000..f3878405 --- /dev/null +++ b/src/cuchaz/enigma/gui/SourceFormatter.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; + +public class SourceFormatter +{ + public static String format( String in ) + { + return collapseNewlines( in ); + } + + private static String collapseNewlines( String in ) + { + StringBuffer buf = new StringBuffer(); + int numBlankLines = 0; + + BufferedReader reader = new BufferedReader( new StringReader( in ) ); + String line = null; + try + { + while( ( line = reader.readLine() ) != null ) + { + // how blank lines is this? + boolean isBlank = line.trim().length() == 0; + if( isBlank ) + { + numBlankLines++; + + // stop printing blank lines after the first one + if( numBlankLines < 2 ) + { + buf.append( line ); + buf.append( "\n" ); + } + } + else + { + numBlankLines = 0; + buf.append( line ); + buf.append( "\n" ); + } + } + } + catch( IOException ex ) + { + // StringReader will never throw an IOExecption here... + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java new file mode 100644 index 00000000..6c108d7c --- /dev/null +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +import cuchaz.enigma.Util; + +public class ArgumentEntry implements Serializable +{ + private static final long serialVersionUID = 4472172468162696006L; + + private MethodEntry m_methodEntry; + private int m_index; + private String m_name; + + public ArgumentEntry( MethodEntry methodEntry, int index, String name ) + { + if( methodEntry == null ) + { + throw new IllegalArgumentException( "Method cannot be null!" ); + } + if( index < 0 ) + { + throw new IllegalArgumentException( "Index must be non-negative!" ); + } + if( name == null ) + { + throw new IllegalArgumentException( "Argument name cannot be null!" ); + } + + m_methodEntry = methodEntry; + m_index = index; + m_name = name; + } + + public MethodEntry getMethodEntry( ) + { + return m_methodEntry; + } + + public int getIndex( ) + { + return m_index; + } + + public String getName( ) + { + return m_name; + } + + @Override + public int hashCode( ) + { + return Util.combineHashesOrdered( m_methodEntry, Integer.valueOf( m_index ).hashCode(), m_name.hashCode() ); + } + + @Override + public boolean equals( Object other ) + { + if( other instanceof ArgumentEntry ) + { + return equals( (ArgumentEntry)other ); + } + return false; + } + + public boolean equals( ArgumentEntry other ) + { + return m_methodEntry.equals( other.m_methodEntry ) + && m_index == other.m_index + && m_name.equals( other.m_name ); + } + + @Override + public String toString( ) + { + return m_methodEntry.toString() + "(" + m_index + ":" + m_name + ")"; + } +} diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java new file mode 100644 index 00000000..7e78d4c0 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + + +public class ClassEntry implements Serializable +{ + private static final long serialVersionUID = 4235460580973955811L; + + private String m_name; + + public ClassEntry( String className ) + { + if( className == null ) + { + throw new IllegalArgumentException( "Class name cannot be null!" ); + } + if( className.contains( "." ) ) + { + throw new IllegalArgumentException( "Class name must be in JVM format. ie, path/to/package/class$inner" ); + } + + m_name = className; + } + + public String getName( ) + { + return m_name; + } + + @Override + public int hashCode( ) + { + return m_name.hashCode(); + } + + @Override + public boolean equals( Object other ) + { + if( other instanceof ClassEntry ) + { + return equals( (ClassEntry)other ); + } + return false; + } + + public boolean equals( ClassEntry other ) + { + return m_name.equals( other.m_name ); + } + + @Override + public String toString( ) + { + return m_name; + } +} diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java new file mode 100644 index 00000000..15a93520 --- /dev/null +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +import cuchaz.enigma.Util; + +public class FieldEntry implements Serializable +{ + private static final long serialVersionUID = 3004663582802885451L; + + private ClassEntry m_classEntry; + private String m_name; + + public FieldEntry( ClassEntry classEntry, String name ) + { + if( classEntry == null ) + { + throw new IllegalArgumentException( "Class cannot be null!" ); + } + if( name == null ) + { + throw new IllegalArgumentException( "Field name cannot be null!" ); + } + + m_classEntry = classEntry; + m_name = name; + } + + public ClassEntry getClassEntry( ) + { + return m_classEntry; + } + + public String getName( ) + { + return m_name; + } + + @Override + public int hashCode( ) + { + return Util.combineHashesOrdered( m_classEntry, m_name ); + } + + @Override + public boolean equals( Object other ) + { + if( other instanceof FieldEntry ) + { + return equals( (FieldEntry)other ); + } + return false; + } + + public boolean equals( FieldEntry other ) + { + return m_classEntry.equals( other.m_classEntry ) + && m_name.equals( other.m_name ); + } + + @Override + public String toString( ) + { + return m_classEntry.getName() + "." + m_name; + } +} diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java new file mode 100644 index 00000000..e71a4664 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +import cuchaz.enigma.Util; + +public class MethodEntry implements Serializable +{ + private static final long serialVersionUID = 4770915224467247458L; + + private ClassEntry m_classEntry; + private String m_name; + private String m_signature; + + public MethodEntry( ClassEntry classEntry, String name, String signature ) + { + if( classEntry == null ) + { + throw new IllegalArgumentException( "Class cannot be null!" ); + } + if( name == null ) + { + throw new IllegalArgumentException( "Method name cannot be null!" ); + } + if( signature == null ) + { + throw new IllegalArgumentException( "Method signature cannot be null!" ); + } + + m_classEntry = classEntry; + m_name = name; + m_signature = signature; + } + + public ClassEntry getClassEntry( ) + { + return m_classEntry; + } + + public String getName( ) + { + return m_name; + } + + public String getSignature( ) + { + return m_signature; + } + + @Override + public int hashCode( ) + { + return Util.combineHashesOrdered( m_classEntry, m_name, m_signature ); + } + + @Override + public boolean equals( Object other ) + { + if( other instanceof MethodEntry ) + { + return equals( (MethodEntry)other ); + } + return false; + } + + public boolean equals( MethodEntry other ) + { + return m_classEntry.equals( other.m_classEntry ) + && m_name.equals( other.m_name ) + && m_signature.equals( other.m_signature ); + } + + @Override + public String toString( ) + { + return m_classEntry.getName() + "." + m_name + ":" + m_signature; + } +} -- cgit v1.2.3 From 4ea3e0cffa54326bc2566fa563c6b5aa5b9b1c96 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 26 Jul 2014 19:52:30 -0400 Subject: got rid of temp debug traces --- src/cuchaz/enigma/analysis/Analyzer.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/cuchaz/enigma/analysis/Analyzer.java b/src/cuchaz/enigma/analysis/Analyzer.java index 1cdabe75..dad8dc52 100644 --- a/src/cuchaz/enigma/analysis/Analyzer.java +++ b/src/cuchaz/enigma/analysis/Analyzer.java @@ -152,8 +152,6 @@ class TreeVisitor extends TreeScanner private void indexArgument( VariableTree variableTree, SourcedAst ast, MethodEntry methodEntry, int index ) { - System.out.println( "\tFound argument: " + variableTree.getName() ); - // build the entry ArgumentEntry entry = new ArgumentEntry( methodEntry, index, variableTree.getName().toString() ); -- cgit v1.2.3 From 999c64037fb7251f87bd7b105231b3763e003c07 Mon Sep 17 00:00:00 2001 From: hg Date: Sun, 27 Jul 2014 00:52:28 -0400 Subject: made gui responsive to caret position and show identifier info --- src/cuchaz/enigma/Controller.java | 83 ++++++++ src/cuchaz/enigma/Deobfuscator.java | 5 + src/cuchaz/enigma/Main.java | 33 +--- src/cuchaz/enigma/analysis/SourceIndex.java | 30 ++- src/cuchaz/enigma/gui/ClassSelectionHandler.java | 18 -- src/cuchaz/enigma/gui/ClassSelectionListener.java | 18 ++ src/cuchaz/enigma/gui/Gui.java | 228 +++++++++++++++++++--- src/cuchaz/enigma/gui/RenameListener.java | 18 ++ src/cuchaz/enigma/mapping/ArgumentEntry.java | 3 +- src/cuchaz/enigma/mapping/ClassEntry.java | 3 +- src/cuchaz/enigma/mapping/Entry.java | 16 ++ src/cuchaz/enigma/mapping/FieldEntry.java | 3 +- src/cuchaz/enigma/mapping/MethodEntry.java | 3 +- 13 files changed, 379 insertions(+), 82 deletions(-) create mode 100644 src/cuchaz/enigma/Controller.java delete mode 100644 src/cuchaz/enigma/gui/ClassSelectionHandler.java create mode 100644 src/cuchaz/enigma/gui/ClassSelectionListener.java create mode 100644 src/cuchaz/enigma/gui/RenameListener.java create mode 100644 src/cuchaz/enigma/mapping/Entry.java diff --git a/src/cuchaz/enigma/Controller.java b/src/cuchaz/enigma/Controller.java new file mode 100644 index 00000000..debc5e38 --- /dev/null +++ b/src/cuchaz/enigma/Controller.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; + +import cuchaz.enigma.analysis.Analyzer; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.gui.ClassSelectionListener; +import cuchaz.enigma.gui.Gui; +import cuchaz.enigma.gui.RenameListener; +import cuchaz.enigma.mapping.Entry; + +public class Controller implements ClassSelectionListener, CaretListener, RenameListener +{ + private Deobfuscator m_deobfuscator; + private Gui m_gui; + private SourceIndex m_index; + + public Controller( Deobfuscator deobfuscator, Gui gui ) + { + m_deobfuscator = deobfuscator; + m_gui = gui; + m_index = null; + + // update GUI + gui.setTitle( deobfuscator.getJarName() ); + gui.setObfClasses( deobfuscator.getObfuscatedClasses() ); + + // handle events + gui.setClassSelectionListener( this ); + gui.setCaretListener( this ); + gui.setRenameListener( this ); + } + + @Override + public void classSelected( final ClassFile classFile ) + { + m_gui.setSource( "(deobfuscating...)" ); + + // run the deobfuscator in a separate thread so we don't block the GUI event queue + new Thread( ) + { + @Override + public void run( ) + { + // deobfuscate the bytecode + String source = m_deobfuscator.getSource( classFile ); + m_gui.setSource( source ); + + // index the source file + m_index = Analyzer.analyze( classFile.getName(), source ); + m_gui.highlightTokens( m_index.tokens() ); + } + }.start(); + } + + @Override + public void caretUpdate( CaretEvent event ) + { + if( m_index != null ) + { + int pos = event.getDot(); + m_gui.showEntry( m_index.getEntry( pos ) ); + } + } + + @Override + public void rename( Entry entry, String newName ) + { + // TEMP + System.out.println( "Rename " + entry + " to " + newName ); + } +} diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 83a21719..97c57505 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -62,6 +62,11 @@ public class Deobfuscator m_settings.setShowSyntheticMembers( true ); } + public String getJarName( ) + { + return m_file.getName(); + } + public List getObfuscatedClasses( ) { List classes = new ArrayList(); diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index e08c16ed..4842e208 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -12,9 +12,6 @@ package cuchaz.enigma; import java.io.File; -import cuchaz.enigma.analysis.Analyzer; -import cuchaz.enigma.analysis.SourceIndex; -import cuchaz.enigma.gui.ClassSelectionHandler; import cuchaz.enigma.gui.Gui; public class Main @@ -28,36 +25,10 @@ public class Main private static void startGui( ) throws Exception { - final Gui gui = new Gui(); - // settings final File jarFile = new File( "/home/jeff/.minecraft/versions/1.7.10/1.7.10.jar" ); - gui.setTitle( jarFile.getName() ); - - // init the deobfuscator - final Deobfuscator deobfuscator = new Deobfuscator( jarFile ); - gui.setObfClasses( deobfuscator.getObfuscatedClasses() ); - // handle events - gui.setClassSelectionHandler( new ClassSelectionHandler( ) - { - @Override - public void classSelected( final ClassFile classFile ) - { - gui.setSource( "(deobfuscating...)" ); - - // run the deobfuscator in a separate thread so we don't block the GUI event queue - new Thread( ) - { - @Override - public void run( ) - { - String source = deobfuscator.getSource( classFile ); - SourceIndex index = Analyzer.analyze( classFile.getName(), source ); - gui.setSource( source, index ); - } - }.start(); - } - } ); + // start the GUI and tie it to the deobfuscator + new Controller( new Deobfuscator( jarFile ), new Gui() ); } } diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index a4b5329b..ee92d1ed 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -19,10 +19,12 @@ import jsyntaxpane.Token; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -public class SourceIndex implements Iterable> +import cuchaz.enigma.mapping.Entry; + +public class SourceIndex implements Iterable> { - private BiMap m_entryToToken; - private BiMap m_tokenToEntry; + private BiMap m_entryToToken; + private BiMap m_tokenToEntry; public SourceIndex( ) { @@ -30,12 +32,12 @@ public class SourceIndex implements Iterable> m_tokenToEntry = m_entryToToken.inverse(); } - public void add( Object entry, Token token ) + public void add( Entry entry, Token token ) { m_entryToToken.put( entry, token ); } - public Iterator> iterator( ) + public Iterator> iterator( ) { return m_entryToToken.entrySet().iterator(); } @@ -45,12 +47,26 @@ public class SourceIndex implements Iterable> return m_entryToToken.values(); } - public Object getEntry( Token token ) + public Entry getEntry( Token token ) { return m_tokenToEntry.get( token ); } - public Object getToken( Object entry ) + public Entry getEntry( int pos ) + { + // linear search is fast enough for now + for( Map.Entry entry : this ) + { + Token token = entry.getValue(); + if( pos >= token.start && pos <= token.end() ) + { + return entry.getKey(); + } + } + return null; + } + + public Token getToken( Entry entry ) { return m_entryToToken.get( entry ); } diff --git a/src/cuchaz/enigma/gui/ClassSelectionHandler.java b/src/cuchaz/enigma/gui/ClassSelectionHandler.java deleted file mode 100644 index a50cf6a3..00000000 --- a/src/cuchaz/enigma/gui/ClassSelectionHandler.java +++ /dev/null @@ -1,18 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.gui; - -import cuchaz.enigma.ClassFile; - -public interface ClassSelectionHandler -{ - void classSelected( ClassFile classFile ); -} diff --git a/src/cuchaz/enigma/gui/ClassSelectionListener.java b/src/cuchaz/enigma/gui/ClassSelectionListener.java new file mode 100644 index 00000000..4a2aa8d0 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassSelectionListener.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import cuchaz.enigma.ClassFile; + +public interface ClassSelectionListener +{ + void classSelected( ClassFile classFile ); +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index e0d53d83..d1a3cb21 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -12,11 +12,20 @@ package cuchaz.enigma.gui; import java.awt.BorderLayout; import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.List; import java.util.Vector; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JButton; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; @@ -24,14 +33,21 @@ import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; +import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.WindowConstants; +import javax.swing.event.CaretListener; import javax.swing.text.BadLocationException; import jsyntaxpane.DefaultSyntaxKit; import jsyntaxpane.Token; import cuchaz.enigma.ClassFile; import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; public class Gui { @@ -42,11 +58,18 @@ public class Gui private JList m_obfClasses; private JList m_deobfClasses; private JEditorPane m_editor; + private JPanel m_actionPanel; + private JPanel m_renamePanel; + private JLabel m_typeLabel; + private JTextField m_nameField; + private JButton m_renameButton; - // handlers - private ClassSelectionHandler m_classSelectionHandler; + // listeners + private ClassSelectionListener m_classSelectionListener; + private RenameListener m_renameListener; private BoxHighlightPainter m_highlightPainter; + private Entry m_selectedEntry; public Gui( ) { @@ -66,12 +89,12 @@ public class Gui { if( event.getClickCount() == 2 ) { - if( m_classSelectionHandler != null ) + if( m_classSelectionListener != null ) { ClassFile selected = m_obfClasses.getSelectedValue(); if( selected != null ) { - m_classSelectionHandler.classSelected( selected ); + m_classSelectionListener.classSelected( selected ); } } } @@ -93,6 +116,34 @@ public class Gui deobfPanel.add( new JLabel( "De-obfuscated Classes" ), BorderLayout.NORTH ); deobfPanel.add( deobfScroller, BorderLayout.CENTER ); + // init action panel + m_actionPanel = new JPanel(); + m_actionPanel.setLayout( new BoxLayout( m_actionPanel, BoxLayout.Y_AXIS ) ); + m_actionPanel.setPreferredSize( new Dimension( 0, 120 ) ); + m_actionPanel.setBorder( BorderFactory.createTitledBorder( "Identifier Info" ) ); + m_nameField = new JTextField( 26 ); + m_renameButton = new JButton( "Rename" ); + m_renameButton.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + if( m_renameListener != null && m_selectedEntry != null ) + { + m_renameListener.rename( m_selectedEntry, m_nameField.getText() ); + } + } + } ); + m_renamePanel = new JPanel(); + m_renamePanel.setLayout( new FlowLayout( FlowLayout.LEFT, 6, 0 ) ); + m_typeLabel = new JLabel( "LongName:", JLabel.RIGHT ); + // NOTE: this looks ridiculous, but it fixes the label size to the size of current text + m_typeLabel.setPreferredSize( m_typeLabel.getPreferredSize() ); + m_renamePanel.add( m_typeLabel ); + m_renamePanel.add( m_nameField ); + m_renamePanel.add( m_renameButton ); + clearEntry(); + // init editor DefaultSyntaxKit.initKit(); m_editor = new JEditorPane(); @@ -102,17 +153,23 @@ public class Gui // layout controls JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); - JSplitPane splitMain = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, sourceScroller ); + JPanel rightPanel = new JPanel(); + rightPanel.setLayout( new BorderLayout() ); + rightPanel.add( m_actionPanel, BorderLayout.NORTH ); + rightPanel.add( sourceScroller, BorderLayout.CENTER ); + JSplitPane splitMain = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, rightPanel ); pane.add( splitMain, BorderLayout.CENTER ); // show the frame pane.doLayout(); m_frame.setSize( 800, 600 ); + m_frame.setMinimumSize( new Dimension( 640, 480 ) ); m_frame.setVisible( true ); m_frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE ); - // init handlers - m_classSelectionHandler = null; + // init listeners + m_classSelectionListener = null; + m_renameListener = null; m_highlightPainter = new BoxHighlightPainter(); } @@ -135,29 +192,156 @@ public class Gui public void setSource( String source, SourceIndex index ) { m_editor.setText( source ); - + } + + public void highlightTokens( Iterable tokens ) + { // remove any old highlighters - m_editor.getHighlighter().removeAllHighlights();; + m_editor.getHighlighter().removeAllHighlights(); - if( index != null ) + if( tokens == null ) { - // color things based on the index - for( Token token : index.tokens() ) + return; + } + + // color things based on the index + for( Token token : tokens ) + { + try { - try - { - m_editor.getHighlighter().addHighlight( token.start, token.end(), m_highlightPainter ); - } - catch( BadLocationException ex ) - { - throw new Error( ex ); - } + m_editor.getHighlighter().addHighlight( token.start, token.end(), m_highlightPainter ); + } + catch( BadLocationException ex ) + { + throw new IllegalArgumentException( ex ); } } + + redraw(); + } + + public void setClassSelectionListener( ClassSelectionListener val ) + { + m_classSelectionListener = val; + } + + public void setRenameListener( RenameListener val ) + { + m_renameListener = val; + } + + public void setCaretListener( CaretListener listener ) + { + // remove any old listeners + for( CaretListener oldListener : m_editor.getCaretListeners() ) + { + m_editor.removeCaretListener( oldListener ); + } + + m_editor.addCaretListener( listener ); + } + + public void clearEntry( ) + { + m_actionPanel.removeAll(); + JLabel label = new JLabel( "No identifier selected" ); + unboldLabel( label ); + label.setHorizontalAlignment( JLabel.CENTER ); + m_actionPanel.add( label ); + + redraw(); + } + + public void showEntry( Entry entry ) + { + if( entry == null ) + { + clearEntry(); + return; + } + + // layout the action panel + m_actionPanel.removeAll(); + m_actionPanel.add( m_renamePanel ); + m_nameField.setText( entry.getName() ); + + // layout the dynamic section + JPanel dynamicPanel = new JPanel(); + dynamicPanel.setLayout( new GridLayout( 3, 1, 0, 0 ) ); + m_actionPanel.add( dynamicPanel ); + if( entry instanceof ClassEntry ) + { + showEntry( (ClassEntry)entry, dynamicPanel ); + } + else if( entry instanceof FieldEntry ) + { + showEntry( (FieldEntry)entry, dynamicPanel ); + } + else if( entry instanceof MethodEntry ) + { + showEntry( (MethodEntry)entry, dynamicPanel ); + } + else if( entry instanceof ArgumentEntry ) + { + showEntry( (ArgumentEntry)entry, dynamicPanel ); + } + else + { + throw new Error( "Unknown entry type: " + entry.getClass().getName() ); + } + + redraw(); + } + + public void showEntry( ClassEntry entry, JPanel panel ) + { + m_typeLabel.setText( "Class: " ); + } + + public void showEntry( FieldEntry entry, JPanel panel ) + { + m_typeLabel.setText( "Field: " ); + addNameValue( panel, "Class", entry.getClassEntry().getName() ); + } + + public void showEntry( MethodEntry entry, JPanel panel ) + { + m_typeLabel.setText( "Method: " ); + addNameValue( panel, "Class", entry.getClassEntry().getName() ); + addNameValue( panel, "Signature", entry.getSignature() ); + } + + public void showEntry( ArgumentEntry entry, JPanel panel ) + { + m_typeLabel.setText( "Argument: " ); + addNameValue( panel, "Class", entry.getMethodEntry().getClassEntry().getName() ); + addNameValue( panel, "Method", entry.getMethodEntry().getName() ); + addNameValue( panel, "Index", Integer.toString( entry.getIndex() ) ); + } + + private void addNameValue( JPanel container, String name, String value ) + { + JPanel panel = new JPanel(); + panel.setLayout( new FlowLayout( FlowLayout.LEFT, 6, 0 ) ); + container.add( panel ); + + JLabel label = new JLabel( name + ":", JLabel.RIGHT ); + label.setPreferredSize( new Dimension( 80, label.getPreferredSize().height ) ); + panel.add( label ); + + panel.add( unboldLabel( new JLabel( value, JLabel.LEFT ) ) ); + } + + private JLabel unboldLabel( JLabel label ) + { + Font font = label.getFont(); + label.setFont( font.deriveFont( font.getStyle() & ~Font.BOLD ) ); + return label; } - public void setClassSelectionHandler( ClassSelectionHandler val ) + private void redraw( ) { - m_classSelectionHandler = val; + m_frame.validate(); + m_frame.repaint(); } } diff --git a/src/cuchaz/enigma/gui/RenameListener.java b/src/cuchaz/enigma/gui/RenameListener.java new file mode 100644 index 00000000..58cdadb0 --- /dev/null +++ b/src/cuchaz/enigma/gui/RenameListener.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import cuchaz.enigma.mapping.Entry; + +public interface RenameListener +{ + void rename( Entry entry, String newName ); +} diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java index 6c108d7c..dc3b4df7 100644 --- a/src/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -14,7 +14,7 @@ import java.io.Serializable; import cuchaz.enigma.Util; -public class ArgumentEntry implements Serializable +public class ArgumentEntry implements Entry, Serializable { private static final long serialVersionUID = 4472172468162696006L; @@ -52,6 +52,7 @@ public class ArgumentEntry implements Serializable return m_index; } + @Override public String getName( ) { return m_name; diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 7e78d4c0..3a757675 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -13,7 +13,7 @@ package cuchaz.enigma.mapping; import java.io.Serializable; -public class ClassEntry implements Serializable +public class ClassEntry implements Entry, Serializable { private static final long serialVersionUID = 4235460580973955811L; @@ -33,6 +33,7 @@ public class ClassEntry implements Serializable m_name = className; } + @Override public String getName( ) { return m_name; diff --git a/src/cuchaz/enigma/mapping/Entry.java b/src/cuchaz/enigma/mapping/Entry.java new file mode 100644 index 00000000..14435324 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Entry.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public interface Entry +{ + String getName( ); +} diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java index 15a93520..25b665ab 100644 --- a/src/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -14,7 +14,7 @@ import java.io.Serializable; import cuchaz.enigma.Util; -public class FieldEntry implements Serializable +public class FieldEntry implements Entry, Serializable { private static final long serialVersionUID = 3004663582802885451L; @@ -41,6 +41,7 @@ public class FieldEntry implements Serializable return m_classEntry; } + @Override public String getName( ) { return m_name; diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java index e71a4664..4afc099b 100644 --- a/src/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -14,7 +14,7 @@ import java.io.Serializable; import cuchaz.enigma.Util; -public class MethodEntry implements Serializable +public class MethodEntry implements Entry, Serializable { private static final long serialVersionUID = 4770915224467247458L; @@ -47,6 +47,7 @@ public class MethodEntry implements Serializable return m_classEntry; } + @Override public String getName( ) { return m_name; -- cgit v1.2.3 From d7321b5b0d38c575e54c770f7aa18dacbacab3c8 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 27 Jul 2014 22:33:21 -0400 Subject: added identifier renaming capability copied some code over from M3L to handle the heavy bytecode magic. It's ok... M3L will eventually depend on Enigma. Completely restructured the mappings though. This way is better. =) --- src/cuchaz/enigma/Constants.java | 18 + src/cuchaz/enigma/Controller.java | 67 +++- src/cuchaz/enigma/Deobfuscator.java | 115 ++++++- src/cuchaz/enigma/TranslatingTypeLoader.java | 93 ++++++ .../enigma/bytecode/BytecodeIndexIterator.java | 180 ++++++++++ src/cuchaz/enigma/bytecode/BytecodeTools.java | 269 +++++++++++++++ src/cuchaz/enigma/bytecode/ClassTranslator.java | 171 ++++++++++ src/cuchaz/enigma/bytecode/ConstPoolEditor.java | 316 ++++++++++++++++++ src/cuchaz/enigma/bytecode/InfoType.java | 364 +++++++++++++++++++++ .../bytecode/accessors/ClassInfoAccessor.java | 69 ++++ .../bytecode/accessors/ConstInfoAccessor.java | 199 +++++++++++ .../accessors/InvokeDynamicInfoAccessor.java | 96 ++++++ .../bytecode/accessors/MemberRefInfoAccessor.java | 96 ++++++ .../accessors/MethodHandleInfoAccessor.java | 96 ++++++ .../bytecode/accessors/MethodTypeInfoAccessor.java | 69 ++++ .../accessors/NameAndTypeInfoAccessor.java | 96 ++++++ .../bytecode/accessors/StringInfoAccessor.java | 69 ++++ .../bytecode/accessors/Utf8InfoAccessor.java | 33 ++ src/cuchaz/enigma/gui/Gui.java | 53 +-- src/cuchaz/enigma/gui/RenameListener.java | 2 +- src/cuchaz/enigma/mapping/Ancestries.java | 134 ++++++++ src/cuchaz/enigma/mapping/ArgumentEntry.java | 27 ++ src/cuchaz/enigma/mapping/ArgumentIndex.java | 41 +++ src/cuchaz/enigma/mapping/ClassEntry.java | 5 + src/cuchaz/enigma/mapping/ClassIndex.java | 159 +++++++++ .../enigma/mapping/DeobfuscatedAncestries.java | 57 ++++ src/cuchaz/enigma/mapping/EntryPair.java | 46 +++ src/cuchaz/enigma/mapping/FieldEntry.java | 17 + src/cuchaz/enigma/mapping/MethodEntry.java | 21 +- src/cuchaz/enigma/mapping/MethodIndex.java | 125 +++++++ src/cuchaz/enigma/mapping/SignatureUpdater.java | 87 +++++ .../enigma/mapping/TranslationDirection.java | 34 ++ src/cuchaz/enigma/mapping/TranslationMappings.java | 187 +++++++++++ src/cuchaz/enigma/mapping/Translator.java | 201 ++++++++++++ 34 files changed, 3566 insertions(+), 46 deletions(-) create mode 100644 src/cuchaz/enigma/Constants.java create mode 100644 src/cuchaz/enigma/TranslatingTypeLoader.java create mode 100644 src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java create mode 100644 src/cuchaz/enigma/bytecode/BytecodeTools.java create mode 100644 src/cuchaz/enigma/bytecode/ClassTranslator.java create mode 100644 src/cuchaz/enigma/bytecode/ConstPoolEditor.java create mode 100644 src/cuchaz/enigma/bytecode/InfoType.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java create mode 100644 src/cuchaz/enigma/mapping/Ancestries.java create mode 100644 src/cuchaz/enigma/mapping/ArgumentIndex.java create mode 100644 src/cuchaz/enigma/mapping/ClassIndex.java create mode 100644 src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java create mode 100644 src/cuchaz/enigma/mapping/EntryPair.java create mode 100644 src/cuchaz/enigma/mapping/MethodIndex.java create mode 100644 src/cuchaz/enigma/mapping/SignatureUpdater.java create mode 100644 src/cuchaz/enigma/mapping/TranslationDirection.java create mode 100644 src/cuchaz/enigma/mapping/TranslationMappings.java create mode 100644 src/cuchaz/enigma/mapping/Translator.java diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java new file mode 100644 index 00000000..09787145 --- /dev/null +++ b/src/cuchaz/enigma/Constants.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + + +public class Constants +{ + public static final int MiB = 1024*1024; // 1 mebibyte + public static final int KiB = 1024; // 1 kebibyte +} diff --git a/src/cuchaz/enigma/Controller.java b/src/cuchaz/enigma/Controller.java index debc5e38..3af139e8 100644 --- a/src/cuchaz/enigma/Controller.java +++ b/src/cuchaz/enigma/Controller.java @@ -18,19 +18,23 @@ import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.gui.ClassSelectionListener; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.RenameListener; +import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.EntryPair; public class Controller implements ClassSelectionListener, CaretListener, RenameListener { private Deobfuscator m_deobfuscator; private Gui m_gui; private SourceIndex m_index; + private ClassFile m_currentFile; public Controller( Deobfuscator deobfuscator, Gui gui ) { m_deobfuscator = deobfuscator; m_gui = gui; m_index = null; + m_currentFile = null; // update GUI gui.setTitle( deobfuscator.getJarName() ); @@ -43,7 +47,51 @@ public class Controller implements ClassSelectionListener, CaretListener, Rename } @Override - public void classSelected( final ClassFile classFile ) + public void classSelected( ClassFile classFile ) + { + m_currentFile = classFile; + deobfuscate( m_currentFile ); + } + + @Override + public void caretUpdate( CaretEvent event ) + { + if( m_index != null ) + { + int pos = event.getDot(); + Entry deobfEntry = m_index.getEntry( pos ); + if( deobfEntry != null ) + { + m_gui.showEntryPair( new EntryPair( m_deobfuscator.obfuscate( deobfEntry ), deobfEntry ) ); + } + else + { + m_gui.clearEntryPair(); + } + } + } + + @Override + public void rename( Entry obfsEntry, String newName ) + { + m_deobfuscator.rename( obfsEntry, newName ); + + // did we rename the current file? + if( obfsEntry instanceof ClassEntry ) + { + ClassEntry classEntry = (ClassEntry)obfsEntry; + + // update the current file + if( classEntry.getName().equals( m_currentFile.getName() ) ) + { + m_currentFile = new ClassFile( newName ); + } + } + + deobfuscate( m_currentFile ); + } + + private void deobfuscate( final ClassFile classFile ) { m_gui.setSource( "(deobfuscating...)" ); @@ -63,21 +111,4 @@ public class Controller implements ClassSelectionListener, CaretListener, Rename } }.start(); } - - @Override - public void caretUpdate( CaretEvent event ) - { - if( m_index != null ) - { - int pos = event.getDot(); - m_gui.showEntry( m_index.getEntry( pos ) ); - } - } - - @Override - public void rename( Entry entry, String newName ) - { - // TEMP - System.out.println( "Rename " + entry + " to " + newName ); - } } diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 97c57505..b1abd9e0 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -11,7 +11,9 @@ package cuchaz.enigma; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; @@ -21,16 +23,27 @@ import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import com.strobel.assembler.metadata.JarTypeLoader; import com.strobel.decompiler.Decompiler; import com.strobel.decompiler.DecompilerSettings; import com.strobel.decompiler.PlainTextOutput; +import cuchaz.enigma.mapping.Ancestries; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.TranslationDirection; +import cuchaz.enigma.mapping.TranslationMappings; +import cuchaz.enigma.mapping.Translator; + public class Deobfuscator { private File m_file; private JarFile m_jar; private DecompilerSettings m_settings; + private Ancestries m_ancestries; + private TranslationMappings m_mappings; private static Comparator m_obfuscatedClassSorter; @@ -56,8 +69,30 @@ public class Deobfuscator { m_file = file; m_jar = new JarFile( m_file ); + + // build the ancestries + InputStream jarIn = null; + try + { + m_ancestries = new Ancestries(); + jarIn = new FileInputStream( m_file ); + m_ancestries.readFromJar( jarIn ); + } + finally + { + Util.closeQuietly( jarIn ); + } + + // init mappings + m_mappings = new TranslationMappings( m_ancestries ); + + // config the decompiler m_settings = DecompilerSettings.javaDefaults(); - m_settings.setTypeLoader( new JarTypeLoader( m_jar ) ); + m_settings.setTypeLoader( new TranslatingTypeLoader( + m_jar, + m_mappings.getTranslator( TranslationDirection.Deobfuscating ), + m_mappings.getTranslator( TranslationDirection.Obfuscating ) + ) ); m_settings.setForceExplicitImports( true ); m_settings.setShowSyntheticMembers( true ); } @@ -111,4 +146,80 @@ public class Deobfuscator Decompiler.decompile( classFile.getName(), new PlainTextOutput( buf ), m_settings ); return buf.toString(); } + + // NOTE: these methods are a bit messy... oh well + + public void rename( Entry entry, String newName ) + { + if( entry instanceof ClassEntry ) + { + m_mappings.setClassName( (ClassEntry)entry, newName ); + } + else if( entry instanceof FieldEntry ) + { + m_mappings.setFieldName( (FieldEntry)entry, newName ); + } + else if( entry instanceof MethodEntry ) + { + m_mappings.setMethodName( (MethodEntry)entry, newName ); + } + else if( entry instanceof ArgumentEntry ) + { + m_mappings.setArgumentName( (ArgumentEntry)entry, newName ); + } + else + { + throw new Error( "Unknown entry type: " + entry.getClass().getName() ); + } + } + + public Entry obfuscate( Entry in ) + { + Translator translator = m_mappings.getTranslator( TranslationDirection.Obfuscating ); + if( in instanceof ClassEntry ) + { + return translator.translateEntry( (ClassEntry)in ); + } + else if( in instanceof FieldEntry ) + { + return translator.translateEntry( (FieldEntry)in ); + } + else if( in instanceof MethodEntry ) + { + return translator.translateEntry( (MethodEntry)in ); + } + else if( in instanceof ArgumentEntry ) + { + return translator.translateEntry( (ArgumentEntry)in ); + } + else + { + throw new Error( "Unknown entry type: " + in.getClass().getName() ); + } + } + + public Entry deobfuscate( Entry in ) + { + Translator translator = m_mappings.getTranslator( TranslationDirection.Deobfuscating ); + if( in instanceof ClassEntry ) + { + return translator.translateEntry( (ClassEntry)in ); + } + else if( in instanceof FieldEntry ) + { + return translator.translateEntry( (FieldEntry)in ); + } + else if( in instanceof MethodEntry ) + { + return translator.translateEntry( (MethodEntry)in ); + } + else if( in instanceof ArgumentEntry ) + { + return translator.translateEntry( (ArgumentEntry)in ); + } + else + { + throw new Error( "Unknown entry type: " + in.getClass().getName() ); + } + } } diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java new file mode 100644 index 00000000..872f4861 --- /dev/null +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.IOException; +import java.io.InputStream; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import javassist.ByteArrayClassPath; +import javassist.ClassPool; +import javassist.CtClass; + +import com.strobel.assembler.metadata.Buffer; +import com.strobel.assembler.metadata.ITypeLoader; + +import cuchaz.enigma.bytecode.ClassTranslator; +import cuchaz.enigma.mapping.Translator; + +public class TranslatingTypeLoader implements ITypeLoader +{ + private JarFile m_jar; + private ClassTranslator m_classTranslator; + private Translator m_obfuscatingTranslator; + + public TranslatingTypeLoader( JarFile jar, Translator deobfuscatingTranslator, Translator obfuscatingTranslator ) + { + m_jar = jar; + m_classTranslator = new ClassTranslator( deobfuscatingTranslator ); + m_obfuscatingTranslator = obfuscatingTranslator; + } + + @Override + public boolean tryLoadType( String name, Buffer out ) + { + // is this a deobufscated class name? + String obfName = m_obfuscatingTranslator.translateClass( name ); + if( obfName != null ) + { + // point to the obfuscated class + name = obfName; + } + + JarEntry entry = m_jar.getJarEntry( name + ".class" ); + if( entry == null ) + { + return false; + } + + try + { + // read the class file into a buffer + byte[] buf = new byte[(int)entry.getSize()]; + InputStream in = m_jar.getInputStream( entry ); + int bytesRead = in.read( buf ); + assert( bytesRead == buf.length ); + + // translate the class + ClassPool classPool = new ClassPool(); + classPool.insertClassPath( new ByteArrayClassPath( name, buf ) ); + try + { + CtClass c = classPool.get( name ); + m_classTranslator.translate( c ); + buf = c.toBytecode(); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + + // pass it along to the decompiler + out.reset( buf.length ); + System.arraycopy( buf, 0, out.array(), out.position(), buf.length ); + out.position( 0 ); + + return true; + } + catch( IOException ex ) + { + throw new Error( ex ); + } + } + +} diff --git a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java new file mode 100644 index 00000000..aadbeb25 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Iterator; + +import javassist.bytecode.BadBytecode; +import javassist.bytecode.Bytecode; +import javassist.bytecode.CodeAttribute; +import javassist.bytecode.CodeIterator; +import javassist.bytecode.Opcode; + +public class BytecodeIndexIterator implements Iterator +{ + public static class Index + { + private CodeIterator m_iter; + private int m_pos; + private boolean m_isWide; + + protected Index( CodeIterator iter, int pos, boolean isWide ) + { + m_iter = iter; + m_pos = pos; + m_isWide = isWide; + } + + public int getIndex( ) + { + if( m_isWide ) + { + return m_iter.s16bitAt( m_pos ); + } + else + { + return m_iter.byteAt( m_pos ); + } + } + + public void setIndex( int val ) + throws BadBytecode + { + if( m_isWide ) + { + m_iter.write16bit( val, m_pos ); + } + else + { + if( val < 256 ) + { + // we can write the byte + m_iter.writeByte( val, m_pos ); + } + else + { + // we need to upgrade this instruction to LDC_W + assert( m_iter.byteAt( m_pos - 1 ) == Opcode.LDC ); + m_iter.insertGap( m_pos - 1, 1 ); + m_iter.writeByte( Opcode.LDC_W, m_pos - 1 ); + m_iter.write16bit( val, m_pos ); + m_isWide = true; + + // move the iterator to the next opcode + m_iter.move( m_pos + 2 ); + } + } + + // sanity check + assert( val == getIndex() ); + } + + public boolean isValid( Bytecode bytecode ) + { + return getIndex() >= 0 && getIndex() < bytecode.getConstPool().getSize(); + } + } + + private Bytecode m_bytecode; + private CodeAttribute m_attribute; + private CodeIterator m_iter; + private Index m_next; + + public BytecodeIndexIterator( Bytecode bytecode ) + throws BadBytecode + { + m_bytecode = bytecode; + m_attribute = bytecode.toCodeAttribute(); + m_iter = m_attribute.iterator(); + + m_next = getNext(); + } + + @Override + public boolean hasNext( ) + { + return m_next != null; + } + + @Override + public Index next( ) + { + Index out = m_next; + try + { + m_next = getNext(); + } + catch( BadBytecode ex ) + { + throw new Error( ex ); + } + return out; + } + + @Override + public void remove( ) + { + throw new UnsupportedOperationException(); + } + + private Index getNext( ) + throws BadBytecode + { + while( m_iter.hasNext() ) + { + int pos = m_iter.next(); + int opcode = m_iter.byteAt( pos ); + switch( opcode ) + { + // for only these opcodes, the next two bytes are a const pool reference + case Opcode.ANEWARRAY: + case Opcode.CHECKCAST: + case Opcode.INSTANCEOF: + case Opcode.INVOKEDYNAMIC: + case Opcode.INVOKEINTERFACE: + case Opcode.INVOKESPECIAL: + case Opcode.INVOKESTATIC: + case Opcode.INVOKEVIRTUAL: + case Opcode.LDC_W: + case Opcode.LDC2_W: + case Opcode.MULTIANEWARRAY: + case Opcode.NEW: + case Opcode.PUTFIELD: + case Opcode.PUTSTATIC: + case Opcode.GETFIELD: + case Opcode.GETSTATIC: + return new Index( m_iter, pos + 1, true ); + + case Opcode.LDC: + return new Index( m_iter, pos + 1, false ); + } + } + + return null; + } + + public Iterable indices( ) + { + return new Iterable( ) + { + @Override + public Iterator iterator( ) + { + return BytecodeIndexIterator.this; + } + }; + } + + public void saveChangesToBytecode( ) + { + BytecodeTools.setBytecode( m_bytecode, m_attribute.getCode() ); + } +} diff --git a/src/cuchaz/enigma/bytecode/BytecodeTools.java b/src/cuchaz/enigma/bytecode/BytecodeTools.java new file mode 100644 index 00000000..664350ea --- /dev/null +++ b/src/cuchaz/enigma/bytecode/BytecodeTools.java @@ -0,0 +1,269 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; +import java.util.Set; + +import javassist.CtBehavior; +import javassist.bytecode.BadBytecode; +import javassist.bytecode.Bytecode; +import javassist.bytecode.CodeAttribute; +import javassist.bytecode.ConstPool; +import javassist.bytecode.ExceptionTable; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import cuchaz.enigma.Util; +import cuchaz.enigma.bytecode.BytecodeIndexIterator.Index; +import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; + +public class BytecodeTools +{ + public static byte[] writeBytecode( Bytecode bytecode ) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream( buf ); + try + { + // write the constant pool + new ConstPoolEditor( bytecode.getConstPool() ).writePool( out ); + + // write metadata + out.writeShort( bytecode.getMaxStack() ); + out.writeShort( bytecode.getMaxLocals() ); + out.writeShort( bytecode.getStackDepth() ); + + // write the code + out.writeShort( bytecode.getSize() ); + out.write( bytecode.get() ); + + // write the exception table + int numEntries = bytecode.getExceptionTable().size(); + out.writeShort( numEntries ); + for( int i=0; i attribute.getMaxLocals() ) + { + attribute.setMaxLocals( bytecode.getMaxLocals() ); + } + if( bytecode.getMaxStack() > attribute.getMaxStack() ) + { + attribute.setMaxStack( bytecode.getMaxStack() ); + } + + return bytecode; + } + + public static Bytecode copyBytecodeToConstPool( ConstPool dest, Bytecode bytecode ) + throws BadBytecode + { + // get the entries this bytecode needs from the const pool + Set indices = Sets.newTreeSet(); + ConstPoolEditor editor = new ConstPoolEditor( bytecode.getConstPool() ); + BytecodeIndexIterator iterator = new BytecodeIndexIterator( bytecode ); + for( Index index : iterator.indices() ) + { + assert( index.isValid( bytecode ) ); + InfoType.gatherIndexTree( indices, editor, index.getIndex() ); + } + + Map indexMap = Maps.newTreeMap(); + + ConstPool src = bytecode.getConstPool(); + ConstPoolEditor editorSrc = new ConstPoolEditor( src ); + ConstPoolEditor editorDest = new ConstPoolEditor( dest ); + + // copy entries over in order of level so the index mapping is easier + for( InfoType type : InfoType.getSortedByLevel() ) + { + for( int index : indices ) + { + ConstInfoAccessor entry = editorSrc.getItem( index ); + + // skip entries that aren't this type + if( entry.getType() != type ) + { + continue; + } + + // make sure the source entry is valid before we copy it + assert( type.subIndicesAreValid( entry, editorSrc ) ); + assert( type.selfIndexIsValid( entry, editorSrc ) ); + + // make a copy of the entry so we can modify it safely + ConstInfoAccessor entryCopy = editorSrc.getItem( index ).copy(); + assert( type.subIndicesAreValid( entryCopy, editorSrc ) ); + assert( type.selfIndexIsValid( entryCopy, editorSrc ) ); + + // remap the indices + type.remapIndices( indexMap, entryCopy ); + assert( type.subIndicesAreValid( entryCopy, editorDest ) ); + + // put the copy in the destination pool + int newIndex = editorDest.addItem( entryCopy.getItem() ); + entryCopy.setIndex( newIndex ); + assert( type.selfIndexIsValid( entryCopy, editorDest ) ) : type + ", self: " + entryCopy + " dest: " + editorDest.getItem( entryCopy.getIndex() ); + + // make sure the source entry is unchanged + assert( type.subIndicesAreValid( entry, editorSrc ) ); + assert( type.selfIndexIsValid( entry, editorSrc ) ); + + // add the index mapping so we can update the bytecode later + if( indexMap.containsKey( index ) ) + { + throw new Error( "Entry at index " + index + " already copied!" ); + } + indexMap.put( index, newIndex ); + } + } + + // make a new bytecode + Bytecode newBytecode = new Bytecode( dest, bytecode.getMaxStack(), bytecode.getMaxLocals() ); + bytecode.setStackDepth( bytecode.getStackDepth() ); + setBytecode( newBytecode, bytecode.get() ); + setExceptionTable( newBytecode, bytecode.getExceptionTable() ); + + // apply the mappings to the bytecode + BytecodeIndexIterator iter = new BytecodeIndexIterator( newBytecode ); + for( Index index : iter.indices() ) + { + int oldIndex = index.getIndex(); + Integer newIndex = indexMap.get( oldIndex ); + if( newIndex != null ) + { + // make sure this mapping makes sense + InfoType typeSrc = editorSrc.getItem( oldIndex ).getType(); + InfoType typeDest = editorDest.getItem( newIndex ).getType(); + assert( typeSrc == typeDest ); + + // apply the mapping + index.setIndex( newIndex ); + } + } + iter.saveChangesToBytecode(); + + // make sure all the indices are valid + iter = new BytecodeIndexIterator( newBytecode ); + for( Index index : iter.indices() ) + { + assert( index.isValid( newBytecode ) ); + } + + return newBytecode; + } + + public static void setBytecode( Bytecode dest, byte[] src ) + { + if( src.length > dest.getSize() ) + { + dest.addGap( src.length - dest.getSize() ); + } + assert( dest.getSize() == src.length ); + for( int i=0; i=0; i-- ) + { + dest.getExceptionTable().remove( i ); + } + + // copy the exception table + for( int i=0; i classNames = getAllClassNames( c ); + ClassMap map = new ClassMap(); + for( String className : classNames ) + { + String translatedName = m_translator.translateClass( className ); + if( translatedName != null ) + { + map.put( className, translatedName ); + } + } + if( !map.isEmpty() ) + { + c.replaceClassName( map ); + } + } + + private Set getAllClassNames( CtClass c ) + { + final Set names = new HashSet(); + ClassMap map = new ClassMap( ) + { + @Override + public Object get( Object obj ) + { + if( obj instanceof String ) + { + names.add( (String)obj ); + } + return null; + } + private static final long serialVersionUID = -202160293602070641L; + }; + c.replaceClassName( map ); + return names; + } +} diff --git a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java new file mode 100644 index 00000000..aa6149c9 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java @@ -0,0 +1,316 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; + +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor; +import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; +import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; + +public class ConstPoolEditor +{ + private static Method m_getItem; + private static Method m_addItem; + private static Method m_addItem0; + private static Field m_items; + private static Field m_cache; + private static Field m_numItems; + private static Field m_objects; + private static Field m_elements; + private static Method m_methodWritePool; + private static Constructor m_constructorPool; + + static + { + try + { + m_getItem = ConstPool.class.getDeclaredMethod( "getItem", int.class ); + m_getItem.setAccessible( true ); + + m_addItem = ConstPool.class.getDeclaredMethod( "addItem", Class.forName( "javassist.bytecode.ConstInfo" ) ); + m_addItem.setAccessible( true ); + + m_addItem0 = ConstPool.class.getDeclaredMethod( "addItem0", Class.forName( "javassist.bytecode.ConstInfo" ) ); + m_addItem0.setAccessible( true ); + + m_items = ConstPool.class.getDeclaredField( "items" ); + m_items.setAccessible( true ); + + m_cache = ConstPool.class.getDeclaredField( "itemsCache" ); + m_cache.setAccessible( true ); + + m_numItems = ConstPool.class.getDeclaredField( "numOfItems" ); + m_numItems.setAccessible( true ); + + m_objects = Class.forName( "javassist.bytecode.LongVector" ).getDeclaredField( "objects" ); + m_objects.setAccessible( true ); + + m_elements = Class.forName( "javassist.bytecode.LongVector" ).getDeclaredField( "elements" ); + m_elements.setAccessible( true ); + + m_methodWritePool = ConstPool.class.getDeclaredMethod( "write", DataOutputStream.class ); + m_methodWritePool.setAccessible( true ); + + m_constructorPool = ConstPool.class.getDeclaredConstructor( DataInputStream.class ); + m_constructorPool.setAccessible( true ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + private ConstPool m_pool; + + public ConstPoolEditor( ConstPool pool ) + { + m_pool = pool; + } + + public void writePool( DataOutputStream out ) + { + try + { + m_methodWritePool.invoke( m_pool, out ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public static ConstPool readPool( DataInputStream in ) + { + try + { + return m_constructorPool.newInstance( in ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public String getMemberrefClassname( int memberrefIndex ) + { + return Descriptor.toJvmName( m_pool.getClassInfo( m_pool.getMemberClass( memberrefIndex ) ) ); + } + + public String getMemberrefName( int memberrefIndex ) + { + return m_pool.getUtf8Info( m_pool.getNameAndTypeName( m_pool.getMemberNameAndType( memberrefIndex ) ) ); + } + + public String getMemberrefType( int memberrefIndex ) + { + return m_pool.getUtf8Info( m_pool.getNameAndTypeDescriptor( m_pool.getMemberNameAndType( memberrefIndex ) ) ); + } + + public ConstInfoAccessor getItem( int index ) + { + try + { + Object entry = m_getItem.invoke( m_pool, index ); + if( entry == null ) + { + return null; + } + return new ConstInfoAccessor( entry ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public int addItem( Object item ) + { + try + { + return (Integer)m_addItem.invoke( m_pool, item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public int addItemForceNew( Object item ) + { + try + { + return (Integer)m_addItem0.invoke( m_pool, item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + @SuppressWarnings( "rawtypes" ) + public void removeLastItem( ) + { + try + { + // remove the item from the cache + HashMap cache = getCache(); + if( cache != null ) + { + Object item = getItem( m_pool.getSize() - 1 ); + cache.remove( item ); + } + + // remove the actual item + // based off of LongVector.addElement() + Object items = m_items.get( m_pool ); + Object[][] objects = (Object[][])m_objects.get( items ); + int numElements = (Integer)m_elements.get( items ) - 1; + int nth = numElements >> 7; + int offset = numElements & (128 - 1); + objects[nth][offset] = null; + + // decrement the number of items + m_elements.set( items, numElements ); + m_numItems.set( m_pool, (Integer)m_numItems.get( m_pool ) - 1 ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + @SuppressWarnings( "rawtypes" ) + /* TEMP */public HashMap getCache( ) + { + try + { + return (HashMap)m_cache.get( m_pool ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + @SuppressWarnings( { "rawtypes", "unchecked" } ) + public void changeMemberrefNameAndType( int memberrefIndex, String newName, String newType ) + { + // NOTE: when changing values, we always need to copy-on-write + try + { + // get the memberref item + Object item = getItem( memberrefIndex ).getItem(); + + // update the cache + HashMap cache = getCache(); + if( cache != null ) + { + cache.remove( item ); + } + + new MemberRefInfoAccessor( item ).setNameAndTypeIndex( m_pool.addNameAndTypeInfo( newName, newType ) ); + + // update the cache + if( cache != null ) + { + cache.put( item, item ); + } + } + catch( Exception ex ) + { + throw new Error( ex ); + } + + // make sure the change worked + assert( newName.equals( getMemberrefName( memberrefIndex ) ) ); + assert( newType.equals( getMemberrefType( memberrefIndex ) ) ); + } + + @SuppressWarnings( { "rawtypes", "unchecked" } ) + public void changeClassName( int classNameIndex, String newName ) + { + // NOTE: when changing values, we always need to copy-on-write + try + { + // get the class item + Object item = getItem( classNameIndex ).getItem(); + + // update the cache + HashMap cache = getCache(); + if( cache != null ) + { + cache.remove( item ); + } + + // add the new name and repoint the name-and-type to it + new ClassInfoAccessor( item ).setNameIndex( m_pool.addUtf8Info( newName ) ); + + // update the cache + if( cache != null ) + { + cache.put( item, item ); + } + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public static ConstPool newConstPool( ) + { + // const pool expects the name of a class to initialize itself + // but we want an empty pool + // so give it a bogus name, and then clear the entries afterwards + ConstPool pool = new ConstPool( "a" ); + + ConstPoolEditor editor = new ConstPoolEditor( pool ); + int size = pool.getSize(); + for( int i=0; i indices, ConstPoolEditor editor, ConstInfoAccessor entry ) + { + ClassInfoAccessor accessor = new ClassInfoAccessor( entry.getItem() ); + gatherIndexTree( indices, editor, accessor.getNameIndex() ); + } + + @Override + public void remapIndices( Map map, ConstInfoAccessor entry ) + { + ClassInfoAccessor accessor = new ClassInfoAccessor( entry.getItem() ); + accessor.setNameIndex( remapIndex( map, accessor.getNameIndex() ) ); + } + + @Override + public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) + { + ClassInfoAccessor accessor = new ClassInfoAccessor( entry.getItem() ); + ConstInfoAccessor nameEntry = pool.getItem( accessor.getNameIndex() ); + return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag(); + } + }, + StringInfo( 8, 1 ) + { + @Override + public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) + { + StringInfoAccessor accessor = new StringInfoAccessor( entry.getItem() ); + gatherIndexTree( indices, editor, accessor.getStringIndex() ); + } + + @Override + public void remapIndices( Map map, ConstInfoAccessor entry ) + { + StringInfoAccessor accessor = new StringInfoAccessor( entry.getItem() ); + accessor.setStringIndex( remapIndex( map, accessor.getStringIndex() ) ); + } + + @Override + public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) + { + StringInfoAccessor accessor = new StringInfoAccessor( entry.getItem() ); + ConstInfoAccessor stringEntry = pool.getItem( accessor.getStringIndex() ); + return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag(); + } + }, + FieldRefInfo( 9, 2 ) + { + @Override + public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) + { + MemberRefInfoAccessor accessor = new MemberRefInfoAccessor( entry.getItem() ); + gatherIndexTree( indices, editor, accessor.getClassIndex() ); + gatherIndexTree( indices, editor, accessor.getNameAndTypeIndex() ); + } + + @Override + public void remapIndices( Map map, ConstInfoAccessor entry ) + { + MemberRefInfoAccessor accessor = new MemberRefInfoAccessor( entry.getItem() ); + accessor.setClassIndex( remapIndex( map, accessor.getClassIndex() ) ); + accessor.setNameAndTypeIndex( remapIndex( map, accessor.getNameAndTypeIndex() ) ); + } + + @Override + public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) + { + MemberRefInfoAccessor accessor = new MemberRefInfoAccessor( entry.getItem() ); + ConstInfoAccessor classEntry = pool.getItem( accessor.getClassIndex() ); + ConstInfoAccessor nameAndTypeEntry = pool.getItem( accessor.getNameAndTypeIndex() ); + return classEntry != null && classEntry.getTag() == ClassInfo.getTag() + && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); + } + }, + MethodRefInfo( 10, 2 ) // same as FieldRefInfo + { + @Override + public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) + { + FieldRefInfo.gatherIndexTree( indices, editor, entry ); + } + + @Override + public void remapIndices( Map map, ConstInfoAccessor entry ) + { + FieldRefInfo.remapIndices( map, entry ); + } + + @Override + public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) + { + return FieldRefInfo.subIndicesAreValid( entry, pool ); + } + }, + InterfaceMethodRefInfo( 11, 2 ) // same as FieldRefInfo + { + @Override + public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) + { + FieldRefInfo.gatherIndexTree( indices, editor, entry ); + } + + @Override + public void remapIndices( Map map, ConstInfoAccessor entry ) + { + FieldRefInfo.remapIndices( map, entry ); + } + + @Override + public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) + { + return FieldRefInfo.subIndicesAreValid( entry, pool ); + } + }, + NameAndTypeInfo( 12, 1 ) + { + @Override + public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) + { + NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor( entry.getItem() ); + gatherIndexTree( indices, editor, accessor.getNameIndex() ); + gatherIndexTree( indices, editor, accessor.getTypeIndex() ); + } + + @Override + public void remapIndices( Map map, ConstInfoAccessor entry ) + { + NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor( entry.getItem() ); + accessor.setNameIndex( remapIndex( map, accessor.getNameIndex() ) ); + accessor.setTypeIndex( remapIndex( map, accessor.getTypeIndex() ) ); + } + + @Override + public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) + { + NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor( entry.getItem() ); + ConstInfoAccessor nameEntry = pool.getItem( accessor.getNameIndex() ); + ConstInfoAccessor typeEntry = pool.getItem( accessor.getTypeIndex() ); + return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() + && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); + } + }, + MethodHandleInfo( 15, 3 ) + { + @Override + public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) + { + MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor( entry.getItem() ); + gatherIndexTree( indices, editor, accessor.getTypeIndex() ); + gatherIndexTree( indices, editor, accessor.getMethodRefIndex() ); + } + + @Override + public void remapIndices( Map map, ConstInfoAccessor entry ) + { + MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor( entry.getItem() ); + accessor.setTypeIndex( remapIndex( map, accessor.getTypeIndex() ) ); + accessor.setMethodRefIndex( remapIndex( map, accessor.getMethodRefIndex() ) ); + } + + @Override + public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) + { + MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor( entry.getItem() ); + ConstInfoAccessor typeEntry = pool.getItem( accessor.getTypeIndex() ); + ConstInfoAccessor methodRefEntry = pool.getItem( accessor.getMethodRefIndex() ); + return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() + && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag(); + } + }, + MethodTypeInfo( 16, 1 ) + { + @Override + public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) + { + MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor( entry.getItem() ); + gatherIndexTree( indices, editor, accessor.getTypeIndex() ); + } + + @Override + public void remapIndices( Map map, ConstInfoAccessor entry ) + { + MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor( entry.getItem() ); + accessor.setTypeIndex( remapIndex( map, accessor.getTypeIndex() ) ); + } + + @Override + public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) + { + MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor( entry.getItem() ); + ConstInfoAccessor typeEntry = pool.getItem( accessor.getTypeIndex() ); + return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); + } + }, + InvokeDynamicInfo( 18, 2 ) + { + @Override + public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) + { + InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor( entry.getItem() ); + gatherIndexTree( indices, editor, accessor.getBootstrapIndex() ); + gatherIndexTree( indices, editor, accessor.getNameAndTypeIndex() ); + } + + @Override + public void remapIndices( Map map, ConstInfoAccessor entry ) + { + InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor( entry.getItem() ); + accessor.setBootstrapIndex( remapIndex( map, accessor.getBootstrapIndex() ) ); + accessor.setNameAndTypeIndex( remapIndex( map, accessor.getNameAndTypeIndex() ) ); + } + + @Override + public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) + { + InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor( entry.getItem() ); + ConstInfoAccessor bootstrapEntry = pool.getItem( accessor.getBootstrapIndex() ); + ConstInfoAccessor nameAndTypeEntry = pool.getItem( accessor.getNameAndTypeIndex() ); + return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() + && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); + } + }; + + private static Map m_types; + + static + { + m_types = Maps.newTreeMap(); + for( InfoType type : values() ) + { + m_types.put( type.getTag(), type ); + } + } + + private int m_tag; + private int m_level; + + private InfoType( int tag, int level ) + { + m_tag = tag; + m_level = level; + } + + public int getTag( ) + { + return m_tag; + } + + public int getLevel( ) + { + return m_level; + } + + public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) + { + // by default, do nothing + } + + public void remapIndices( Map map, ConstInfoAccessor entry ) + { + // by default, do nothing + } + + public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) + { + // by default, everything is good + return true; + } + + public boolean selfIndexIsValid( ConstInfoAccessor entry, ConstPoolEditor pool ) + { + ConstInfoAccessor entryCheck = pool.getItem( entry.getIndex() ); + if( entryCheck == null ) + { + return false; + } + return entryCheck.getItem().equals( entry.getItem() ); + } + + public static InfoType getByTag( int tag ) + { + return m_types.get( tag ); + } + + public static List getByLevel( int level ) + { + List types = Lists.newArrayList(); + for( InfoType type : values() ) + { + if( type.getLevel() == level ) + { + types.add( type ); + } + } + return types; + } + + public static List getSortedByLevel( ) + { + List types = Lists.newArrayList(); + types.addAll( getByLevel( 0 ) ); + types.addAll( getByLevel( 1 ) ); + types.addAll( getByLevel( 2 ) ); + types.addAll( getByLevel( 3 ) ); + return types; + } + + public static void gatherIndexTree( Collection indices, ConstPoolEditor editor, int index ) + { + // add own index + indices.add( index ); + + // recurse + ConstInfoAccessor entry = editor.getItem( index ); + entry.getType().gatherIndexTree( indices, editor, entry ); + } + + private static int remapIndex( Map map, int index ) + { + Integer newIndex = map.get( index ); + if( newIndex == null ) + { + newIndex = index; + } + return newIndex; + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java new file mode 100644 index 00000000..41e1d047 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class ClassInfoAccessor +{ + private static Class m_class; + private static Field m_nameIndex; + + static + { + try + { + m_class = Class.forName( "javassist.bytecode.ClassInfo" ); + m_nameIndex = m_class.getDeclaredField( "name" ); + m_nameIndex.setAccessible( true ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public static boolean isType( ConstInfoAccessor accessor ) + { + return m_class.isAssignableFrom( accessor.getItem().getClass() ); + } + + private Object m_item; + + public ClassInfoAccessor( Object item ) + { + m_item = item; + } + + public int getNameIndex( ) + { + try + { + return (Integer)m_nameIndex.get( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void setNameIndex( int val ) + { + try + { + m_nameIndex.set( m_item, val ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java new file mode 100644 index 00000000..3c3d3fa4 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import cuchaz.enigma.bytecode.InfoType; + +public class ConstInfoAccessor +{ + private static Class m_class; + private static Field m_index; + private static Method m_getTag; + + static + { + try + { + m_class = Class.forName( "javassist.bytecode.ConstInfo" ); + m_index = m_class.getDeclaredField( "index" ); + m_index.setAccessible( true ); + m_getTag = m_class.getMethod( "getTag" ); + m_getTag.setAccessible( true ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + private Object m_item; + + public ConstInfoAccessor( Object item ) + { + if( item == null ) + { + throw new IllegalArgumentException( "item cannot be null!" ); + } + m_item = item; + } + + public ConstInfoAccessor( DataInputStream in ) + throws IOException + { + try + { + // read the entry + String className = in.readUTF(); + int oldIndex = in.readInt(); + + // NOTE: ConstInfo instances write a type id (a "tag"), but they don't read it back + // so we have to read it here + in.readByte(); + + Constructor constructor = Class.forName( className ).getConstructor( DataInputStream.class, int.class ); + constructor.setAccessible( true ); + m_item = constructor.newInstance( in, oldIndex ); + } + catch( IOException ex ) + { + throw ex; + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public Object getItem( ) + { + return m_item; + } + + public int getIndex( ) + { + try + { + return (Integer)m_index.get( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void setIndex( int val ) + { + try + { + m_index.set( m_item, val ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public int getTag( ) + { + try + { + return (Integer)m_getTag.invoke( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public ConstInfoAccessor copy( ) + { + return new ConstInfoAccessor( copyItem() ); + } + + public Object copyItem( ) + { + // I don't know of a simpler way to copy one of these silly things... + try + { + // serialize the item + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream( buf ); + write( out ); + + // deserialize the item + DataInputStream in = new DataInputStream( new ByteArrayInputStream( buf.toByteArray() ) ); + Object item = new ConstInfoAccessor( in ).getItem(); + in.close(); + + return item; + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void write( DataOutputStream out ) + throws IOException + { + try + { + out.writeUTF( m_item.getClass().getName() ); + out.writeInt( getIndex() ); + + Method method = m_item.getClass().getMethod( "write", DataOutputStream.class ); + method.setAccessible( true ); + method.invoke( m_item, out ); + } + catch( IOException ex ) + { + throw ex; + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + @Override + public String toString( ) + { + try + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + PrintWriter out = new PrintWriter( buf ); + Method print = m_item.getClass().getMethod( "print", PrintWriter.class ); + print.setAccessible( true ); + print.invoke( m_item, out ); + out.close(); + return buf.toString().replace( "\n", "" ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public InfoType getType( ) + { + return InfoType.getByTag( getTag() ); + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java new file mode 100644 index 00000000..169306a4 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class InvokeDynamicInfoAccessor +{ + private static Class m_class; + private static Field m_bootstrapIndex; + private static Field m_nameAndTypeIndex; + + static + { + try + { + m_class = Class.forName( "javassist.bytecode.InvokeDynamicInfo" ); + m_bootstrapIndex = m_class.getDeclaredField( "bootstrap" ); + m_bootstrapIndex.setAccessible( true ); + m_nameAndTypeIndex = m_class.getDeclaredField( "nameAndType" ); + m_nameAndTypeIndex.setAccessible( true ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public static boolean isType( ConstInfoAccessor accessor ) + { + return m_class.isAssignableFrom( accessor.getItem().getClass() ); + } + + private Object m_item; + + public InvokeDynamicInfoAccessor( Object item ) + { + m_item = item; + } + + public int getBootstrapIndex( ) + { + try + { + return (Integer)m_bootstrapIndex.get( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void setBootstrapIndex( int val ) + { + try + { + m_bootstrapIndex.set( m_item, val ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public int getNameAndTypeIndex( ) + { + try + { + return (Integer)m_nameAndTypeIndex.get( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void setNameAndTypeIndex( int val ) + { + try + { + m_nameAndTypeIndex.set( m_item, val ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java new file mode 100644 index 00000000..2ee3aff8 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class MemberRefInfoAccessor +{ + private static Class m_class; + private static Field m_classIndex; + private static Field m_nameAndTypeIndex; + + static + { + try + { + m_class = Class.forName( "javassist.bytecode.MemberrefInfo" ); + m_classIndex = m_class.getDeclaredField( "classIndex" ); + m_classIndex.setAccessible( true ); + m_nameAndTypeIndex = m_class.getDeclaredField( "nameAndTypeIndex" ); + m_nameAndTypeIndex.setAccessible( true ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public static boolean isType( ConstInfoAccessor accessor ) + { + return m_class.isAssignableFrom( accessor.getItem().getClass() ); + } + + private Object m_item; + + public MemberRefInfoAccessor( Object item ) + { + m_item = item; + } + + public int getClassIndex( ) + { + try + { + return (Integer)m_classIndex.get( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void setClassIndex( int val ) + { + try + { + m_classIndex.set( m_item, val ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public int getNameAndTypeIndex( ) + { + try + { + return (Integer)m_nameAndTypeIndex.get( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void setNameAndTypeIndex( int val ) + { + try + { + m_nameAndTypeIndex.set( m_item, val ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java new file mode 100644 index 00000000..27b7aee8 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class MethodHandleInfoAccessor +{ + private static Class m_class; + private static Field m_kindIndex; + private static Field m_indexIndex; + + static + { + try + { + m_class = Class.forName( "javassist.bytecode.MethodHandleInfo" ); + m_kindIndex = m_class.getDeclaredField( "refKind" ); + m_kindIndex.setAccessible( true ); + m_indexIndex = m_class.getDeclaredField( "refIndex" ); + m_indexIndex.setAccessible( true ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public static boolean isType( ConstInfoAccessor accessor ) + { + return m_class.isAssignableFrom( accessor.getItem().getClass() ); + } + + private Object m_item; + + public MethodHandleInfoAccessor( Object item ) + { + m_item = item; + } + + public int getTypeIndex( ) + { + try + { + return (Integer)m_kindIndex.get( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void setTypeIndex( int val ) + { + try + { + m_kindIndex.set( m_item, val ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public int getMethodRefIndex( ) + { + try + { + return (Integer)m_indexIndex.get( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void setMethodRefIndex( int val ) + { + try + { + m_indexIndex.set( m_item, val ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java new file mode 100644 index 00000000..4cba6a2a --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class MethodTypeInfoAccessor +{ + private static Class m_class; + private static Field m_descriptorIndex; + + static + { + try + { + m_class = Class.forName( "javassist.bytecode.MethodTypeInfo" ); + m_descriptorIndex = m_class.getDeclaredField( "descriptor" ); + m_descriptorIndex.setAccessible( true ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public static boolean isType( ConstInfoAccessor accessor ) + { + return m_class.isAssignableFrom( accessor.getItem().getClass() ); + } + + private Object m_item; + + public MethodTypeInfoAccessor( Object item ) + { + m_item = item; + } + + public int getTypeIndex( ) + { + try + { + return (Integer)m_descriptorIndex.get( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void setTypeIndex( int val ) + { + try + { + m_descriptorIndex.set( m_item, val ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java new file mode 100644 index 00000000..03b4de3c --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class NameAndTypeInfoAccessor +{ + private static Class m_class; + private static Field m_nameIndex; + private static Field m_typeIndex; + + static + { + try + { + m_class = Class.forName( "javassist.bytecode.NameAndTypeInfo" ); + m_nameIndex = m_class.getDeclaredField( "memberName" ); + m_nameIndex.setAccessible( true ); + m_typeIndex = m_class.getDeclaredField( "typeDescriptor" ); + m_typeIndex.setAccessible( true ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public static boolean isType( ConstInfoAccessor accessor ) + { + return m_class.isAssignableFrom( accessor.getItem().getClass() ); + } + + private Object m_item; + + public NameAndTypeInfoAccessor( Object item ) + { + m_item = item; + } + + public int getNameIndex( ) + { + try + { + return (Integer)m_nameIndex.get( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void setNameIndex( int val ) + { + try + { + m_nameIndex.set( m_item, val ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public int getTypeIndex( ) + { + try + { + return (Integer)m_typeIndex.get( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void setTypeIndex( int val ) + { + try + { + m_typeIndex.set( m_item, val ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java new file mode 100644 index 00000000..5cdfce4d --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class StringInfoAccessor +{ + private static Class m_class; + private static Field m_stringIndex; + + static + { + try + { + m_class = Class.forName( "javassist.bytecode.StringInfo" ); + m_stringIndex = m_class.getDeclaredField( "string" ); + m_stringIndex.setAccessible( true ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public static boolean isType( ConstInfoAccessor accessor ) + { + return m_class.isAssignableFrom( accessor.getItem().getClass() ); + } + + private Object m_item; + + public StringInfoAccessor( Object item ) + { + m_item = item; + } + + public int getStringIndex( ) + { + try + { + return (Integer)m_stringIndex.get( m_item ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public void setStringIndex( int val ) + { + try + { + m_stringIndex.set( m_item, val ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java new file mode 100644 index 00000000..1cadd836 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +public class Utf8InfoAccessor +{ + private static Class m_class; + + static + { + try + { + m_class = Class.forName( "javassist.bytecode.Utf8Info" ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + + public static boolean isType( ConstInfoAccessor accessor ) + { + return m_class.isAssignableFrom( accessor.getItem().getClass() ); + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index d1a3cb21..2a539a3f 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -45,7 +45,7 @@ import cuchaz.enigma.ClassFile; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; @@ -69,7 +69,7 @@ public class Gui private RenameListener m_renameListener; private BoxHighlightPainter m_highlightPainter; - private Entry m_selectedEntry; + private EntryPair m_selectedEntryPair; public Gui( ) { @@ -128,9 +128,9 @@ public class Gui @Override public void actionPerformed( ActionEvent event ) { - if( m_renameListener != null && m_selectedEntry != null ) + if( m_renameListener != null && m_selectedEntryPair != null ) { - m_renameListener.rename( m_selectedEntry, m_nameField.getText() ); + m_renameListener.rename( m_selectedEntryPair.obf, m_nameField.getText() ); } } } ); @@ -142,7 +142,7 @@ public class Gui m_renamePanel.add( m_typeLabel ); m_renamePanel.add( m_nameField ); m_renamePanel.add( m_renameButton ); - clearEntry(); + clearEntryPair(); // init editor DefaultSyntaxKit.initKit(); @@ -241,7 +241,7 @@ public class Gui m_editor.addCaretListener( listener ); } - public void clearEntry( ) + public void clearEntryPair( ) { m_actionPanel.removeAll(); JLabel label = new JLabel( "No identifier selected" ); @@ -251,67 +251,72 @@ public class Gui redraw(); } - - public void showEntry( Entry entry ) + + public void showEntryPair( EntryPair pair ) { - if( entry == null ) + if( pair == null ) { - clearEntry(); + clearEntryPair(); return; } + // TEMP + System.out.println( "Pair:\n" + pair.obf + "\n" + pair.deobf ); + + m_selectedEntryPair = pair; + // layout the action panel m_actionPanel.removeAll(); m_actionPanel.add( m_renamePanel ); - m_nameField.setText( entry.getName() ); + m_nameField.setText( pair.deobf.getName() ); // layout the dynamic section JPanel dynamicPanel = new JPanel(); dynamicPanel.setLayout( new GridLayout( 3, 1, 0, 0 ) ); m_actionPanel.add( dynamicPanel ); - if( entry instanceof ClassEntry ) + if( pair.deobf instanceof ClassEntry ) { - showEntry( (ClassEntry)entry, dynamicPanel ); + showEntry( (ClassEntry)pair.deobf, dynamicPanel ); } - else if( entry instanceof FieldEntry ) + else if( pair.deobf instanceof FieldEntry ) { - showEntry( (FieldEntry)entry, dynamicPanel ); + showEntry( (FieldEntry)pair.deobf, dynamicPanel ); } - else if( entry instanceof MethodEntry ) + else if( pair.deobf instanceof MethodEntry ) { - showEntry( (MethodEntry)entry, dynamicPanel ); + showEntry( (MethodEntry)pair.deobf, dynamicPanel ); } - else if( entry instanceof ArgumentEntry ) + else if( pair.deobf instanceof ArgumentEntry ) { - showEntry( (ArgumentEntry)entry, dynamicPanel ); + showEntry( (ArgumentEntry)pair.deobf, dynamicPanel ); } else { - throw new Error( "Unknown entry type: " + entry.getClass().getName() ); + throw new Error( "Unknown entry type: " + pair.deobf.getClass().getName() ); } redraw(); } - public void showEntry( ClassEntry entry, JPanel panel ) + private void showEntry( ClassEntry entry, JPanel panel ) { m_typeLabel.setText( "Class: " ); } - public void showEntry( FieldEntry entry, JPanel panel ) + private void showEntry( FieldEntry entry, JPanel panel ) { m_typeLabel.setText( "Field: " ); addNameValue( panel, "Class", entry.getClassEntry().getName() ); } - public void showEntry( MethodEntry entry, JPanel panel ) + private void showEntry( MethodEntry entry, JPanel panel ) { m_typeLabel.setText( "Method: " ); addNameValue( panel, "Class", entry.getClassEntry().getName() ); addNameValue( panel, "Signature", entry.getSignature() ); } - public void showEntry( ArgumentEntry entry, JPanel panel ) + private void showEntry( ArgumentEntry entry, JPanel panel ) { m_typeLabel.setText( "Argument: " ); addNameValue( panel, "Class", entry.getMethodEntry().getClassEntry().getName() ); diff --git a/src/cuchaz/enigma/gui/RenameListener.java b/src/cuchaz/enigma/gui/RenameListener.java index 58cdadb0..7d45505b 100644 --- a/src/cuchaz/enigma/gui/RenameListener.java +++ b/src/cuchaz/enigma/gui/RenameListener.java @@ -14,5 +14,5 @@ import cuchaz.enigma.mapping.Entry; public interface RenameListener { - void rename( Entry entry, String newName ); + void rename( Entry obfEntry, String newName ); } diff --git a/src/cuchaz/enigma/mapping/Ancestries.java b/src/cuchaz/enigma/mapping/Ancestries.java new file mode 100644 index 00000000..b7a5e249 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Ancestries.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import javassist.ByteArrayClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.Descriptor; + +import com.google.common.collect.Maps; + +import cuchaz.enigma.Constants; + +public class Ancestries implements Serializable +{ + private static final long serialVersionUID = 738687982126844179L; + + private Map m_superclasses; + + public Ancestries( ) + { + m_superclasses = Maps.newHashMap(); + } + + public void readFromJar( InputStream in ) + throws IOException + { + ClassPool classPool = new ClassPool(); + + ZipInputStream zin = new ZipInputStream( in ); + ZipEntry entry; + while( ( entry = zin.getNextEntry() ) != null ) + { + // filter out non-classes + if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) ) + { + continue; + } + + // read the class into a buffer + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buf = new byte[Constants.KiB]; + int totalNumBytesRead = 0; + while( zin.available() > 0 ) + { + int numBytesRead = zin.read( buf ); + if( numBytesRead < 0 ) + { + break; + } + bos.write( buf, 0, numBytesRead ); + + // sanity checking + totalNumBytesRead += numBytesRead; + if( totalNumBytesRead > Constants.MiB ) + { + throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" ); + } + } + + // determine the class name (ie chop off the ".class") + String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); + + // get a javassist handle for the class + classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); + try + { + CtClass c = classPool.get( className ); + addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); + } + catch( NotFoundException ex ) + { + throw new Error( "Unable to load class: " + className ); + } + } + } + + public void addSuperclass( String className, String superclassName ) + { + className = Descriptor.toJvmName( className ); + superclassName = Descriptor.toJvmName( superclassName ); + + if( className.equals( superclassName ) ) + { + throw new IllegalArgumentException( "Class cannot be its own superclass! " + className ); + } + + if( !isJre( className ) && !isJre( superclassName ) ) + { + m_superclasses.put( className, superclassName ); + } + } + + public String getSuperclassName( String className ) + { + return m_superclasses.get( className ); + } + + public List getAncestry( String className ) + { + List ancestors = new ArrayList(); + while( className != null ) + { + className = getSuperclassName( className ); + ancestors.add( className ); + } + return ancestors; + } + + private boolean isJre( String className ) + { + return className.startsWith( "java/" ) + || className.startsWith( "javax/" ); + } +} diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java index dc3b4df7..c1624a83 100644 --- a/src/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -42,6 +42,13 @@ public class ArgumentEntry implements Entry, Serializable m_name = name; } + public ArgumentEntry( ArgumentEntry other ) + { + m_methodEntry = new MethodEntry( other.m_methodEntry ); + m_index = other.m_index; + m_name = other.m_name; + } + public MethodEntry getMethodEntry( ) { return m_methodEntry; @@ -58,6 +65,26 @@ public class ArgumentEntry implements Entry, Serializable return m_name; } + public ClassEntry getClassEntry( ) + { + return m_methodEntry.getClassEntry(); + } + + public String getClassName( ) + { + return m_methodEntry.getClassName(); + } + + public String getMethodName( ) + { + return m_methodEntry.getName(); + } + + public String getMethodSignature( ) + { + return m_methodEntry.getSignature(); + } + @Override public int hashCode( ) { diff --git a/src/cuchaz/enigma/mapping/ArgumentIndex.java b/src/cuchaz/enigma/mapping/ArgumentIndex.java new file mode 100644 index 00000000..57488d14 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ArgumentIndex.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +public class ArgumentIndex implements Serializable +{ + private static final long serialVersionUID = 8610742471440861315L; + + private String m_obfName; + private String m_deobfName; + + public ArgumentIndex( String obfName, String deobfName ) + { + m_obfName = obfName; + m_deobfName = deobfName; + } + + public String getObfName( ) + { + return m_obfName; + } + + public String getDeobfName( ) + { + return m_deobfName; + } + public void setDeobfName( String val ) + { + m_deobfName = val; + } +} diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 3a757675..0968e955 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -33,6 +33,11 @@ public class ClassEntry implements Entry, Serializable m_name = className; } + public ClassEntry( ClassEntry other ) + { + m_name = other.m_name; + } + @Override public String getName( ) { diff --git a/src/cuchaz/enigma/mapping/ClassIndex.java b/src/cuchaz/enigma/mapping/ClassIndex.java new file mode 100644 index 00000000..699807b8 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ClassIndex.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; +import java.util.Map; + +import com.beust.jcommander.internal.Maps; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + +public class ClassIndex implements Serializable +{ + private static final long serialVersionUID = -5148491146902340107L; + + private String m_obfName; + private String m_deobfName; + private BiMap m_fieldsObfToDeobf; + private Map m_methodsByObf; + private Map m_methodsByDeobf; + + public ClassIndex( String obfName, String deobfName ) + { + m_obfName = obfName; + m_deobfName = deobfName; + m_fieldsObfToDeobf = HashBiMap.create(); + m_methodsByObf = Maps.newHashMap(); + m_methodsByDeobf = Maps.newHashMap(); + } + + public String getObfName( ) + { + return m_obfName; + } + + public String getDeobfName( ) + { + return m_deobfName; + } + public void setDeobfName( String val ) + { + m_deobfName = val; + } + + public String getObfFieldName( String deobfName ) + { + return m_fieldsObfToDeobf.inverse().get( deobfName ); + } + + public String getDeobfFieldName( String obfName ) + { + return m_fieldsObfToDeobf.get( obfName ); + } + + public void setFieldName( String obfName, String deobfName ) + { + m_fieldsObfToDeobf.put( obfName, deobfName ); + } + + public MethodIndex getMethodByObf( String obfName, String signature ) + { + return m_methodsByObf.get( getMethodKey( obfName, signature ) ); + } + + public MethodIndex getMethodByDeobf( String deobfName, String signature ) + { + return m_methodsByDeobf.get( getMethodKey( deobfName, signature ) ); + } + + private String getMethodKey( String name, String signature ) + { + return name + signature; + } + + public void setMethodNameAndSignature( String obfName, String obfSignature, String deobfName, String deobfSignature ) + { + if( deobfName == null ) + { + throw new IllegalArgumentException( "deobf name cannot be null!" ); + } + + MethodIndex methodIndex = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) ); + if( methodIndex == null ) + { + methodIndex = createMethodIndex( obfName, obfSignature ); + } + + m_methodsByDeobf.remove( getMethodKey( methodIndex.getDeobfName(), methodIndex.getDeobfSignature() ) ); + methodIndex.setDeobfName( deobfName ); + methodIndex.setDeobfSignature( deobfSignature ); + m_methodsByDeobf.put( getMethodKey( deobfName, deobfSignature ), methodIndex ); + } + + public void updateDeobfMethodSignatures( Translator translator ) + { + for( MethodIndex methodIndex : m_methodsByObf.values() ) + { + methodIndex.setDeobfSignature( translator.translateSignature( methodIndex.getObfSignature() ) ); + } + } + + public void setArgumentName( String obfMethodName, String obfMethodSignature, int index, String obfName, String deobfName ) + { + if( deobfName == null ) + { + throw new IllegalArgumentException( "deobf name cannot be null!" ); + } + + MethodIndex methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ); + if( methodIndex == null ) + { + methodIndex = createMethodIndex( obfMethodName, obfMethodSignature ); + } + methodIndex.setArgumentName( index, obfName, deobfName ); + } + + private MethodIndex createMethodIndex( String obfName, String obfSignature ) + { + MethodIndex methodIndex = new MethodIndex( obfName, obfSignature, obfName, obfSignature ); + String key = getMethodKey( obfName, obfSignature ); + m_methodsByObf.put( key, methodIndex ); + m_methodsByDeobf.put( key, methodIndex ); + return methodIndex; + } + + @Override + public String toString( ) + { + StringBuilder buf = new StringBuilder(); + buf.append( m_obfName ); + buf.append( " <-> " ); + buf.append( m_deobfName ); + buf.append( "\n" ); + buf.append( "Fields:\n" ); + for( Map.Entry entry : m_fieldsObfToDeobf.entrySet() ) + { + buf.append( "\t" ); + buf.append( entry.getKey() ); + buf.append( " <-> " ); + buf.append( entry.getValue() ); + buf.append( "\n" ); + } + buf.append( "Methods:\n" ); + for( MethodIndex methodIndex : m_methodsByObf.values() ) + { + buf.append( methodIndex.toString() ); + buf.append( "\n" ); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java b/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java new file mode 100644 index 00000000..5320f110 --- /dev/null +++ b/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.util.Map; + +public class DeobfuscatedAncestries extends Ancestries +{ + private static final long serialVersionUID = 8316248774892618324L; + + private Ancestries m_ancestries; + private Map m_classesByObf; + private Map m_classesByDeobf; + + protected DeobfuscatedAncestries( Ancestries ancestries, Map classesByObf, Map classesByDeobf ) + { + m_ancestries = ancestries; + m_classesByObf = classesByObf; + m_classesByDeobf = classesByDeobf; + } + + @Override + public String getSuperclassName( String deobfClassName ) + { + // obfuscate the class name + ClassIndex classIndex = m_classesByDeobf.get( deobfClassName ); + if( classIndex == null ) + { + return null; + } + String obfClassName = classIndex.getObfName(); + + // get the superclass + String obfSuperclassName = m_ancestries.getSuperclassName( obfClassName ); + if( obfSuperclassName == null ) + { + return null; + } + + // deobfuscate the superclass name + classIndex = m_classesByObf.get( obfSuperclassName ); + if( classIndex == null ) + { + return null; + } + + return classIndex.getDeobfName(); + } +} diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java new file mode 100644 index 00000000..e40e9992 --- /dev/null +++ b/src/cuchaz/enigma/mapping/EntryPair.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import cuchaz.enigma.Util; + +public class EntryPair +{ + public Entry obf; + public Entry deobf; + + public EntryPair( Entry obf, Entry deobf ) + { + this.obf = obf; + this.deobf = deobf; + } + + @Override + public int hashCode( ) + { + return Util.combineHashesOrdered( obf, deobf ); + } + + @Override + public boolean equals( Object other ) + { + if( other instanceof EntryPair ) + { + return equals( (EntryPair)other ); + } + return false; + } + + public boolean equals( EntryPair other ) + { + return obf.equals( other.obf ) && deobf.equals( other.deobf ); + } +} diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java index 25b665ab..b9f42394 100644 --- a/src/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -36,6 +36,18 @@ public class FieldEntry implements Entry, Serializable m_name = name; } + public FieldEntry( FieldEntry other ) + { + m_classEntry = new ClassEntry( other.m_classEntry ); + m_name = other.m_name; + } + + public FieldEntry( FieldEntry other, String newClassName ) + { + m_classEntry = new ClassEntry( newClassName ); + m_name = other.m_name; + } + public ClassEntry getClassEntry( ) { return m_classEntry; @@ -47,6 +59,11 @@ public class FieldEntry implements Entry, Serializable return m_name; } + public String getClassName( ) + { + return m_classEntry.getName(); + } + @Override public int hashCode( ) { diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java index 4afc099b..9ea2d08e 100644 --- a/src/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -42,6 +42,20 @@ public class MethodEntry implements Entry, Serializable m_signature = signature; } + public MethodEntry( MethodEntry other ) + { + m_classEntry = new ClassEntry( other.m_classEntry ); + m_name = other.m_name; + m_signature = other.m_signature; + } + + public MethodEntry( MethodEntry other, String newClassName ) + { + m_classEntry = new ClassEntry( newClassName ); + m_name = other.m_name; + m_signature = other.m_signature; + } + public ClassEntry getClassEntry( ) { return m_classEntry; @@ -58,6 +72,11 @@ public class MethodEntry implements Entry, Serializable return m_signature; } + public String getClassName( ) + { + return m_classEntry.getName(); + } + @Override public int hashCode( ) { @@ -84,6 +103,6 @@ public class MethodEntry implements Entry, Serializable @Override public String toString( ) { - return m_classEntry.getName() + "." + m_name + ":" + m_signature; + return m_classEntry.getName() + "." + m_name + m_signature; } } diff --git a/src/cuchaz/enigma/mapping/MethodIndex.java b/src/cuchaz/enigma/mapping/MethodIndex.java new file mode 100644 index 00000000..f965355d --- /dev/null +++ b/src/cuchaz/enigma/mapping/MethodIndex.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; +import java.util.Map; +import java.util.TreeMap; + +public class MethodIndex implements Serializable +{ + private static final long serialVersionUID = -4409570216084263978L; + + private String m_obfName; + private String m_deobfName; + private String m_obfSignature; + private String m_deobfSignature; + private Map m_arguments; + + public MethodIndex( String obfName, String obfSignature, String deobfName, String deobfSignature ) + { + m_obfName = obfName; + m_deobfName = deobfName; + m_obfSignature = obfSignature; + m_deobfSignature = deobfSignature; + m_arguments = new TreeMap(); + } + + public String getObfName( ) + { + return m_obfName; + } + + public String getDeobfName( ) + { + return m_deobfName; + } + public void setDeobfName( String val ) + { + m_deobfName = val; + } + + public String getObfSignature( ) + { + return m_obfSignature; + } + + public String getDeobfSignature( ) + { + return m_deobfSignature; + } + public void setDeobfSignature( String val ) + { + m_deobfSignature = val; + } + + public String getObfArgumentName( int index ) + { + ArgumentIndex argumentIndex = m_arguments.get( index ); + if( argumentIndex != null ) + { + return argumentIndex.getObfName(); + } + + return null; + } + + public String getDeobfArgumentName( int index ) + { + ArgumentIndex argumentIndex = m_arguments.get( index ); + if( argumentIndex != null ) + { + return argumentIndex.getDeobfName(); + } + + return null; + } + + public void setArgumentName( int index, String obfName, String deobfName ) + { + ArgumentIndex argumentIndex = m_arguments.get( index ); + if( argumentIndex == null ) + { + argumentIndex = new ArgumentIndex( obfName, deobfName ); + m_arguments.put( index, argumentIndex ); + } + else + { + argumentIndex.setDeobfName( deobfName ); + } + } + + @Override + public String toString( ) + { + StringBuilder buf = new StringBuilder(); + buf.append( "\t" ); + buf.append( m_obfName ); + buf.append( " <-> " ); + buf.append( m_deobfName ); + buf.append( "\n" ); + buf.append( "\t" ); + buf.append( m_obfSignature ); + buf.append( " <-> " ); + buf.append( m_deobfSignature ); + buf.append( "\n" ); + buf.append( "\tArguments:\n" ); + for( ArgumentIndex argumentIndex : m_arguments.values() ) + { + buf.append( "\t\t" ); + buf.append( argumentIndex.getObfName() ); + buf.append( " <-> " ); + buf.append( argumentIndex.getDeobfName() ); + buf.append( "\n" ); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java new file mode 100644 index 00000000..4c0dbac1 --- /dev/null +++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.StringReader; + +public class SignatureUpdater +{ + public interface ClassNameUpdater + { + String update( String className ); + } + + public static String update( String signature, ClassNameUpdater updater ) + { + try + { + StringBuilder buf = new StringBuilder(); + + // read the signature character-by-character + StringReader reader = new StringReader( signature ); + int i = -1; + while( ( i = reader.read() ) != -1 ) + { + char c = (char)i; + + // does this character start a class name? + if( c == 'L' ) + { + // update the class name and add it to the buffer + buf.append( 'L' ); + String className = readClass( reader ); + if( className == null ) + { + throw new IllegalArgumentException( "Malformed signature: " + signature ); + } + buf.append( updater.update( className ) ); + buf.append( ';' ); + } + else + { + // copy the character into the buffer + buf.append( c ); + } + } + + return buf.toString(); + } + catch( IOException ex ) + { + // I'm pretty sure a StringReader will never throw one of these + throw new Error( ex ); + } + } + + private static String readClass( StringReader reader ) + throws IOException + { + // read all the characters in the buffer until we hit a ';' + StringBuilder buf = new StringBuilder(); + int i = -1; + while( ( i = reader.read() ) != -1 ) + { + char c = (char)i; + + if( c == ';' ) + { + return buf.toString(); + } + else + { + buf.append( c ); + } + } + + return null; + } +} diff --git a/src/cuchaz/enigma/mapping/TranslationDirection.java b/src/cuchaz/enigma/mapping/TranslationDirection.java new file mode 100644 index 00000000..79ae0d32 --- /dev/null +++ b/src/cuchaz/enigma/mapping/TranslationDirection.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + + +public enum TranslationDirection +{ + Deobfuscating + { + @Override + public T choose( T deobfChoice, T obfChoice ) + { + return deobfChoice; + } + }, + Obfuscating + { + @Override + public T choose( T deobfChoice, T obfChoice ) + { + return obfChoice; + } + }; + + public abstract T choose( T deobfChoice, T obfChoice ); +} diff --git a/src/cuchaz/enigma/mapping/TranslationMappings.java b/src/cuchaz/enigma/mapping/TranslationMappings.java new file mode 100644 index 00000000..d6cd4491 --- /dev/null +++ b/src/cuchaz/enigma/mapping/TranslationMappings.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.Map; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import com.beust.jcommander.internal.Maps; + +import cuchaz.enigma.Util; + +public class TranslationMappings implements Serializable +{ + private static final long serialVersionUID = 4649790259460259026L; + + private Map m_classesByObf; + private Map m_classesByDeobf; + private Ancestries m_ancestries; + + public TranslationMappings( Ancestries ancestries ) + { + m_classesByObf = Maps.newHashMap(); + m_classesByDeobf = Maps.newHashMap(); + m_ancestries = ancestries; + } + + public static TranslationMappings newFromResource( String resource ) + throws IOException + { + InputStream in = null; + try + { + in = TranslationMappings.class.getResourceAsStream( resource ); + return newFromStream( in ); + } + finally + { + Util.closeQuietly( in ); + } + } + + public Translator getTranslator( TranslationDirection direction ) + { + return new Translator( + direction, + direction.choose( m_classesByObf, m_classesByDeobf ), + direction.choose( m_ancestries, new DeobfuscatedAncestries( m_ancestries, m_classesByObf, m_classesByDeobf ) ) + ); + } + + public void setClassName( ClassEntry obf, String deobfName ) + { + ClassIndex classIndex = m_classesByObf.get( obf.getName() ); + if( classIndex == null ) + { + classIndex = createClassIndex( obf ); + } + + m_classesByDeobf.remove( classIndex.getDeobfName() ); + classIndex.setDeobfName( deobfName ); + m_classesByDeobf.put( deobfName, classIndex ); + + updateDeobfMethodSignatures(); + + // TEMP + String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf ); + assert( translatedName != null && translatedName.equals( deobfName ) ); + } + + public void setFieldName( FieldEntry obf, String deobfName ) + { + ClassIndex classIndex = m_classesByObf.get( obf.getClassName() ); + if( classIndex == null ) + { + classIndex = createClassIndex( obf.getClassEntry() ); + } + + classIndex.setFieldName( obf.getName(), deobfName ); + + // TEMP + System.out.println( classIndex ); + String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf ); + assert( translatedName != null && translatedName.equals( deobfName ) ); + } + + public void setMethodName( MethodEntry obf, String deobfName ) + { + ClassIndex classIndex = m_classesByObf.get( obf.getClassName() ); + if( classIndex == null ) + { + classIndex = createClassIndex( obf.getClassEntry() ); + } + + String deobfSignature = getTranslator( TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); + classIndex.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); + + // TODO: update ancestor/descendant methods in other classes in the inheritance hierarchy too + + // TEMP + System.out.println( classIndex ); + String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf ); + assert( translatedName != null && translatedName.equals( deobfName ) ); + } + + public void setArgumentName( ArgumentEntry obf, String deobfName ) + { + ClassIndex classIndex = m_classesByObf.get( obf.getClassName() ); + if( classIndex == null ) + { + classIndex = createClassIndex( obf.getClassEntry() ); + } + + classIndex.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName(), deobfName ); + + // TEMP + System.out.println( classIndex ); + String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf ); + assert( translatedName != null && translatedName.equals( deobfName ) ); + } + + public void write( OutputStream out ) + throws IOException + { + // TEMP: just use the object output for now. We can find a more efficient storage format later + GZIPOutputStream gzipout = new GZIPOutputStream( out ); + ObjectOutputStream oout = new ObjectOutputStream( gzipout ); + oout.writeObject( this ); + gzipout.finish(); + } + + public static TranslationMappings newFromStream( InputStream in ) + throws IOException + { + try + { + return (TranslationMappings)new ObjectInputStream( new GZIPInputStream( in ) ).readObject(); + } + catch( ClassNotFoundException ex ) + { + throw new Error( ex ); + } + } + + private ClassIndex createClassIndex( ClassEntry obf ) + { + ClassIndex classIndex = new ClassIndex( obf.getName(), obf.getName() ); + m_classesByObf.put( classIndex.getObfName(), classIndex ); + m_classesByDeobf.put( classIndex.getDeobfName(), classIndex ); + return classIndex; + } + + private void updateDeobfMethodSignatures( ) + { + Translator translator = getTranslator( TranslationDirection.Deobfuscating ); + for( ClassIndex classIndex : m_classesByObf.values() ) + { + classIndex.updateDeobfMethodSignatures( translator ); + } + } + + @Override + public String toString( ) + { + StringBuilder buf = new StringBuilder(); + for( ClassIndex classIndex : m_classesByObf.values() ) + { + buf.append( classIndex.toString() ); + buf.append( "\n" ); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java new file mode 100644 index 00000000..bae0dce7 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; + +public class Translator +{ + private TranslationDirection m_direction; + private Map m_classes; + private Ancestries m_ancestries; + + protected Translator( TranslationDirection direction, Map classes, Ancestries ancestries ) + { + m_direction = direction; + m_classes = classes; + m_ancestries = ancestries; + } + + public String translate( ClassEntry in ) + { + return translateClass( in.getName() ); + } + + public String translateClass( String in ) + { + ClassIndex classIndex = m_classes.get( in ); + if( classIndex != null ) + { + return m_direction.choose( + classIndex.getDeobfName(), + classIndex.getObfName() + ); + } + + return null; + } + + public ClassEntry translateEntry( ClassEntry in ) + { + String name = translate( in ); + if( name == null ) + { + return in; + } + return new ClassEntry( name ); + } + + public String translate( FieldEntry in ) + { + for( String className : getSelfAndAncestors( in.getClassName() ) ) + { + // look for the class + ClassIndex classIndex = m_classes.get( className ); + if( classIndex != null ) + { + // look for the field + String deobfName = m_direction.choose( + classIndex.getDeobfFieldName( in.getName() ), + classIndex.getObfFieldName( in.getName() ) + ); + if( deobfName != null ) + { + return deobfName; + } + } + } + + return null; + } + + public FieldEntry translateEntry( FieldEntry in ) + { + String name = translate( in ); + if( name == null ) + { + name = in.getName(); + } + return new FieldEntry( + translateEntry( in.getClassEntry() ), + name + ); + } + + public String translate( MethodEntry in ) + { + for( String className : getSelfAndAncestors( in.getClassName() ) ) + { + // look for the class + ClassIndex classIndex = m_classes.get( className ); + if( classIndex != null ) + { + // look for the method + MethodIndex methodIndex = m_direction.choose( + classIndex.getMethodByObf( in.getName(), in.getSignature() ), + classIndex.getMethodByDeobf( in.getName(), in.getSignature() ) + ); + if( methodIndex != null ) + { + return m_direction.choose( + methodIndex.getDeobfName(), + methodIndex.getObfName() + ); + } + } + } + + return null; + } + + public MethodEntry translateEntry( MethodEntry in ) + { + String name = translate( in ); + if( name == null ) + { + name = in.getName(); + } + return new MethodEntry( + translateEntry( in.getClassEntry() ), + name, + translateSignature( in.getSignature() ) + ); + } + + public String translate( ArgumentEntry in ) + { + for( String className : getSelfAndAncestors( in.getClassName() ) ) + { + // look for the class + ClassIndex classIndex = m_classes.get( className ); + if( classIndex != null ) + { + // look for the method + MethodIndex methodIndex = m_direction.choose( + classIndex.getMethodByObf( in.getMethodName(), in.getMethodSignature() ), + classIndex.getMethodByDeobf( in.getMethodName(), in.getMethodSignature() ) + ); + if( methodIndex != null ) + { + return m_direction.choose( + methodIndex.getDeobfArgumentName( in.getIndex() ), + methodIndex.getObfArgumentName( in.getIndex() ) + ); + } + } + } + + return null; + } + + public ArgumentEntry translateEntry( ArgumentEntry in ) + { + String name = translate( in ); + if( name == null ) + { + name = in.getName(); + } + return new ArgumentEntry( + translateEntry( in.getMethodEntry() ), + in.getIndex(), + name + ); + } + + public String translateSignature( String signature ) + { + return SignatureUpdater.update( signature, new ClassNameUpdater( ) + { + @Override + public String update( String className ) + { + String translatedName = translateClass( className ); + if( translatedName != null ) + { + return translatedName; + } + return className; + } + } ); + } + + private List getSelfAndAncestors( String className ) + { + List ancestry = new ArrayList(); + ancestry.add( className ); + ancestry.addAll( m_ancestries.getAncestry( className ) ); + return ancestry; + } +} -- cgit v1.2.3 From 1318888e5b37a2d76270c5c330e63d4b5dcf779e Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 29 Jul 2014 00:37:51 -0400 Subject: added start of menu bar added about bow --- conf/about.html | 6 +++ src/cuchaz/enigma/Constants.java | 3 ++ src/cuchaz/enigma/Util.java | 44 +++++++++++++++++ src/cuchaz/enigma/gui/AboutDialog.java | 86 ++++++++++++++++++++++++++++++++++ src/cuchaz/enigma/gui/Gui.java | 28 +++++++++-- 5 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 conf/about.html create mode 100644 src/cuchaz/enigma/gui/AboutDialog.java diff --git a/conf/about.html b/conf/about.html new file mode 100644 index 00000000..b75c1bf0 --- /dev/null +++ b/conf/about.html @@ -0,0 +1,6 @@ + +

%s

+

A tool for debofuscation of Java code

+

+

Version: %s

+ \ No newline at end of file diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 09787145..a8c4f44c 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -13,6 +13,9 @@ package cuchaz.enigma; public class Constants { + public static final String Name = "Enigma"; + public static final String Version = "0.1"; + public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024*1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte } diff --git a/src/cuchaz/enigma/Util.java b/src/cuchaz/enigma/Util.java index c51eb621..84927fd9 100644 --- a/src/cuchaz/enigma/Util.java +++ b/src/cuchaz/enigma/Util.java @@ -10,10 +10,17 @@ ******************************************************************************/ package cuchaz.enigma; +import java.awt.Desktop; import java.io.Closeable; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.URISyntaxException; import java.util.jar.JarFile; +import com.google.common.io.CharStreams; + public class Util { @@ -62,4 +69,41 @@ public class Util } } } + + public static String readStreamToString( InputStream in ) + throws IOException + { + return CharStreams.toString( new InputStreamReader( in, "UTF-8" ) ); + } + + public static String readResourceToString( String path ) + throws IOException + { + InputStream in = Util.class.getResourceAsStream( path ); + if( in == null ) + { + throw new IllegalArgumentException( "Resource not found! " + path ); + } + return readStreamToString( in ); + } + + public static void openUrl( String url ) + { + if( Desktop.isDesktopSupported() ) + { + Desktop desktop = Desktop.getDesktop(); + try + { + desktop.browse( new URI( url ) ); + } + catch( IOException ex ) + { + throw new Error( ex ); + } + catch( URISyntaxException ex ) + { + throw new IllegalArgumentException( ex ); + } + } + } } diff --git a/src/cuchaz/enigma/gui/AboutDialog.java b/src/cuchaz/enigma/gui/AboutDialog.java new file mode 100644 index 00000000..2584182f --- /dev/null +++ b/src/cuchaz/enigma/gui/AboutDialog.java @@ -0,0 +1,86 @@ +package cuchaz.enigma.gui; + +import java.awt.Color; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.WindowConstants; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.Util; + +public class AboutDialog +{ + public static void show( JFrame parent ) + { + // init frame + final JFrame frame = new JFrame( Constants.Name + " - About" ); + final Container pane = frame.getContentPane(); + pane.setLayout( new FlowLayout() ); + + // load the content + try + { + String html = Util.readResourceToString( "/about.html" ); + html = String.format( html, Constants.Name, Constants.Version ); + JLabel label = new JLabel( html ); + label.setHorizontalAlignment( JLabel.CENTER ); + pane.add( label ); + } + catch( IOException ex ) + { + throw new Error( ex ); + } + + // show the link + String html = "%s"; + html = String.format( html, Constants.Url, Constants.Url ); + JButton link = new JButton( html ); + link.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + Util.openUrl( Constants.Url ); + } + } ); + link.setBorderPainted( false ); + link.setOpaque( false ); + link.setBackground( Color.WHITE ); + link.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + link.setFocusable( false ); + JPanel linkPanel = new JPanel(); + linkPanel.add( link ); + pane.add( linkPanel ); + + // show ok button + JButton okButton = new JButton( "Ok" ); + pane.add( okButton ); + okButton.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + frame.dispose(); + } + } ); + + // show the frame + pane.doLayout(); + frame.setSize( 400, 220 ); + frame.setResizable( false ); + frame.setLocationRelativeTo( parent ); + frame.setVisible( true ); + frame.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE ); + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 2a539a3f..631089c4 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -30,6 +30,9 @@ import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; @@ -42,6 +45,7 @@ import javax.swing.text.BadLocationException; import jsyntaxpane.DefaultSyntaxKit; import jsyntaxpane.Token; import cuchaz.enigma.ClassFile; +import cuchaz.enigma.Constants; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; @@ -51,8 +55,6 @@ import cuchaz.enigma.mapping.MethodEntry; public class Gui { - private static final String Name = "Enigma"; - // controls private JFrame m_frame; private JList m_obfClasses; @@ -74,7 +76,7 @@ public class Gui public Gui( ) { // init frame - m_frame = new JFrame( Name ); + m_frame = new JFrame( Constants.Name ); final Container pane = m_frame.getContentPane(); pane.setLayout( new BorderLayout() ); @@ -160,6 +162,24 @@ public class Gui JSplitPane splitMain = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, rightPanel ); pane.add( splitMain, BorderLayout.CENTER ); + // init menus + JMenuBar menuBar = new JMenuBar(); + JMenu menu = new JMenu( "Help" ); + menu.setMnemonic( 'h' ); + JMenuItem item = new JMenuItem( "About" ); + item.setMnemonic( 'a' ); + item.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + AboutDialog.show( m_frame ); + } + } ); + menu.add( item ); + menuBar.add( menu ); + m_frame.setJMenuBar( menuBar ); + // show the frame pane.doLayout(); m_frame.setSize( 800, 600 ); @@ -176,7 +196,7 @@ public class Gui public void setTitle( String title ) { - m_frame.setTitle( Name + " - " + title ); + m_frame.setTitle( Constants.Name + " - " + title ); } public void setObfClasses( List classes ) -- cgit v1.2.3 From 85b3ea9beb5934012280dc0efa475f334dd9a93a Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 29 Jul 2014 23:12:30 -0400 Subject: added gui/cli loading of jars/mappings gui can save mappings too --- src/cuchaz/enigma/Controller.java | 114 ------------- src/cuchaz/enigma/Deobfuscator.java | 31 +++- src/cuchaz/enigma/Main.java | 36 +++- src/cuchaz/enigma/gui/Gui.java | 278 ++++++++++++++++++++++++------- src/cuchaz/enigma/gui/GuiController.java | 147 ++++++++++++++++ 5 files changed, 420 insertions(+), 186 deletions(-) delete mode 100644 src/cuchaz/enigma/Controller.java create mode 100644 src/cuchaz/enigma/gui/GuiController.java diff --git a/src/cuchaz/enigma/Controller.java b/src/cuchaz/enigma/Controller.java deleted file mode 100644 index 3af139e8..00000000 --- a/src/cuchaz/enigma/Controller.java +++ /dev/null @@ -1,114 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma; - -import javax.swing.event.CaretEvent; -import javax.swing.event.CaretListener; - -import cuchaz.enigma.analysis.Analyzer; -import cuchaz.enigma.analysis.SourceIndex; -import cuchaz.enigma.gui.ClassSelectionListener; -import cuchaz.enigma.gui.Gui; -import cuchaz.enigma.gui.RenameListener; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.Entry; -import cuchaz.enigma.mapping.EntryPair; - -public class Controller implements ClassSelectionListener, CaretListener, RenameListener -{ - private Deobfuscator m_deobfuscator; - private Gui m_gui; - private SourceIndex m_index; - private ClassFile m_currentFile; - - public Controller( Deobfuscator deobfuscator, Gui gui ) - { - m_deobfuscator = deobfuscator; - m_gui = gui; - m_index = null; - m_currentFile = null; - - // update GUI - gui.setTitle( deobfuscator.getJarName() ); - gui.setObfClasses( deobfuscator.getObfuscatedClasses() ); - - // handle events - gui.setClassSelectionListener( this ); - gui.setCaretListener( this ); - gui.setRenameListener( this ); - } - - @Override - public void classSelected( ClassFile classFile ) - { - m_currentFile = classFile; - deobfuscate( m_currentFile ); - } - - @Override - public void caretUpdate( CaretEvent event ) - { - if( m_index != null ) - { - int pos = event.getDot(); - Entry deobfEntry = m_index.getEntry( pos ); - if( deobfEntry != null ) - { - m_gui.showEntryPair( new EntryPair( m_deobfuscator.obfuscate( deobfEntry ), deobfEntry ) ); - } - else - { - m_gui.clearEntryPair(); - } - } - } - - @Override - public void rename( Entry obfsEntry, String newName ) - { - m_deobfuscator.rename( obfsEntry, newName ); - - // did we rename the current file? - if( obfsEntry instanceof ClassEntry ) - { - ClassEntry classEntry = (ClassEntry)obfsEntry; - - // update the current file - if( classEntry.getName().equals( m_currentFile.getName() ) ) - { - m_currentFile = new ClassFile( newName ); - } - } - - deobfuscate( m_currentFile ); - } - - private void deobfuscate( final ClassFile classFile ) - { - m_gui.setSource( "(deobfuscating...)" ); - - // run the deobfuscator in a separate thread so we don't block the GUI event queue - new Thread( ) - { - @Override - public void run( ) - { - // deobfuscate the bytecode - String source = m_deobfuscator.getSource( classFile ); - m_gui.setSource( source ); - - // index the source file - m_index = Analyzer.analyze( classFile.getName(), source ); - m_gui.highlightTokens( m_index.tokens() ); - } - }.start(); - } -} diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index b1abd9e0..bc7065fd 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -83,18 +83,13 @@ public class Deobfuscator Util.closeQuietly( jarIn ); } - // init mappings - m_mappings = new TranslationMappings( m_ancestries ); - // config the decompiler m_settings = DecompilerSettings.javaDefaults(); - m_settings.setTypeLoader( new TranslatingTypeLoader( - m_jar, - m_mappings.getTranslator( TranslationDirection.Deobfuscating ), - m_mappings.getTranslator( TranslationDirection.Obfuscating ) - ) ); m_settings.setForceExplicitImports( true ); m_settings.setShowSyntheticMembers( true ); + + // init mappings + setMappings( new TranslationMappings( m_ancestries ) ); } public String getJarName( ) @@ -102,6 +97,26 @@ public class Deobfuscator return m_file.getName(); } + public TranslationMappings getMappings( ) + { + return m_mappings; + } + public void setMappings( TranslationMappings val ) + { + if( val == null ) + { + val = new TranslationMappings( m_ancestries ); + } + m_mappings = val; + + // update decompiler options + m_settings.setTypeLoader( new TranslatingTypeLoader( + m_jar, + m_mappings.getTranslator( TranslationDirection.Deobfuscating ), + m_mappings.getTranslator( TranslationDirection.Obfuscating ) + ) ); + } + public List getObfuscatedClasses( ) { List classes = new ArrayList(); diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index 4842e208..6a300ed6 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -19,16 +19,38 @@ public class Main public static void main( String[] args ) throws Exception { - startGui(); + Gui gui = new Gui(); + + // parse command-line args + if( args.length >= 1 ) + { + gui.getController().openJar( getFile( args[0] ) ); + } + if( args.length >= 2 ) + { + gui.getController().openMappings( getFile( args[1] ) ); + } } - private static void startGui( ) - throws Exception + private static File getFile( String path ) { - // settings - final File jarFile = new File( "/home/jeff/.minecraft/versions/1.7.10/1.7.10.jar" ); + // expand ~ to the home dir + if( path.startsWith( "~" ) ) + { + // get the home dir + File dirHome = new File( System.getProperty( "user.home" ) ); + + // is the path just ~/ or is it ~user/ ? + if( path.startsWith( "~/" ) ) + { + return new File( dirHome, path.substring( 2 ) ); + } + else + { + return new File( dirHome.getParentFile(), path.substring( 1 ) ); + } + } - // start the GUI and tie it to the deobfuscator - new Controller( new Deobfuscator( jarFile ), new Gui() ); + return new File( path ); } } diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 631089c4..a86ff9b0 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -20,6 +20,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.io.IOException; import java.util.List; import java.util.Vector; @@ -27,6 +28,7 @@ import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JEditorPane; +import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; @@ -39,6 +41,7 @@ import javax.swing.JSplitPane; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.WindowConstants; +import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.text.BadLocationException; @@ -55,6 +58,8 @@ import cuchaz.enigma.mapping.MethodEntry; public class Gui { + private GuiController m_controller; + // controls private JFrame m_frame; private JList m_obfClasses; @@ -65,16 +70,28 @@ public class Gui private JLabel m_typeLabel; private JTextField m_nameField; private JButton m_renameButton; + private BoxHighlightPainter m_highlightPainter; - // listeners - private ClassSelectionListener m_classSelectionListener; - private RenameListener m_renameListener; + // dynamic menu items + private JMenuItem m_closeJarMenu; + private JMenuItem m_openMappingsMenu; + private JMenuItem m_saveMappingsMenu; + private JMenuItem m_saveMappingsAsMenu; + private JMenuItem m_closeMappingsMenu; - private BoxHighlightPainter m_highlightPainter; + // state private EntryPair m_selectedEntryPair; + private JFileChooser m_jarFileChooser; + private JFileChooser m_mappingFileChooser; public Gui( ) { + m_controller = new GuiController( this ); + + // init file choosers + m_jarFileChooser = new JFileChooser(); + m_mappingFileChooser = new JFileChooser(); + // init frame m_frame = new JFrame( Constants.Name ); final Container pane = m_frame.getContentPane(); @@ -91,13 +108,10 @@ public class Gui { if( event.getClickCount() == 2 ) { - if( m_classSelectionListener != null ) + ClassFile selected = m_obfClasses.getSelectedValue(); + if( selected != null ) { - ClassFile selected = m_obfClasses.getSelectedValue(); - if( selected != null ) - { - m_classSelectionListener.classSelected( selected ); - } + m_controller.deobfuscateClass( selected ); } } } @@ -130,9 +144,9 @@ public class Gui @Override public void actionPerformed( ActionEvent event ) { - if( m_renameListener != null && m_selectedEntryPair != null ) + if( m_selectedEntryPair != null ) { - m_renameListener.rename( m_selectedEntryPair.obf, m_nameField.getText() ); + m_controller.rename( m_selectedEntryPair.obf, m_nameField.getText() ); } } } ); @@ -148,10 +162,27 @@ public class Gui // init editor DefaultSyntaxKit.initKit(); + m_highlightPainter = new BoxHighlightPainter(); m_editor = new JEditorPane(); m_editor.setEditable( false ); JScrollPane sourceScroller = new JScrollPane( m_editor ); m_editor.setContentType( "text/java" ); + m_editor.addCaretListener( new CaretListener( ) + { + @Override + public void caretUpdate( CaretEvent event ) + { + m_selectedEntryPair = m_controller.getEntryPair( event.getDot() ); + if( m_selectedEntryPair != null ) + { + showEntryPair( m_selectedEntryPair ); + } + else + { + clearEntryPair(); + } + } + } ); // layout controls JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); @@ -164,21 +195,147 @@ public class Gui // init menus JMenuBar menuBar = new JMenuBar(); - JMenu menu = new JMenu( "Help" ); - menu.setMnemonic( 'h' ); - JMenuItem item = new JMenuItem( "About" ); - item.setMnemonic( 'a' ); - item.addActionListener( new ActionListener( ) + m_frame.setJMenuBar( menuBar ); + { + JMenu menu = new JMenu( "File" ); + menuBar.add( menu ); + { + JMenuItem item = new JMenuItem( "Open Jar..." ); + menu.add( item ); + item.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + if( m_jarFileChooser.showOpenDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) + { + try + { + m_controller.openJar( m_jarFileChooser.getSelectedFile() ); + } + catch( IOException ex ) + { + throw new Error( ex ); + } + } + } + } ); + } + { + JMenuItem item = new JMenuItem( "Close Jar" ); + menu.add( item ); + item.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + m_controller.closeJar(); + } + } ); + m_closeJarMenu = item; + } + menu.addSeparator(); + { + JMenuItem item = new JMenuItem( "Open Mappings..." ); + menu.add( item ); + item.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + if( m_mappingFileChooser.showOpenDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) + { + try + { + m_controller.openMappings( m_mappingFileChooser.getSelectedFile() ); + m_saveMappingsMenu.setEnabled( true ); + } + catch( IOException ex ) + { + throw new Error( ex ); + } + } + } + } ); + m_openMappingsMenu = item; + } + { + JMenuItem item = new JMenuItem( "Save Mappings" ); + menu.add( item ); + item.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + try + { + m_controller.saveMappings( m_mappingFileChooser.getSelectedFile() ); + } + catch( IOException ex ) + { + throw new Error( ex ); + } + } + } ); + m_saveMappingsMenu = item; + } + { + JMenuItem item = new JMenuItem( "Save Mappings As..." ); + menu.add( item ); + item.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) { - @Override - public void actionPerformed( ActionEvent event ) + if( m_mappingFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) { - AboutDialog.show( m_frame ); + try + { + m_controller.saveMappings( m_mappingFileChooser.getSelectedFile() ); + m_saveMappingsMenu.setEnabled( true ); + } + catch( IOException ex ) + { + throw new Error( ex ); + } } - } ); + } + } ); + m_saveMappingsAsMenu = item; + } + { + JMenuItem item = new JMenuItem( "Close Mapppings" ); menu.add( item ); + item.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + m_controller.closeMappings(); + } + } ); + m_closeMappingsMenu = item; + } + } + { + JMenu menu = new JMenu( "Help" ); menuBar.add( menu ); - m_frame.setJMenuBar( menuBar ); + { + JMenuItem item = new JMenuItem( "About" ); + menu.add( item ); + item.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + AboutDialog.show( m_frame ); + } + } ); + } + } + + // init state + onCloseJar(); // show the frame pane.doLayout(); @@ -186,22 +343,52 @@ public class Gui m_frame.setMinimumSize( new Dimension( 640, 480 ) ); m_frame.setVisible( true ); m_frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE ); + } + + public GuiController getController( ) + { + return m_controller; + } + + public void onOpenJar( String jarName ) + { + // update gui + m_frame.setTitle( Constants.Name + " - " + jarName ); + setSource( null ); - // init listeners - m_classSelectionListener = null; - m_renameListener = null; - - m_highlightPainter = new BoxHighlightPainter(); + // update menu + m_closeJarMenu.setEnabled( true ); + m_openMappingsMenu.setEnabled( true ); + m_saveMappingsMenu.setEnabled( false ); + m_saveMappingsAsMenu.setEnabled( true ); + m_closeMappingsMenu.setEnabled( true ); } - public void setTitle( String title ) + public void onCloseJar( ) { - m_frame.setTitle( Constants.Name + " - " + title ); + // update gui + m_frame.setTitle( Constants.Name ); + setObfClasses( null ); + setSource( null ); + + // update menu + m_closeJarMenu.setEnabled( false ); + m_openMappingsMenu.setEnabled( false ); + m_saveMappingsMenu.setEnabled( false ); + m_saveMappingsAsMenu.setEnabled( false ); + m_closeMappingsMenu.setEnabled( false ); } public void setObfClasses( List classes ) { - m_obfClasses.setListData( new Vector( classes ) ); + if( classes != null ) + { + m_obfClasses.setListData( new Vector( classes ) ); + } + else + { + m_obfClasses.setListData( new Vector() ); + } } public void setSource( String source ) @@ -212,9 +399,10 @@ public class Gui public void setSource( String source, SourceIndex index ) { m_editor.setText( source ); + setHighlightedTokens( null ); } - public void highlightTokens( Iterable tokens ) + public void setHighlightedTokens( Iterable tokens ) { // remove any old highlighters m_editor.getHighlighter().removeAllHighlights(); @@ -240,28 +428,7 @@ public class Gui redraw(); } - public void setClassSelectionListener( ClassSelectionListener val ) - { - m_classSelectionListener = val; - } - - public void setRenameListener( RenameListener val ) - { - m_renameListener = val; - } - - public void setCaretListener( CaretListener listener ) - { - // remove any old listeners - for( CaretListener oldListener : m_editor.getCaretListeners() ) - { - m_editor.removeCaretListener( oldListener ); - } - - m_editor.addCaretListener( listener ); - } - - public void clearEntryPair( ) + private void clearEntryPair( ) { m_actionPanel.removeAll(); JLabel label = new JLabel( "No identifier selected" ); @@ -272,7 +439,7 @@ public class Gui redraw(); } - public void showEntryPair( EntryPair pair ) + private void showEntryPair( EntryPair pair ) { if( pair == null ) { @@ -280,9 +447,6 @@ public class Gui return; } - // TEMP - System.out.println( "Pair:\n" + pair.obf + "\n" + pair.deobf ); - m_selectedEntryPair = pair; // layout the action panel diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java new file mode 100644 index 00000000..5df2d434 --- /dev/null +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import cuchaz.enigma.ClassFile; +import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.Analyzer; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.EntryPair; +import cuchaz.enigma.mapping.TranslationMappings; + +public class GuiController +{ + private Deobfuscator m_deobfuscator; + private Gui m_gui; + private SourceIndex m_index; + private ClassFile m_currentFile; + + public GuiController( Gui gui ) + { + m_gui = gui; + m_deobfuscator = null; + m_index = null; + m_currentFile = null; + } + + public void openJar( File file ) + throws IOException + { + m_deobfuscator = new Deobfuscator( file ); + m_gui.onOpenJar( m_deobfuscator.getJarName() ); + m_gui.setObfClasses( m_deobfuscator.getObfuscatedClasses() ); + } + + public void closeJar( ) + { + m_deobfuscator = null; + m_gui.onCloseJar(); + } + + public void openMappings( File file ) + throws IOException + { + FileInputStream in = new FileInputStream( file ); + m_deobfuscator.setMappings( TranslationMappings.newFromStream( in ) ); + in.close(); + refreshOpenFiles(); + } + + public void saveMappings( File file ) + throws IOException + { + FileOutputStream out = new FileOutputStream( file ); + m_deobfuscator.getMappings().write( out ); + out.close(); + } + + public void closeMappings( ) + { + m_deobfuscator.setMappings( null ); + refreshOpenFiles(); + } + + public void deobfuscateClass( ClassFile classFile ) + { + m_currentFile = classFile; + deobfuscate( m_currentFile ); + } + + public EntryPair getEntryPair( int pos ) + { + if( m_index == null ) + { + return null; + } + + Entry deobfEntry = m_index.getEntry( pos ); + if( deobfEntry == null ) + { + return null; + } + return new EntryPair( m_deobfuscator.obfuscate( deobfEntry ), deobfEntry ); + } + + public void rename( Entry obfsEntry, String newName ) + { + m_deobfuscator.rename( obfsEntry, newName ); + + // did we rename the current file? + if( obfsEntry instanceof ClassEntry ) + { + ClassEntry classEntry = (ClassEntry)obfsEntry; + + // update the current file + if( classEntry.getName().equals( m_currentFile.getName() ) ) + { + m_currentFile = new ClassFile( newName ); + } + } + + refreshOpenFiles(); + } + + private void refreshOpenFiles( ) + { + if( m_currentFile != null ) + { + deobfuscate( m_currentFile ); + } + } + + private void deobfuscate( final ClassFile classFile ) + { + m_gui.setSource( "(deobfuscating...)" ); + + // run the deobfuscator in a separate thread so we don't block the GUI event queue + new Thread( ) + { + @Override + public void run( ) + { + // deobfuscate the bytecode + String source = m_deobfuscator.getSource( classFile ); + m_gui.setSource( source ); + + // index the source file + m_index = Analyzer.analyze( classFile.getName(), source ); + m_gui.setHighlightedTokens( m_index.tokens() ); + } + }.start(); + } +} -- cgit v1.2.3 From bb4525e1fbeb2b94ff8e3f84d9e627d67f9c54b1 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 29 Jul 2014 23:24:01 -0400 Subject: forgot to apply copyright notices --- src/cuchaz/enigma/gui/AboutDialog.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cuchaz/enigma/gui/AboutDialog.java b/src/cuchaz/enigma/gui/AboutDialog.java index 2584182f..79b75432 100644 --- a/src/cuchaz/enigma/gui/AboutDialog.java +++ b/src/cuchaz/enigma/gui/AboutDialog.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.gui; import java.awt.Color; -- cgit v1.2.3 From 4349d22cc8abf5ec74075dde1b45c5f2f8679bbf Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 30 Jul 2014 23:43:09 -0400 Subject: switched to line-by-line mergable, human-readable file format for mappings --- src/cuchaz/enigma/Deobfuscator.java | 31 +-- src/cuchaz/enigma/gui/GuiController.java | 17 +- src/cuchaz/enigma/mapping/ArgumentIndex.java | 41 ---- src/cuchaz/enigma/mapping/ArgumentMapping.java | 42 ++++ src/cuchaz/enigma/mapping/ClassIndex.java | 159 --------------- src/cuchaz/enigma/mapping/ClassMapping.java | 217 +++++++++++++++++++++ .../enigma/mapping/DeobfuscatedAncestries.java | 8 +- src/cuchaz/enigma/mapping/FieldEntry.java | 1 + src/cuchaz/enigma/mapping/FieldMapping.java | 41 ++++ src/cuchaz/enigma/mapping/Mappings.java | 128 ++++++++++++ src/cuchaz/enigma/mapping/MappingsReader.java | 129 ++++++++++++ src/cuchaz/enigma/mapping/MappingsWriter.java | 75 +++++++ src/cuchaz/enigma/mapping/MethodIndex.java | 125 ------------ src/cuchaz/enigma/mapping/MethodMapping.java | 136 +++++++++++++ src/cuchaz/enigma/mapping/Renamer.java | 125 ++++++++++++ src/cuchaz/enigma/mapping/TranslationMappings.java | 187 ------------------ src/cuchaz/enigma/mapping/Translator.java | 16 +- 17 files changed, 933 insertions(+), 545 deletions(-) delete mode 100644 src/cuchaz/enigma/mapping/ArgumentIndex.java create mode 100644 src/cuchaz/enigma/mapping/ArgumentMapping.java delete mode 100644 src/cuchaz/enigma/mapping/ClassIndex.java create mode 100644 src/cuchaz/enigma/mapping/ClassMapping.java create mode 100644 src/cuchaz/enigma/mapping/FieldMapping.java create mode 100644 src/cuchaz/enigma/mapping/Mappings.java create mode 100644 src/cuchaz/enigma/mapping/MappingsReader.java create mode 100644 src/cuchaz/enigma/mapping/MappingsWriter.java delete mode 100644 src/cuchaz/enigma/mapping/MethodIndex.java create mode 100644 src/cuchaz/enigma/mapping/MethodMapping.java create mode 100644 src/cuchaz/enigma/mapping/Renamer.java delete mode 100644 src/cuchaz/enigma/mapping/TranslationMappings.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index bc7065fd..7be57062 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -32,9 +32,10 @@ import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Renamer; import cuchaz.enigma.mapping.TranslationDirection; -import cuchaz.enigma.mapping.TranslationMappings; import cuchaz.enigma.mapping.Translator; public class Deobfuscator @@ -43,7 +44,8 @@ public class Deobfuscator private JarFile m_jar; private DecompilerSettings m_settings; private Ancestries m_ancestries; - private TranslationMappings m_mappings; + private Mappings m_mappings; + private Renamer m_renamer; private static Comparator m_obfuscatedClassSorter; @@ -89,7 +91,7 @@ public class Deobfuscator m_settings.setShowSyntheticMembers( true ); // init mappings - setMappings( new TranslationMappings( m_ancestries ) ); + setMappings( new Mappings() ); } public String getJarName( ) @@ -97,23 +99,24 @@ public class Deobfuscator return m_file.getName(); } - public TranslationMappings getMappings( ) + public Mappings getMappings( ) { return m_mappings; } - public void setMappings( TranslationMappings val ) + public void setMappings( Mappings val ) { if( val == null ) { - val = new TranslationMappings( m_ancestries ); + val = new Mappings(); } m_mappings = val; + m_renamer = new Renamer( m_ancestries, m_mappings ); // update decompiler options m_settings.setTypeLoader( new TranslatingTypeLoader( m_jar, - m_mappings.getTranslator( TranslationDirection.Deobfuscating ), - m_mappings.getTranslator( TranslationDirection.Obfuscating ) + m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ), + m_mappings.getTranslator( m_ancestries, TranslationDirection.Obfuscating ) ) ); } @@ -168,19 +171,19 @@ public class Deobfuscator { if( entry instanceof ClassEntry ) { - m_mappings.setClassName( (ClassEntry)entry, newName ); + m_renamer.setClassName( (ClassEntry)entry, newName ); } else if( entry instanceof FieldEntry ) { - m_mappings.setFieldName( (FieldEntry)entry, newName ); + m_renamer.setFieldName( (FieldEntry)entry, newName ); } else if( entry instanceof MethodEntry ) { - m_mappings.setMethodName( (MethodEntry)entry, newName ); + m_renamer.setMethodName( (MethodEntry)entry, newName ); } else if( entry instanceof ArgumentEntry ) { - m_mappings.setArgumentName( (ArgumentEntry)entry, newName ); + m_renamer.setArgumentName( (ArgumentEntry)entry, newName ); } else { @@ -190,7 +193,7 @@ public class Deobfuscator public Entry obfuscate( Entry in ) { - Translator translator = m_mappings.getTranslator( TranslationDirection.Obfuscating ); + Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Obfuscating ); if( in instanceof ClassEntry ) { return translator.translateEntry( (ClassEntry)in ); @@ -215,7 +218,7 @@ public class Deobfuscator public Entry deobfuscate( Entry in ) { - Translator translator = m_mappings.getTranslator( TranslationDirection.Deobfuscating ); + Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ); if( in instanceof ClassEntry ) { return translator.translateEntry( (ClassEntry)in ); diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 5df2d434..fb22b961 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -11,8 +11,8 @@ package cuchaz.enigma.gui; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import cuchaz.enigma.ClassFile; @@ -22,7 +22,8 @@ import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; -import cuchaz.enigma.mapping.TranslationMappings; +import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.MappingsWriter; public class GuiController { @@ -56,17 +57,19 @@ public class GuiController public void openMappings( File file ) throws IOException { - FileInputStream in = new FileInputStream( file ); - m_deobfuscator.setMappings( TranslationMappings.newFromStream( in ) ); + FileReader in = new FileReader( file ); + m_deobfuscator.setMappings( new MappingsReader().read( in ) ); in.close(); + // TEMP + System.out.println( m_deobfuscator.getMappings() ); refreshOpenFiles(); } public void saveMappings( File file ) throws IOException { - FileOutputStream out = new FileOutputStream( file ); - m_deobfuscator.getMappings().write( out ); + FileWriter out = new FileWriter( file ); + new MappingsWriter().write( out, m_deobfuscator.getMappings() ); out.close(); } diff --git a/src/cuchaz/enigma/mapping/ArgumentIndex.java b/src/cuchaz/enigma/mapping/ArgumentIndex.java deleted file mode 100644 index 57488d14..00000000 --- a/src/cuchaz/enigma/mapping/ArgumentIndex.java +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.mapping; - -import java.io.Serializable; - -public class ArgumentIndex implements Serializable -{ - private static final long serialVersionUID = 8610742471440861315L; - - private String m_obfName; - private String m_deobfName; - - public ArgumentIndex( String obfName, String deobfName ) - { - m_obfName = obfName; - m_deobfName = deobfName; - } - - public String getObfName( ) - { - return m_obfName; - } - - public String getDeobfName( ) - { - return m_deobfName; - } - public void setDeobfName( String val ) - { - m_deobfName = val; - } -} diff --git a/src/cuchaz/enigma/mapping/ArgumentMapping.java b/src/cuchaz/enigma/mapping/ArgumentMapping.java new file mode 100644 index 00000000..d5e020a6 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +public class ArgumentMapping implements Serializable +{ + private static final long serialVersionUID = 8610742471440861315L; + + private int m_index; + private String m_name; + + // NOTE: this argument order is important for the MethodReader/MethodWriter + public ArgumentMapping( int index, String name ) + { + m_index = index; + m_name = name; + } + + public int getIndex( ) + { + return m_index; + } + + public String getName( ) + { + return m_name; + } + public void setName( String val ) + { + m_name = val; + } +} diff --git a/src/cuchaz/enigma/mapping/ClassIndex.java b/src/cuchaz/enigma/mapping/ClassIndex.java deleted file mode 100644 index 699807b8..00000000 --- a/src/cuchaz/enigma/mapping/ClassIndex.java +++ /dev/null @@ -1,159 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.mapping; - -import java.io.Serializable; -import java.util.Map; - -import com.beust.jcommander.internal.Maps; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; - -public class ClassIndex implements Serializable -{ - private static final long serialVersionUID = -5148491146902340107L; - - private String m_obfName; - private String m_deobfName; - private BiMap m_fieldsObfToDeobf; - private Map m_methodsByObf; - private Map m_methodsByDeobf; - - public ClassIndex( String obfName, String deobfName ) - { - m_obfName = obfName; - m_deobfName = deobfName; - m_fieldsObfToDeobf = HashBiMap.create(); - m_methodsByObf = Maps.newHashMap(); - m_methodsByDeobf = Maps.newHashMap(); - } - - public String getObfName( ) - { - return m_obfName; - } - - public String getDeobfName( ) - { - return m_deobfName; - } - public void setDeobfName( String val ) - { - m_deobfName = val; - } - - public String getObfFieldName( String deobfName ) - { - return m_fieldsObfToDeobf.inverse().get( deobfName ); - } - - public String getDeobfFieldName( String obfName ) - { - return m_fieldsObfToDeobf.get( obfName ); - } - - public void setFieldName( String obfName, String deobfName ) - { - m_fieldsObfToDeobf.put( obfName, deobfName ); - } - - public MethodIndex getMethodByObf( String obfName, String signature ) - { - return m_methodsByObf.get( getMethodKey( obfName, signature ) ); - } - - public MethodIndex getMethodByDeobf( String deobfName, String signature ) - { - return m_methodsByDeobf.get( getMethodKey( deobfName, signature ) ); - } - - private String getMethodKey( String name, String signature ) - { - return name + signature; - } - - public void setMethodNameAndSignature( String obfName, String obfSignature, String deobfName, String deobfSignature ) - { - if( deobfName == null ) - { - throw new IllegalArgumentException( "deobf name cannot be null!" ); - } - - MethodIndex methodIndex = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) ); - if( methodIndex == null ) - { - methodIndex = createMethodIndex( obfName, obfSignature ); - } - - m_methodsByDeobf.remove( getMethodKey( methodIndex.getDeobfName(), methodIndex.getDeobfSignature() ) ); - methodIndex.setDeobfName( deobfName ); - methodIndex.setDeobfSignature( deobfSignature ); - m_methodsByDeobf.put( getMethodKey( deobfName, deobfSignature ), methodIndex ); - } - - public void updateDeobfMethodSignatures( Translator translator ) - { - for( MethodIndex methodIndex : m_methodsByObf.values() ) - { - methodIndex.setDeobfSignature( translator.translateSignature( methodIndex.getObfSignature() ) ); - } - } - - public void setArgumentName( String obfMethodName, String obfMethodSignature, int index, String obfName, String deobfName ) - { - if( deobfName == null ) - { - throw new IllegalArgumentException( "deobf name cannot be null!" ); - } - - MethodIndex methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ); - if( methodIndex == null ) - { - methodIndex = createMethodIndex( obfMethodName, obfMethodSignature ); - } - methodIndex.setArgumentName( index, obfName, deobfName ); - } - - private MethodIndex createMethodIndex( String obfName, String obfSignature ) - { - MethodIndex methodIndex = new MethodIndex( obfName, obfSignature, obfName, obfSignature ); - String key = getMethodKey( obfName, obfSignature ); - m_methodsByObf.put( key, methodIndex ); - m_methodsByDeobf.put( key, methodIndex ); - return methodIndex; - } - - @Override - public String toString( ) - { - StringBuilder buf = new StringBuilder(); - buf.append( m_obfName ); - buf.append( " <-> " ); - buf.append( m_deobfName ); - buf.append( "\n" ); - buf.append( "Fields:\n" ); - for( Map.Entry entry : m_fieldsObfToDeobf.entrySet() ) - { - buf.append( "\t" ); - buf.append( entry.getKey() ); - buf.append( " <-> " ); - buf.append( entry.getValue() ); - buf.append( "\n" ); - } - buf.append( "Methods:\n" ); - for( MethodIndex methodIndex : m_methodsByObf.values() ) - { - buf.append( methodIndex.toString() ); - buf.append( "\n" ); - } - return buf.toString(); - } -} diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java new file mode 100644 index 00000000..3ba3569f --- /dev/null +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -0,0 +1,217 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; +import java.util.Map; + +import com.beust.jcommander.internal.Maps; + +public class ClassMapping implements Serializable +{ + private static final long serialVersionUID = -5148491146902340107L; + + private String m_obfName; + private String m_deobfName; + private Map m_fieldsByObf; + private Map m_fieldsByDeobf; + private Map m_methodsByObf; + private Map m_methodsByDeobf; + + // NOTE: this argument order is important for the MethodReader/MethodWriter + public ClassMapping( String obfName, String deobfName ) + { + m_obfName = obfName; + m_deobfName = deobfName; + m_fieldsByObf = Maps.newHashMap(); + m_fieldsByDeobf = Maps.newHashMap(); + m_methodsByObf = Maps.newHashMap(); + m_methodsByDeobf = Maps.newHashMap(); + } + + public String getObfName( ) + { + return m_obfName; + } + + public String getDeobfName( ) + { + return m_deobfName; + } + public void setDeobfName( String val ) + { + m_deobfName = val; + } + + public Iterable fields( ) + { + assert( m_fieldsByObf.size() == m_fieldsByDeobf.size() ); + return m_fieldsByObf.values(); + } + + protected void addFieldMapping( FieldMapping fieldMapping ) + { + m_fieldsByObf.put( fieldMapping.getObfName(), fieldMapping ); + m_fieldsByDeobf.put( fieldMapping.getDeobfName(), fieldMapping ); + } + + public Iterable methods( ) + { + assert( m_methodsByObf.size() == m_methodsByDeobf.size() ); + return m_methodsByObf.values(); + } + + protected void addMethodMapping( MethodMapping methodMapping ) + { + m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping ); + m_methodsByDeobf.put( getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ), methodMapping ); + } + + public String getObfFieldName( String deobfName ) + { + FieldMapping fieldMapping = m_fieldsByDeobf.get( deobfName ); + if( fieldMapping != null ) + { + return fieldMapping.getObfName(); + } + return null; + } + + public String getDeobfFieldName( String obfName ) + { + FieldMapping fieldMapping = m_fieldsByObf.get( obfName ); + if( fieldMapping != null ) + { + return fieldMapping.getDeobfName(); + } + return null; + } + + public void setFieldName( String obfName, String deobfName ) + { + if( deobfName == null ) + { + throw new IllegalArgumentException( "deobf name cannot be null!" ); + } + + FieldMapping fieldMapping = m_fieldsByObf.get( obfName ); + if( fieldMapping == null ) + { + fieldMapping = new FieldMapping( obfName, deobfName ); + m_fieldsByObf.put( obfName, fieldMapping ); + m_fieldsByDeobf.put( deobfName, fieldMapping ); + } + + m_fieldsByDeobf.remove( fieldMapping.getDeobfName() ); + fieldMapping.setDeobfName( deobfName ); + m_fieldsByDeobf.put( deobfName, fieldMapping ); + } + + public MethodMapping getMethodByObf( String obfName, String signature ) + { + return m_methodsByObf.get( getMethodKey( obfName, signature ) ); + } + + public MethodMapping getMethodByDeobf( String deobfName, String signature ) + { + return m_methodsByDeobf.get( getMethodKey( deobfName, signature ) ); + } + + private String getMethodKey( String name, String signature ) + { + if( name == null ) + { + throw new IllegalArgumentException( "name cannot be null!" ); + } + if( signature == null ) + { + throw new IllegalArgumentException( "signature cannot be null!" ); + } + return name + signature; + } + + public void setMethodNameAndSignature( String obfName, String obfSignature, String deobfName, String deobfSignature ) + { + if( deobfName == null ) + { + throw new IllegalArgumentException( "deobf name cannot be null!" ); + } + + MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) ); + if( methodIndex == null ) + { + methodIndex = createMethodIndex( obfName, obfSignature ); + } + + m_methodsByDeobf.remove( getMethodKey( methodIndex.getDeobfName(), methodIndex.getDeobfSignature() ) ); + methodIndex.setDeobfName( deobfName ); + methodIndex.setDeobfSignature( deobfSignature ); + m_methodsByDeobf.put( getMethodKey( deobfName, deobfSignature ), methodIndex ); + } + + public void updateDeobfMethodSignatures( Translator translator ) + { + for( MethodMapping methodIndex : m_methodsByObf.values() ) + { + methodIndex.setDeobfSignature( translator.translateSignature( methodIndex.getObfSignature() ) ); + } + } + + public void setArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName ) + { + if( argumentName == null ) + { + throw new IllegalArgumentException( "argument name cannot be null!" ); + } + + MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ); + if( methodIndex == null ) + { + methodIndex = createMethodIndex( obfMethodName, obfMethodSignature ); + } + methodIndex.setArgumentName( argumentIndex, argumentName ); + } + + private MethodMapping createMethodIndex( String obfName, String obfSignature ) + { + MethodMapping methodIndex = new MethodMapping( obfName, obfName, obfSignature, obfSignature ); + String key = getMethodKey( obfName, obfSignature ); + m_methodsByObf.put( key, methodIndex ); + m_methodsByDeobf.put( key, methodIndex ); + return methodIndex; + } + + @Override + public String toString( ) + { + StringBuilder buf = new StringBuilder(); + buf.append( m_obfName ); + buf.append( " <-> " ); + buf.append( m_deobfName ); + buf.append( "\n" ); + buf.append( "Fields:\n" ); + for( FieldMapping fieldMapping : fields() ) + { + buf.append( "\t" ); + buf.append( fieldMapping.getObfName() ); + buf.append( " <-> " ); + buf.append( fieldMapping.getDeobfName() ); + buf.append( "\n" ); + } + buf.append( "Methods:\n" ); + for( MethodMapping methodIndex : m_methodsByObf.values() ) + { + buf.append( methodIndex.toString() ); + buf.append( "\n" ); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java b/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java index 5320f110..dcb0741a 100644 --- a/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java +++ b/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java @@ -17,10 +17,10 @@ public class DeobfuscatedAncestries extends Ancestries private static final long serialVersionUID = 8316248774892618324L; private Ancestries m_ancestries; - private Map m_classesByObf; - private Map m_classesByDeobf; + private Map m_classesByObf; + private Map m_classesByDeobf; - protected DeobfuscatedAncestries( Ancestries ancestries, Map classesByObf, Map classesByDeobf ) + protected DeobfuscatedAncestries( Ancestries ancestries, Map classesByObf, Map classesByDeobf ) { m_ancestries = ancestries; m_classesByObf = classesByObf; @@ -31,7 +31,7 @@ public class DeobfuscatedAncestries extends Ancestries public String getSuperclassName( String deobfClassName ) { // obfuscate the class name - ClassIndex classIndex = m_classesByDeobf.get( deobfClassName ); + ClassMapping classIndex = m_classesByDeobf.get( deobfClassName ); if( classIndex == null ) { return null; diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java index b9f42394..eefc4c4c 100644 --- a/src/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -21,6 +21,7 @@ public class FieldEntry implements Entry, Serializable private ClassEntry m_classEntry; private String m_name; + // NOTE: this argument order is important for the MethodReader/MethodWriter public FieldEntry( ClassEntry classEntry, String name ) { if( classEntry == null ) diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java new file mode 100644 index 00000000..618f45c6 --- /dev/null +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +public class FieldMapping implements Serializable +{ + private static final long serialVersionUID = 8610742471440861315L; + + private String m_obfName; + private String m_deobfName; + + public FieldMapping( String obfName, String deobfName ) + { + m_obfName = obfName; + m_deobfName = deobfName; + } + + public String getObfName( ) + { + return m_obfName; + } + + public String getDeobfName( ) + { + return m_deobfName; + } + public void setDeobfName( String val ) + { + m_deobfName = val; + } +} diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java new file mode 100644 index 00000000..2a39057a --- /dev/null +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +import com.beust.jcommander.internal.Maps; + +import cuchaz.enigma.Util; + +public class Mappings implements Serializable +{ + private static final long serialVersionUID = 4649790259460259026L; + + protected Map m_classesByObf; + protected Map m_classesByDeobf; + + public Mappings( ) + { + m_classesByObf = Maps.newHashMap(); + m_classesByDeobf = Maps.newHashMap(); + } + + public Mappings( Iterable classes ) + { + this(); + + for( ClassMapping classMapping : classes ) + { + m_classesByObf.put( classMapping.getObfName(), classMapping ); + m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ); + } + } + + public static Mappings newFromResource( String resource ) + throws IOException + { + InputStream in = null; + try + { + in = Mappings.class.getResourceAsStream( resource ); + return newFromStream( in ); + } + finally + { + Util.closeQuietly( in ); + } + } + + public Iterable classes( ) + { + assert( m_classesByObf.size() == m_classesByDeobf.size() ); + return m_classesByObf.values(); + } + + protected void addClassMapping( ClassMapping classMapping ) + { + m_classesByObf.put( classMapping.getObfName(), classMapping ); + m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ); + } + + public ClassMapping getClassByObf( ClassEntry entry ) + { + return getClassByObf( entry.getName() ); + } + + public ClassMapping getClassByObf( String obfName ) + { + return m_classesByObf.get( obfName ); + } + + public ClassMapping getClassByDeobf( ClassEntry entry ) + { + return getClassByObf( entry.getName() ); + } + + public ClassMapping getClassByDeobf( String deobfName ) + { + return m_classesByDeobf.get( deobfName ); + } + + public Translator getTranslator( Ancestries ancestries, TranslationDirection direction ) + { + return new Translator( + direction, + direction.choose( m_classesByObf, m_classesByDeobf ), + direction.choose( ancestries, new DeobfuscatedAncestries( ancestries, m_classesByObf, m_classesByDeobf ) ) + ); + } + + public static Mappings newFromStream( InputStream in ) + throws IOException + { + try + { + return (Mappings)new ObjectInputStream( new GZIPInputStream( in ) ).readObject(); + } + catch( ClassNotFoundException ex ) + { + throw new Error( ex ); + } + } + + @Override + public String toString( ) + { + StringBuilder buf = new StringBuilder(); + for( ClassMapping classMapping : m_classesByObf.values() ) + { + buf.append( classMapping.toString() ); + buf.append( "\n" ); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java new file mode 100644 index 00000000..b0394090 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.NoSuchElementException; +import java.util.Scanner; + +import cuchaz.enigma.Util; + +public class MappingsReader +{ + public Mappings read( Reader in ) + throws IOException + { + return read( new BufferedReader( in ) ); + } + + public Mappings read( BufferedReader in ) + throws IOException + { + Mappings mappings = new Mappings(); + ClassMapping classMapping = null; + MethodMapping methodMapping = null; + + int lineNumber = 0; + String line = null; + while( ( line = in.readLine() ) != null ) + { + lineNumber++; + + // strip comments + int commentPos = line.indexOf( '#' ); + if( commentPos >= 0 ) + { + line = line.substring( 0, commentPos ); + } + + // skip blank lines + line = line.trim(); + if( line.length() <= 0 ) + { + continue; + } + + Scanner scanner = new Scanner( line ); + try + { + while( scanner.hasNext() ) + { + // read the first token + String token = scanner.next(); + + if( token.equalsIgnoreCase( "CLASS" ) ) + { + classMapping = readClass( scanner ); + mappings.addClassMapping( classMapping ); + methodMapping = null; + } + else if( token.equalsIgnoreCase( "FIELD" ) ) + { + if( classMapping == null ) + { + throw new IllegalArgumentException( "Line " + lineNumber + ": Unexpected FIELD entry here!" ); + } + classMapping.addFieldMapping( readField( scanner ) ); + } + else if( token.equalsIgnoreCase( "METHOD" ) ) + { + if( classMapping == null ) + { + throw new IllegalArgumentException( "Line " + lineNumber + ": Unexpected METHOD entry here!" ); + } + methodMapping = readMethod( scanner ); + classMapping.addMethodMapping( methodMapping ); + } + else if( token.equalsIgnoreCase( "ARG" ) ) + { + if( classMapping == null || methodMapping == null ) + { + throw new IllegalArgumentException( "Line " + lineNumber + ": Unexpected ARG entry here!" ); + } + methodMapping.addArgumentMapping( readArgument( scanner ) ); + } + } + } + catch( NoSuchElementException ex ) + { + throw new IllegalArgumentException( "Line " + lineNumber + ": malformed line!" ); + } + finally + { + Util.closeQuietly( scanner ); + } + } + + return mappings; + } + + private ArgumentMapping readArgument( Scanner scanner ) + { + return new ArgumentMapping( scanner.nextInt(), scanner.next() ); + } + + private ClassMapping readClass( Scanner scanner ) + { + return new ClassMapping( scanner.next(), scanner.next() ); + } + + private FieldMapping readField( Scanner scanner ) + { + return new FieldMapping( scanner.next(), scanner.next() ); + } + + private MethodMapping readMethod( Scanner scanner ) + { + return new MethodMapping( scanner.next(), scanner.next(), scanner.next(), scanner.next() ); + } +} diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java new file mode 100644 index 00000000..20863687 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; + +public class MappingsWriter +{ + public void write( Writer out, Mappings mappings ) + throws IOException + { + write( new PrintWriter( out ), mappings ); + } + + public void write( PrintWriter out, Mappings mappings ) + throws IOException + { + for( ClassMapping classMapping : mappings.classes() ) + { + write( out, classMapping ); + } + } + + public void write( PrintWriter out, ClassMapping classMapping ) + throws IOException + { + out.format( "CLASS %s %s\n", classMapping.getObfName(), classMapping.getDeobfName() ); + + for( FieldMapping fieldMapping : classMapping.fields() ) + { + write( out, fieldMapping ); + } + + for( MethodMapping methodMapping : classMapping.methods() ) + { + write( out, methodMapping ); + } + } + + public void write( PrintWriter out, FieldMapping fieldMapping ) + throws IOException + { + out.format( "\tFIELD %s %s\n", fieldMapping.getObfName(), fieldMapping.getDeobfName() ); + } + + public void write( PrintWriter out, MethodMapping methodMapping ) + throws IOException + { + out.format( "\tMETHOD %s %s %s %s\n", + methodMapping.getObfName(), methodMapping.getDeobfName(), + methodMapping.getObfSignature(), methodMapping.getDeobfSignature() + ); + + for( ArgumentMapping argumentMapping : methodMapping.arguments() ) + { + write( out, argumentMapping ); + } + } + + public void write( PrintWriter out, ArgumentMapping argumentMapping ) + throws IOException + { + out.format( "\t\tARG %d %s\n", argumentMapping.getIndex(), argumentMapping.getName() ); + } +} diff --git a/src/cuchaz/enigma/mapping/MethodIndex.java b/src/cuchaz/enigma/mapping/MethodIndex.java deleted file mode 100644 index f965355d..00000000 --- a/src/cuchaz/enigma/mapping/MethodIndex.java +++ /dev/null @@ -1,125 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.mapping; - -import java.io.Serializable; -import java.util.Map; -import java.util.TreeMap; - -public class MethodIndex implements Serializable -{ - private static final long serialVersionUID = -4409570216084263978L; - - private String m_obfName; - private String m_deobfName; - private String m_obfSignature; - private String m_deobfSignature; - private Map m_arguments; - - public MethodIndex( String obfName, String obfSignature, String deobfName, String deobfSignature ) - { - m_obfName = obfName; - m_deobfName = deobfName; - m_obfSignature = obfSignature; - m_deobfSignature = deobfSignature; - m_arguments = new TreeMap(); - } - - public String getObfName( ) - { - return m_obfName; - } - - public String getDeobfName( ) - { - return m_deobfName; - } - public void setDeobfName( String val ) - { - m_deobfName = val; - } - - public String getObfSignature( ) - { - return m_obfSignature; - } - - public String getDeobfSignature( ) - { - return m_deobfSignature; - } - public void setDeobfSignature( String val ) - { - m_deobfSignature = val; - } - - public String getObfArgumentName( int index ) - { - ArgumentIndex argumentIndex = m_arguments.get( index ); - if( argumentIndex != null ) - { - return argumentIndex.getObfName(); - } - - return null; - } - - public String getDeobfArgumentName( int index ) - { - ArgumentIndex argumentIndex = m_arguments.get( index ); - if( argumentIndex != null ) - { - return argumentIndex.getDeobfName(); - } - - return null; - } - - public void setArgumentName( int index, String obfName, String deobfName ) - { - ArgumentIndex argumentIndex = m_arguments.get( index ); - if( argumentIndex == null ) - { - argumentIndex = new ArgumentIndex( obfName, deobfName ); - m_arguments.put( index, argumentIndex ); - } - else - { - argumentIndex.setDeobfName( deobfName ); - } - } - - @Override - public String toString( ) - { - StringBuilder buf = new StringBuilder(); - buf.append( "\t" ); - buf.append( m_obfName ); - buf.append( " <-> " ); - buf.append( m_deobfName ); - buf.append( "\n" ); - buf.append( "\t" ); - buf.append( m_obfSignature ); - buf.append( " <-> " ); - buf.append( m_deobfSignature ); - buf.append( "\n" ); - buf.append( "\tArguments:\n" ); - for( ArgumentIndex argumentIndex : m_arguments.values() ) - { - buf.append( "\t\t" ); - buf.append( argumentIndex.getObfName() ); - buf.append( " <-> " ); - buf.append( argumentIndex.getDeobfName() ); - buf.append( "\n" ); - } - return buf.toString(); - } -} diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java new file mode 100644 index 00000000..f2bc54d1 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; +import java.util.Map; +import java.util.TreeMap; + +public class MethodMapping implements Serializable +{ + private static final long serialVersionUID = -4409570216084263978L; + + private String m_obfName; + private String m_deobfName; + private String m_obfSignature; + private String m_deobfSignature; + private Map m_arguments; + + // NOTE: this argument order is important for the MethodReader/MethodWriter + public MethodMapping( String obfName, String deobfName, String obfSignature, String deobfSignature ) + { + m_obfName = obfName; + m_deobfName = deobfName; + m_obfSignature = obfSignature; + m_deobfSignature = deobfSignature; + m_arguments = new TreeMap(); + } + + public String getObfName( ) + { + return m_obfName; + } + + public String getDeobfName( ) + { + return m_deobfName; + } + public void setDeobfName( String val ) + { + m_deobfName = val; + } + + public String getObfSignature( ) + { + return m_obfSignature; + } + + public String getDeobfSignature( ) + { + return m_deobfSignature; + } + public void setDeobfSignature( String val ) + { + m_deobfSignature = val; + } + + public Iterable arguments( ) + { + return m_arguments.values(); + } + + protected void addArgumentMapping( ArgumentMapping argumentMapping ) + { + m_arguments.put( argumentMapping.getIndex(), argumentMapping ); + } + + public String getObfArgumentName( int index ) + { + ArgumentMapping argumentMapping = m_arguments.get( index ); + if( argumentMapping != null ) + { + return argumentMapping.getName(); + } + + return null; + } + + public String getDeobfArgumentName( int index ) + { + ArgumentMapping argumentMapping = m_arguments.get( index ); + if( argumentMapping != null ) + { + return argumentMapping.getName(); + } + + return null; + } + + public void setArgumentName( int index, String name ) + { + ArgumentMapping argumentMapping = m_arguments.get( index ); + if( argumentMapping == null ) + { + argumentMapping = new ArgumentMapping( index, name ); + m_arguments.put( index, argumentMapping ); + } + else + { + argumentMapping.setName( name ); + } + } + + @Override + public String toString( ) + { + StringBuilder buf = new StringBuilder(); + buf.append( "\t" ); + buf.append( m_obfName ); + buf.append( " <-> " ); + buf.append( m_deobfName ); + buf.append( "\n" ); + buf.append( "\t" ); + buf.append( m_obfSignature ); + buf.append( " <-> " ); + buf.append( m_deobfSignature ); + buf.append( "\n" ); + buf.append( "\tArguments:\n" ); + for( ArgumentMapping argumentMapping : m_arguments.values() ) + { + buf.append( "\t\t" ); + buf.append( argumentMapping.getIndex() ); + buf.append( " <-> " ); + buf.append( argumentMapping.getName() ); + buf.append( "\n" ); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/mapping/Renamer.java b/src/cuchaz/enigma/mapping/Renamer.java new file mode 100644 index 00000000..4a648ad3 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Renamer.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.util.zip.GZIPOutputStream; + +public class Renamer +{ + private Ancestries m_ancestries; + private Mappings m_mappings; + + public Renamer( Ancestries ancestries, Mappings mappings ) + { + m_ancestries = ancestries; + m_mappings = mappings; + } + + public void setClassName( ClassEntry obf, String deobfName ) + { + ClassMapping classMapping = m_mappings.m_classesByObf.get( obf.getName() ); + if( classMapping == null ) + { + classMapping = createClassMapping( obf ); + } + + m_mappings.m_classesByDeobf.remove( classMapping.getDeobfName() ); + classMapping.setDeobfName( deobfName ); + m_mappings.m_classesByDeobf.put( deobfName, classMapping ); + + updateDeobfMethodSignatures(); + + // TEMP + String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf ); + assert( translatedName != null && translatedName.equals( deobfName ) ); + } + + public void setFieldName( FieldEntry obf, String deobfName ) + { + ClassMapping classMapping = m_mappings.m_classesByObf.get( obf.getClassName() ); + if( classMapping == null ) + { + classMapping = createClassMapping( obf.getClassEntry() ); + } + + classMapping.setFieldName( obf.getName(), deobfName ); + + // TEMP + System.out.println( classMapping ); + String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf ); + assert( translatedName != null && translatedName.equals( deobfName ) ); + } + + public void setMethodName( MethodEntry obf, String deobfName ) + { + ClassMapping classMapping = m_mappings.m_classesByObf.get( obf.getClassName() ); + if( classMapping == null ) + { + classMapping = createClassMapping( obf.getClassEntry() ); + } + + String deobfSignature = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); + classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); + + // TODO: update ancestor/descendant methods in other classes in the inheritance hierarchy too + + // TEMP + System.out.println( classMapping ); + String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf ); + assert( translatedName != null && translatedName.equals( deobfName ) ); + } + + public void setArgumentName( ArgumentEntry obf, String deobfName ) + { + ClassMapping classMapping = m_mappings.m_classesByObf.get( obf.getClassName() ); + if( classMapping == null ) + { + classMapping = createClassMapping( obf.getClassEntry() ); + } + + classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName ); + + // TEMP + System.out.println( classMapping ); + String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf ); + assert( translatedName != null && translatedName.equals( deobfName ) ); + } + + public void write( OutputStream out ) + throws IOException + { + // TEMP: just use the object output for now. We can find a more efficient storage format later + GZIPOutputStream gzipout = new GZIPOutputStream( out ); + ObjectOutputStream oout = new ObjectOutputStream( gzipout ); + oout.writeObject( this ); + gzipout.finish(); + } + + private ClassMapping createClassMapping( ClassEntry obf ) + { + ClassMapping classMapping = new ClassMapping( obf.getName(), obf.getName() ); + m_mappings.m_classesByObf.put( classMapping.getObfName(), classMapping ); + m_mappings.m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ); + return classMapping; + } + + private void updateDeobfMethodSignatures( ) + { + Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ); + for( ClassMapping classMapping : m_mappings.m_classesByObf.values() ) + { + classMapping.updateDeobfMethodSignatures( translator ); + } + } +} diff --git a/src/cuchaz/enigma/mapping/TranslationMappings.java b/src/cuchaz/enigma/mapping/TranslationMappings.java deleted file mode 100644 index d6cd4491..00000000 --- a/src/cuchaz/enigma/mapping/TranslationMappings.java +++ /dev/null @@ -1,187 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.mapping; - -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.io.Serializable; -import java.util.Map; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -import com.beust.jcommander.internal.Maps; - -import cuchaz.enigma.Util; - -public class TranslationMappings implements Serializable -{ - private static final long serialVersionUID = 4649790259460259026L; - - private Map m_classesByObf; - private Map m_classesByDeobf; - private Ancestries m_ancestries; - - public TranslationMappings( Ancestries ancestries ) - { - m_classesByObf = Maps.newHashMap(); - m_classesByDeobf = Maps.newHashMap(); - m_ancestries = ancestries; - } - - public static TranslationMappings newFromResource( String resource ) - throws IOException - { - InputStream in = null; - try - { - in = TranslationMappings.class.getResourceAsStream( resource ); - return newFromStream( in ); - } - finally - { - Util.closeQuietly( in ); - } - } - - public Translator getTranslator( TranslationDirection direction ) - { - return new Translator( - direction, - direction.choose( m_classesByObf, m_classesByDeobf ), - direction.choose( m_ancestries, new DeobfuscatedAncestries( m_ancestries, m_classesByObf, m_classesByDeobf ) ) - ); - } - - public void setClassName( ClassEntry obf, String deobfName ) - { - ClassIndex classIndex = m_classesByObf.get( obf.getName() ); - if( classIndex == null ) - { - classIndex = createClassIndex( obf ); - } - - m_classesByDeobf.remove( classIndex.getDeobfName() ); - classIndex.setDeobfName( deobfName ); - m_classesByDeobf.put( deobfName, classIndex ); - - updateDeobfMethodSignatures(); - - // TEMP - String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf ); - assert( translatedName != null && translatedName.equals( deobfName ) ); - } - - public void setFieldName( FieldEntry obf, String deobfName ) - { - ClassIndex classIndex = m_classesByObf.get( obf.getClassName() ); - if( classIndex == null ) - { - classIndex = createClassIndex( obf.getClassEntry() ); - } - - classIndex.setFieldName( obf.getName(), deobfName ); - - // TEMP - System.out.println( classIndex ); - String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf ); - assert( translatedName != null && translatedName.equals( deobfName ) ); - } - - public void setMethodName( MethodEntry obf, String deobfName ) - { - ClassIndex classIndex = m_classesByObf.get( obf.getClassName() ); - if( classIndex == null ) - { - classIndex = createClassIndex( obf.getClassEntry() ); - } - - String deobfSignature = getTranslator( TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); - classIndex.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); - - // TODO: update ancestor/descendant methods in other classes in the inheritance hierarchy too - - // TEMP - System.out.println( classIndex ); - String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf ); - assert( translatedName != null && translatedName.equals( deobfName ) ); - } - - public void setArgumentName( ArgumentEntry obf, String deobfName ) - { - ClassIndex classIndex = m_classesByObf.get( obf.getClassName() ); - if( classIndex == null ) - { - classIndex = createClassIndex( obf.getClassEntry() ); - } - - classIndex.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName(), deobfName ); - - // TEMP - System.out.println( classIndex ); - String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf ); - assert( translatedName != null && translatedName.equals( deobfName ) ); - } - - public void write( OutputStream out ) - throws IOException - { - // TEMP: just use the object output for now. We can find a more efficient storage format later - GZIPOutputStream gzipout = new GZIPOutputStream( out ); - ObjectOutputStream oout = new ObjectOutputStream( gzipout ); - oout.writeObject( this ); - gzipout.finish(); - } - - public static TranslationMappings newFromStream( InputStream in ) - throws IOException - { - try - { - return (TranslationMappings)new ObjectInputStream( new GZIPInputStream( in ) ).readObject(); - } - catch( ClassNotFoundException ex ) - { - throw new Error( ex ); - } - } - - private ClassIndex createClassIndex( ClassEntry obf ) - { - ClassIndex classIndex = new ClassIndex( obf.getName(), obf.getName() ); - m_classesByObf.put( classIndex.getObfName(), classIndex ); - m_classesByDeobf.put( classIndex.getDeobfName(), classIndex ); - return classIndex; - } - - private void updateDeobfMethodSignatures( ) - { - Translator translator = getTranslator( TranslationDirection.Deobfuscating ); - for( ClassIndex classIndex : m_classesByObf.values() ) - { - classIndex.updateDeobfMethodSignatures( translator ); - } - } - - @Override - public String toString( ) - { - StringBuilder buf = new StringBuilder(); - for( ClassIndex classIndex : m_classesByObf.values() ) - { - buf.append( classIndex.toString() ); - buf.append( "\n" ); - } - return buf.toString(); - } -} diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index bae0dce7..3dbc103b 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -19,10 +19,10 @@ import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class Translator { private TranslationDirection m_direction; - private Map m_classes; + private Map m_classes; private Ancestries m_ancestries; - protected Translator( TranslationDirection direction, Map classes, Ancestries ancestries ) + protected Translator( TranslationDirection direction, Map classes, Ancestries ancestries ) { m_direction = direction; m_classes = classes; @@ -36,7 +36,7 @@ public class Translator public String translateClass( String in ) { - ClassIndex classIndex = m_classes.get( in ); + ClassMapping classIndex = m_classes.get( in ); if( classIndex != null ) { return m_direction.choose( @@ -63,7 +63,7 @@ public class Translator for( String className : getSelfAndAncestors( in.getClassName() ) ) { // look for the class - ClassIndex classIndex = m_classes.get( className ); + ClassMapping classIndex = m_classes.get( className ); if( classIndex != null ) { // look for the field @@ -99,11 +99,11 @@ public class Translator for( String className : getSelfAndAncestors( in.getClassName() ) ) { // look for the class - ClassIndex classIndex = m_classes.get( className ); + ClassMapping classIndex = m_classes.get( className ); if( classIndex != null ) { // look for the method - MethodIndex methodIndex = m_direction.choose( + MethodMapping methodIndex = m_direction.choose( classIndex.getMethodByObf( in.getName(), in.getSignature() ), classIndex.getMethodByDeobf( in.getName(), in.getSignature() ) ); @@ -139,11 +139,11 @@ public class Translator for( String className : getSelfAndAncestors( in.getClassName() ) ) { // look for the class - ClassIndex classIndex = m_classes.get( className ); + ClassMapping classIndex = m_classes.get( className ); if( classIndex != null ) { // look for the method - MethodIndex methodIndex = m_direction.choose( + MethodMapping methodIndex = m_direction.choose( classIndex.getMethodByObf( in.getMethodName(), in.getMethodSignature() ), classIndex.getMethodByDeobf( in.getMethodName(), in.getMethodSignature() ) ); -- cgit v1.2.3 From faae0a6514c2565a10f9a62dd18c5d79fbbe4156 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 31 Jul 2014 22:27:26 -0400 Subject: fixed bug with save mappings menu gui shows deobfuscated classes list now working on name validation/sanitization --- src/cuchaz/enigma/ClassFile.java | 34 ++++++----- src/cuchaz/enigma/Deobfuscator.java | 60 +++++-------------- src/cuchaz/enigma/gui/ClassListCellRenderer.java | 40 +++++++++++++ src/cuchaz/enigma/gui/Gui.java | 64 +++++++++++++++++++-- src/cuchaz/enigma/gui/GuiController.java | 18 +++++- .../gui/ObfuscatedClassListCellRenderer.java | 40 ------------- src/cuchaz/enigma/mapping/NameValidator.java | 67 ++++++++++++++++++++++ 7 files changed, 216 insertions(+), 107 deletions(-) create mode 100644 src/cuchaz/enigma/gui/ClassListCellRenderer.java delete mode 100644 src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java create mode 100644 src/cuchaz/enigma/mapping/NameValidator.java diff --git a/src/cuchaz/enigma/ClassFile.java b/src/cuchaz/enigma/ClassFile.java index 221a119e..c3c72a47 100644 --- a/src/cuchaz/enigma/ClassFile.java +++ b/src/cuchaz/enigma/ClassFile.java @@ -10,36 +10,42 @@ ******************************************************************************/ package cuchaz.enigma; -import java.util.regex.Pattern; public class ClassFile { - private static Pattern m_obfuscatedClassPattern; + private String m_obfName; + private String m_deobfName; - static + public ClassFile( String obfName ) { - m_obfuscatedClassPattern = Pattern.compile( "^[a-z]+$" ); + m_obfName = obfName; } - private String m_name; - - public ClassFile( String name ) + public String getName( ) { - m_name = name; + if( m_deobfName != null ) + { + return m_deobfName; + } + return m_obfName; } - public String getName( ) + public String getObfName( ) { - return m_name; + return m_obfName; } - public boolean isObfuscated( ) + public String getDeobfName( ) { - return m_obfuscatedClassPattern.matcher( m_name ).matches(); + return m_deobfName; } - + public void setDeobfName( String val ) + { + m_deobfName = val; + } + public String getPath( ) { - return m_name.replace( ".", "/" ) + ".class"; + return m_deobfName.replace( ".", "/" ) + ".class"; } } diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 7be57062..619eebfc 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -15,9 +15,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; @@ -30,10 +27,12 @@ import com.strobel.decompiler.PlainTextOutput; import cuchaz.enigma.mapping.Ancestries; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.NameValidator; import cuchaz.enigma.mapping.Renamer; import cuchaz.enigma.mapping.TranslationDirection; import cuchaz.enigma.mapping.Translator; @@ -47,25 +46,6 @@ public class Deobfuscator private Mappings m_mappings; private Renamer m_renamer; - private static Comparator m_obfuscatedClassSorter; - - static - { - m_obfuscatedClassSorter = new Comparator( ) - { - @Override - public int compare( ClassFile a, ClassFile b ) - { - if( a.getName().length() != b.getName().length() ) - { - return a.getName().length() - b.getName().length(); - } - - return a.getName().compareTo( b.getName() ); - } - }; - } - public Deobfuscator( File file ) throws IOException { @@ -120,48 +100,38 @@ public class Deobfuscator ) ); } - public List getObfuscatedClasses( ) + public void getSortedClasses( List obfClasses, List deobfClasses ) { - List classes = new ArrayList(); Enumeration entries = m_jar.entries(); while( entries.hasMoreElements() ) { JarEntry entry = entries.nextElement(); // get the class name - String className = toClassName( entry.getName() ); - if( className == null ) + String obfName = NameValidator.fileNameToClassName( entry.getName() ); + if( obfName == null ) { continue; } - ClassFile classFile = new ClassFile( className ); - if( classFile.isObfuscated() ) + ClassFile classFile = new ClassFile( obfName ); + ClassMapping classMapping = m_mappings.getClassByObf( classFile.getName() ); + if( classMapping != null ) { - classes.add( classFile ); + classFile.setDeobfName( classMapping.getDeobfName() ); + deobfClasses.add( classFile ); + } + else + { + obfClasses.add( classFile ); } } - Collections.sort( classes, m_obfuscatedClassSorter ); - return classes; - } - - // TODO: could go somewhere more generic - private static String toClassName( String fileName ) - { - final String suffix = ".class"; - - if( !fileName.endsWith( suffix ) ) - { - return null; - } - - return fileName.substring( 0, fileName.length() - suffix.length() ).replace( "/", "." ); } public String getSource( final ClassFile classFile ) { StringWriter buf = new StringWriter(); - Decompiler.decompile( classFile.getName(), new PlainTextOutput( buf ), m_settings ); + Decompiler.decompile( classFile.getObfName(), new PlainTextOutput( buf ), m_settings ); return buf.toString(); } diff --git a/src/cuchaz/enigma/gui/ClassListCellRenderer.java b/src/cuchaz/enigma/gui/ClassListCellRenderer.java new file mode 100644 index 00000000..302f140b --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassListCellRenderer.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Component; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +import cuchaz.enigma.ClassFile; + +public class ClassListCellRenderer implements ListCellRenderer +{ + private DefaultListCellRenderer m_defaultRenderer; + + public ClassListCellRenderer( ) + { + m_defaultRenderer = new DefaultListCellRenderer(); + } + + @Override + public Component getListCellRendererComponent( JList list, ClassFile classFile, int index, boolean isSelected, boolean hasFocus ) + { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, classFile, index, isSelected, hasFocus ); + + label.setText( classFile.getName() ); + + return label; + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index a86ff9b0..d448dc28 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -21,6 +21,8 @@ import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.IOException; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Vector; @@ -58,6 +60,25 @@ import cuchaz.enigma.mapping.MethodEntry; public class Gui { + private static Comparator m_obfuscatedClassSorter; + + static + { + m_obfuscatedClassSorter = new Comparator( ) + { + @Override + public int compare( ClassFile a, ClassFile b ) + { + if( a.getName().length() != b.getName().length() ) + { + return a.getName().length() - b.getName().length(); + } + + return a.getName().compareTo( b.getName() ); + } + }; + } + private GuiController m_controller; // controls @@ -101,7 +122,7 @@ public class Gui m_obfClasses = new JList(); m_obfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); m_obfClasses.setLayoutOrientation( JList.VERTICAL ); - m_obfClasses.setCellRenderer( new ObfuscatedClassListCellRenderer() ); + m_obfClasses.setCellRenderer( new ClassListCellRenderer() ); m_obfClasses.addMouseListener( new MouseAdapter() { public void mouseClicked( MouseEvent event ) @@ -124,8 +145,23 @@ public class Gui // init deobfuscated classes list m_deobfClasses = new JList(); - m_obfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); - m_obfClasses.setLayoutOrientation( JList.VERTICAL ); + m_deobfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + m_deobfClasses.setLayoutOrientation( JList.VERTICAL ); + m_deobfClasses.setCellRenderer( new ClassListCellRenderer() ); + m_deobfClasses.addMouseListener( new MouseAdapter() + { + public void mouseClicked( MouseEvent event ) + { + if( event.getClickCount() == 2 ) + { + ClassFile selected = m_deobfClasses.getSelectedValue(); + if( selected != null ) + { + m_controller.deobfuscateClass( selected ); + } + } + } + } ); JScrollPane deobfScroller = new JScrollPane( m_deobfClasses ); JPanel deobfPanel = new JPanel(); deobfPanel.setLayout( new BorderLayout() ); @@ -248,7 +284,6 @@ public class Gui try { m_controller.openMappings( m_mappingFileChooser.getSelectedFile() ); - m_saveMappingsMenu.setEnabled( true ); } catch( IOException ex ) { @@ -383,7 +418,9 @@ public class Gui { if( classes != null ) { - m_obfClasses.setListData( new Vector( classes ) ); + Vector sortedClasses = new Vector( classes ); + Collections.sort( sortedClasses, m_obfuscatedClassSorter ); + m_obfClasses.setListData( sortedClasses ); } else { @@ -391,6 +428,23 @@ public class Gui } } + public void setDeobfClasses( List classes ) + { + if( classes != null ) + { + m_deobfClasses.setListData( new Vector( classes ) ); + } + else + { + m_deobfClasses.setListData( new Vector() ); + } + } + + public void setMappingsLoaded( boolean isLoaded ) + { + m_saveMappingsMenu.setEnabled( isLoaded ); + } + public void setSource( String source ) { setSource( source, null ); diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index fb22b961..ce1c31bd 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -14,6 +14,8 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import cuchaz.enigma.ClassFile; import cuchaz.enigma.Deobfuscator; @@ -45,7 +47,7 @@ public class GuiController { m_deobfuscator = new Deobfuscator( file ); m_gui.onOpenJar( m_deobfuscator.getJarName() ); - m_gui.setObfClasses( m_deobfuscator.getObfuscatedClasses() ); + refreshClasses(); } public void closeJar( ) @@ -60,8 +62,8 @@ public class GuiController FileReader in = new FileReader( file ); m_deobfuscator.setMappings( new MappingsReader().read( in ) ); in.close(); - // TEMP - System.out.println( m_deobfuscator.getMappings() ); + m_gui.setMappingsLoaded( true ); + refreshClasses(); refreshOpenFiles(); } @@ -76,6 +78,7 @@ public class GuiController public void closeMappings( ) { m_deobfuscator.setMappings( null ); + m_gui.setMappingsLoaded( false ); refreshOpenFiles(); } @@ -119,6 +122,15 @@ public class GuiController refreshOpenFiles(); } + private void refreshClasses( ) + { + List obfClasses = new ArrayList(); + List deobfClasses = new ArrayList(); + m_deobfuscator.getSortedClasses( obfClasses, deobfClasses ); + m_gui.setObfClasses( obfClasses ); + m_gui.setDeobfClasses( deobfClasses ); + } + private void refreshOpenFiles( ) { if( m_currentFile != null ) diff --git a/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java b/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java deleted file mode 100644 index 0badb3b9..00000000 --- a/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.gui; - -import java.awt.Component; - -import javax.swing.DefaultListCellRenderer; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.ListCellRenderer; - -import cuchaz.enigma.ClassFile; - -public class ObfuscatedClassListCellRenderer implements ListCellRenderer -{ - private DefaultListCellRenderer m_defaultRenderer; - - public ObfuscatedClassListCellRenderer( ) - { - m_defaultRenderer = new DefaultListCellRenderer(); - } - - @Override - public Component getListCellRendererComponent( JList list, ClassFile classFile, int index, boolean isSelected, boolean hasFocus ) - { - JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, classFile, index, isSelected, hasFocus ); - - label.setText( classFile.getName() ); - - return label; - } -} diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java new file mode 100644 index 00000000..30982dc6 --- /dev/null +++ b/src/cuchaz/enigma/mapping/NameValidator.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.util.regex.Pattern; + +public class NameValidator +{ + private static final String IdentifierPattern; + private static final Pattern ClassPattern; + + static + { + // java allows all kinds of weird characters... + StringBuilder startChars = new StringBuilder(); + StringBuilder partChars = new StringBuilder(); + for( int i = Character.MIN_CODE_POINT; i <= Character.MAX_CODE_POINT; i++ ) + { + if( Character.isJavaIdentifierStart( i ) ) + { + startChars.appendCodePoint( i ); + } + if( Character.isJavaIdentifierPart( i ) ) + { + partChars.appendCodePoint( i ); + } + } + + IdentifierPattern = String.format( "[\\Q%s\\E][\\Q%s\\E]*", startChars.toString(), partChars.toString() ); + ClassPattern = Pattern.compile( String.format( "^(%s(\\.|/))*(%s)$", IdentifierPattern, IdentifierPattern ) ); + } + + public String validateClassName( String name ) + { + if( !ClassPattern.matcher( name ).matches() ) + { + throw new IllegalArgumentException( "Illegal name: " + name ); + } + + return classNameToJavaName( name ); + } + + public static String fileNameToClassName( String fileName ) + { + final String suffix = ".class"; + + if( !fileName.endsWith( suffix ) ) + { + return null; + } + + return fileName.substring( 0, fileName.length() - suffix.length() ).replace( "/", "." ); + } + + public static String classNameToJavaName( String className ) + { + return className.replace( ".", "/" ); + } +} -- cgit v1.2.3 From ada041979ecf3dfd4543f3c250fcc78ad594866c Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 2 Aug 2014 16:45:32 -0400 Subject: started working on method parameter renaming --- src/cuchaz/enigma/Deobfuscator.java | 4 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 10 +-- .../enigma/bytecode/MethodParameterWriter.java | 35 ++++++++++ .../enigma/bytecode/MethodParametersAttribute.java | 80 ++++++++++++++++++++++ 4 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 src/cuchaz/enigma/bytecode/MethodParameterWriter.java create mode 100644 src/cuchaz/enigma/bytecode/MethodParametersAttribute.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 619eebfc..edc29e17 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -95,8 +95,8 @@ public class Deobfuscator // update decompiler options m_settings.setTypeLoader( new TranslatingTypeLoader( m_jar, - m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ), - m_mappings.getTranslator( m_ancestries, TranslationDirection.Obfuscating ) + m_mappings.getTranslator( m_ancestries, TranslationDirection.Obfuscating ), + m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ) ) ); } diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 872f4861..cd36e8da 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -23,19 +23,20 @@ import com.strobel.assembler.metadata.Buffer; import com.strobel.assembler.metadata.ITypeLoader; import cuchaz.enigma.bytecode.ClassTranslator; +import cuchaz.enigma.bytecode.MethodParameterWriter; import cuchaz.enigma.mapping.Translator; public class TranslatingTypeLoader implements ITypeLoader { private JarFile m_jar; - private ClassTranslator m_classTranslator; private Translator m_obfuscatingTranslator; + private Translator m_deobfuscatingTranslator; - public TranslatingTypeLoader( JarFile jar, Translator deobfuscatingTranslator, Translator obfuscatingTranslator ) + public TranslatingTypeLoader( JarFile jar, Translator obfuscatingTranslator, Translator deobfuscatingTranslator ) { m_jar = jar; - m_classTranslator = new ClassTranslator( deobfuscatingTranslator ); m_obfuscatingTranslator = obfuscatingTranslator; + m_deobfuscatingTranslator = deobfuscatingTranslator; } @Override @@ -69,7 +70,8 @@ public class TranslatingTypeLoader implements ITypeLoader try { CtClass c = classPool.get( name ); - m_classTranslator.translate( c ); + new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); + new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); buf = c.toBytecode(); } catch( Exception ex ) diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java new file mode 100644 index 00000000..1e5d1f0a --- /dev/null +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.bytecode.AttributeInfo; +import cuchaz.enigma.mapping.Translator; + +public class MethodParameterWriter +{ + private Translator m_translator; + + public MethodParameterWriter( Translator translator ) + { + m_translator = translator; + } + + public void writeMethodArguments( CtClass c ) + { + // Procyon will read method arguments from the "MethodParameters" attribute, so write those + for( CtBehavior behavior : c.getDeclaredBehaviors() ) + { + AttributeInfo attribute = behavior.getMethodInfo().getAttribute( "MethodParameter" ); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java new file mode 100644 index 00000000..0b29403d --- /dev/null +++ b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.List; + +import javassist.bytecode.AttributeInfo; +import javassist.bytecode.ConstPool; + +public class MethodParametersAttribute extends AttributeInfo +{ + public MethodParametersAttribute( ConstPool pool, int attributeNameIndex, List parameterNameIndices ) + { + super( pool, "MethodParameters", writeStruct( attributeNameIndex, parameterNameIndices ) ); + } + + private static byte[] writeStruct( int attributeNameIndex, List parameterNameIndices ) + { + // JVM Spec says the struct looks like this: + // http://cr.openjdk.java.net/~mr/se/8/java-se-8-fr-spec-01/java-se-8-jvms-fr-diffs.pdf + // uint16 name_index -> points to UTF8 entry in constant pool that says "MethodParameters" + // uint32 length -> length of this struct, minus 6 bytes (ie, length of num_params and parameter array) + // uint8 num_params + // for each param: + // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry + // uint16 access_flags -> don't care, just set to 0 + + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream( buf ); + + // NOTE: java hates unsigned integers, so we have to be careful here + // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument + // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte + // if the int is out of range, the byte stream won't look the way we want and weird things will happen + final int SIZEOF_UINT16 = 2; + final int MAX_UINT8 = ( 1 << 8 ) - 1; + final int MAX_UINT16 = ( 1 << 16 ) - 1; + final long MAX_UINT32 = ( 1 << 32 ) - 1; + + try + { + assert( attributeNameIndex >= 0 && attributeNameIndex <= MAX_UINT16 ); + out.writeShort( attributeNameIndex ); + + long length = SIZEOF_UINT16 + parameterNameIndices.size()*( SIZEOF_UINT16 + SIZEOF_UINT16 ); + assert( length >= 0 && length <= MAX_UINT32 ); + out.writeInt( (int)length ); + + assert( parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8 ); + out.writeByte( parameterNameIndices.size() ); + + for( Integer index : parameterNameIndices ) + { + assert( index >= 0 && index <= MAX_UINT16 ); + out.writeShort( index ); + + // just write 0 for the access flags + out.writeShort( 0 ); + } + + out.close(); + return buf.toByteArray(); + } + catch( IOException ex ) + { + throw new Error( ex ); + } + } +} -- cgit v1.2.3 From 76be350b3c54ea88cc1a95b5cf0d1db153f2edb3 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 3 Aug 2014 11:16:33 -0400 Subject: fixed bugs with saving mappings got argument renaming to work --- src/cuchaz/enigma/TranslatingTypeLoader.java | 2 +- src/cuchaz/enigma/analysis/SourcedAst.java | 21 +++++++-- .../enigma/bytecode/MethodParameterWriter.java | 26 ++++++++++- .../enigma/bytecode/MethodParametersAttribute.java | 46 ++++++++++++------ src/cuchaz/enigma/gui/Gui.java | 54 ++++++++++++---------- src/cuchaz/enigma/gui/GuiController.java | 8 ++-- src/cuchaz/enigma/mapping/EntryPair.java | 30 ++---------- src/cuchaz/enigma/mapping/MethodMapping.java | 2 +- 8 files changed, 112 insertions(+), 77 deletions(-) diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index cd36e8da..e57a09de 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -70,8 +70,8 @@ public class TranslatingTypeLoader implements ITypeLoader try { CtClass c = classPool.get( name ); - new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); + new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); buf = c.toBytecode(); } catch( Exception ex ) diff --git a/src/cuchaz/enigma/analysis/SourcedAst.java b/src/cuchaz/enigma/analysis/SourcedAst.java index 04c6f03b..52a34534 100644 --- a/src/cuchaz/enigma/analysis/SourcedAst.java +++ b/src/cuchaz/enigma/analysis/SourcedAst.java @@ -13,7 +13,10 @@ package cuchaz.enigma.analysis; import java.io.IOException; import java.util.HashMap; +import javassist.bytecode.Descriptor; + import com.google.common.collect.Maps; +import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.ImportTree; import com.sun.source.tree.Tree; @@ -34,7 +37,7 @@ public class SourcedAst m_positions = m_trees.getSourcePositions(); m_classNameIndex = Maps.newHashMap(); - // index all the class names + // index all the class names from package imports for( ImportTree importTree : m_tree.getImports() ) { // ignore static imports for now @@ -44,9 +47,9 @@ public class SourcedAst } // get the full and simple class names - String fullName = importTree.getQualifiedIdentifier().toString(); + String fullName = Descriptor.toJvmName( importTree.getQualifiedIdentifier().toString() ); String simpleName = fullName; - String[] parts = fullName.split( "\\." ); + String[] parts = fullName.split( "/" ); if( parts.length > 0 ) { simpleName = parts[parts.length - 1]; @@ -54,6 +57,18 @@ public class SourcedAst m_classNameIndex.put( simpleName, fullName ); } + + // index the self class using the package name + String packageName = Descriptor.toJvmName( m_tree.getPackageName().toString() ); + for( Tree typeTree : m_tree.getTypeDecls() ) + { + if( typeTree instanceof ClassTree ) + { + ClassTree classTree = (ClassTree)typeTree; + String className = classTree.getSimpleName().toString(); + m_classNameIndex.put( className, packageName + "/" + className ); + } + } } public int getStart( Tree node ) diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java index 1e5d1f0a..a8d3983f 100644 --- a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -10,9 +10,15 @@ ******************************************************************************/ package cuchaz.enigma.bytecode; +import java.util.ArrayList; +import java.util.List; + import javassist.CtBehavior; import javassist.CtClass; -import javassist.bytecode.AttributeInfo; +import javassist.bytecode.Descriptor; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; public class MethodParameterWriter @@ -27,9 +33,25 @@ public class MethodParameterWriter public void writeMethodArguments( CtClass c ) { // Procyon will read method arguments from the "MethodParameters" attribute, so write those + ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); for( CtBehavior behavior : c.getDeclaredBehaviors() ) { - AttributeInfo attribute = behavior.getMethodInfo().getAttribute( "MethodParameter" ); + int numParams = Descriptor.numOfParameters( behavior.getMethodInfo().getDescriptor() ); + if( numParams <= 0 ) + { + continue; + } + + // get the list of parameter names + MethodEntry methodEntry = new MethodEntry( classEntry, behavior.getMethodInfo().getName(), behavior.getSignature() ); + List names = new ArrayList( numParams ); + for( int i=0; i parameterNameIndices ) + private MethodParametersAttribute( ConstPool pool, List parameterNameIndices ) { - super( pool, "MethodParameters", writeStruct( attributeNameIndex, parameterNameIndices ) ); + super( pool, "MethodParameters", writeStruct( parameterNameIndices ) ); } - private static byte[] writeStruct( int attributeNameIndex, List parameterNameIndices ) + public static void updateClass( MethodInfo info, List names ) { - // JVM Spec says the struct looks like this: + // add the names to the class const pool + ConstPool constPool = info.getConstPool(); + List parameterNameIndices = new ArrayList(); + for( String name : names ) + { + if( name != null ) + { + parameterNameIndices.add( constPool.addUtf8Info( name ) ); + } + else + { + parameterNameIndices.add( 0 ); + } + } + + // add the attribute to the method + info.addAttribute( new MethodParametersAttribute( constPool, parameterNameIndices ) ); + } + + private static byte[] writeStruct( List parameterNameIndices ) + { + // JVM 8 Spec says the struct looks like this: // http://cr.openjdk.java.net/~mr/se/8/java-se-8-fr-spec-01/java-se-8-jvms-fr-diffs.pdf - // uint16 name_index -> points to UTF8 entry in constant pool that says "MethodParameters" - // uint32 length -> length of this struct, minus 6 bytes (ie, length of num_params and parameter array) // uint8 num_params // for each param: // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry @@ -43,20 +64,13 @@ public class MethodParametersAttribute extends AttributeInfo // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte // if the int is out of range, the byte stream won't look the way we want and weird things will happen + final int SIZEOF_UINT8 = 1; final int SIZEOF_UINT16 = 2; final int MAX_UINT8 = ( 1 << 8 ) - 1; final int MAX_UINT16 = ( 1 << 16 ) - 1; - final long MAX_UINT32 = ( 1 << 32 ) - 1; try { - assert( attributeNameIndex >= 0 && attributeNameIndex <= MAX_UINT16 ); - out.writeShort( attributeNameIndex ); - - long length = SIZEOF_UINT16 + parameterNameIndices.size()*( SIZEOF_UINT16 + SIZEOF_UINT16 ); - assert( length >= 0 && length <= MAX_UINT32 ); - out.writeInt( (int)length ); - assert( parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8 ); out.writeByte( parameterNameIndices.size() ); @@ -70,7 +84,9 @@ public class MethodParametersAttribute extends AttributeInfo } out.close(); - return buf.toByteArray(); + byte[] data = buf.toByteArray(); + assert( data.length == SIZEOF_UINT8 + parameterNameIndices.size()*( SIZEOF_UINT16 + SIZEOF_UINT16 ) ); + return data; } catch( IOException ex ) { diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index d448dc28..3f46b6ec 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -20,6 +20,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.Comparator; @@ -54,6 +55,7 @@ import cuchaz.enigma.Constants; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; @@ -101,9 +103,9 @@ public class Gui private JMenuItem m_closeMappingsMenu; // state - private EntryPair m_selectedEntryPair; + private EntryPair m_selectedEntryPair; private JFileChooser m_jarFileChooser; - private JFileChooser m_mappingFileChooser; + private JFileChooser m_mappingsFileChooser; public Gui( ) { @@ -111,7 +113,7 @@ public class Gui // init file choosers m_jarFileChooser = new JFileChooser(); - m_mappingFileChooser = new JFileChooser(); + m_mappingsFileChooser = new JFileChooser(); // init frame m_frame = new JFrame( Constants.Name ); @@ -279,11 +281,11 @@ public class Gui @Override public void actionPerformed( ActionEvent event ) { - if( m_mappingFileChooser.showOpenDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) + if( m_mappingsFileChooser.showOpenDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) { try { - m_controller.openMappings( m_mappingFileChooser.getSelectedFile() ); + m_controller.openMappings( m_mappingsFileChooser.getSelectedFile() ); } catch( IOException ex ) { @@ -304,7 +306,7 @@ public class Gui { try { - m_controller.saveMappings( m_mappingFileChooser.getSelectedFile() ); + m_controller.saveMappings( m_mappingsFileChooser.getSelectedFile() ); } catch( IOException ex ) { @@ -322,11 +324,11 @@ public class Gui @Override public void actionPerformed( ActionEvent event ) { - if( m_mappingFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) + if( m_mappingsFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) { try { - m_controller.saveMappings( m_mappingFileChooser.getSelectedFile() ); + m_controller.saveMappings( m_mappingsFileChooser.getSelectedFile() ); m_saveMappingsMenu.setEnabled( true ); } catch( IOException ex ) @@ -440,9 +442,10 @@ public class Gui } } - public void setMappingsLoaded( boolean isLoaded ) + public void setMappingsFile( File file ) { - m_saveMappingsMenu.setEnabled( isLoaded ); + m_mappingsFileChooser.setSelectedFile( file ); + m_saveMappingsMenu.setEnabled( file != null ); } public void setSource( String source ) @@ -493,7 +496,8 @@ public class Gui redraw(); } - private void showEntryPair( EntryPair pair ) + @SuppressWarnings( "unchecked" ) + private void showEntryPair( EntryPair pair ) { if( pair == null ) { @@ -514,19 +518,19 @@ public class Gui m_actionPanel.add( dynamicPanel ); if( pair.deobf instanceof ClassEntry ) { - showEntry( (ClassEntry)pair.deobf, dynamicPanel ); + showClassEntryPair( (EntryPair)pair, dynamicPanel ); } else if( pair.deobf instanceof FieldEntry ) { - showEntry( (FieldEntry)pair.deobf, dynamicPanel ); + showFieldEntryPair( (EntryPair)pair, dynamicPanel ); } else if( pair.deobf instanceof MethodEntry ) { - showEntry( (MethodEntry)pair.deobf, dynamicPanel ); + showMethodEntryPair( (EntryPair)pair, dynamicPanel ); } else if( pair.deobf instanceof ArgumentEntry ) { - showEntry( (ArgumentEntry)pair.deobf, dynamicPanel ); + showArgumentEntryPair( (EntryPair)pair, dynamicPanel ); } else { @@ -536,30 +540,30 @@ public class Gui redraw(); } - private void showEntry( ClassEntry entry, JPanel panel ) + private void showClassEntryPair( EntryPair pair, JPanel panel ) { m_typeLabel.setText( "Class: " ); } - private void showEntry( FieldEntry entry, JPanel panel ) + private void showFieldEntryPair( EntryPair pair, JPanel panel ) { m_typeLabel.setText( "Field: " ); - addNameValue( panel, "Class", entry.getClassEntry().getName() ); + addNameValue( panel, "Class", pair.obf.getClassEntry().getName() + " <-> " + pair.deobf.getClassEntry().getName() ); } - private void showEntry( MethodEntry entry, JPanel panel ) + private void showMethodEntryPair( EntryPair pair, JPanel panel ) { m_typeLabel.setText( "Method: " ); - addNameValue( panel, "Class", entry.getClassEntry().getName() ); - addNameValue( panel, "Signature", entry.getSignature() ); + addNameValue( panel, "Class", pair.obf.getClassEntry().getName() + " <-> " + pair.deobf.getClassEntry().getName() ); + addNameValue( panel, "Signature", pair.obf.getSignature() + " <-> " + pair.deobf.getSignature() ); } - private void showEntry( ArgumentEntry entry, JPanel panel ) + private void showArgumentEntryPair( EntryPair pair, JPanel panel ) { m_typeLabel.setText( "Argument: " ); - addNameValue( panel, "Class", entry.getMethodEntry().getClassEntry().getName() ); - addNameValue( panel, "Method", entry.getMethodEntry().getName() ); - addNameValue( panel, "Index", Integer.toString( entry.getIndex() ) ); + addNameValue( panel, "Class", pair.obf.getClassEntry().getName() + " <-> " + pair.deobf.getClassEntry().getName() ); + addNameValue( panel, "Method", pair.obf.getMethodEntry().getName() + " <-> " + pair.deobf.getMethodEntry().getName() ); + addNameValue( panel, "Index", Integer.toString( pair.obf.getIndex() ) ); } private void addNameValue( JPanel container, String name, String value ) diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index ce1c31bd..6704ef8a 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -62,7 +62,7 @@ public class GuiController FileReader in = new FileReader( file ); m_deobfuscator.setMappings( new MappingsReader().read( in ) ); in.close(); - m_gui.setMappingsLoaded( true ); + m_gui.setMappingsFile( file ); refreshClasses(); refreshOpenFiles(); } @@ -78,7 +78,7 @@ public class GuiController public void closeMappings( ) { m_deobfuscator.setMappings( null ); - m_gui.setMappingsLoaded( false ); + m_gui.setMappingsFile( null ); refreshOpenFiles(); } @@ -88,7 +88,7 @@ public class GuiController deobfuscate( m_currentFile ); } - public EntryPair getEntryPair( int pos ) + public EntryPair getEntryPair( int pos ) { if( m_index == null ) { @@ -100,7 +100,7 @@ public class GuiController { return null; } - return new EntryPair( m_deobfuscator.obfuscate( deobfEntry ), deobfEntry ); + return new EntryPair( m_deobfuscator.obfuscate( deobfEntry ), deobfEntry ); } public void rename( Entry obfsEntry, String newName ) diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java index e40e9992..e3325b37 100644 --- a/src/cuchaz/enigma/mapping/EntryPair.java +++ b/src/cuchaz/enigma/mapping/EntryPair.java @@ -10,37 +10,15 @@ ******************************************************************************/ package cuchaz.enigma.mapping; -import cuchaz.enigma.Util; -public class EntryPair +public class EntryPair { - public Entry obf; - public Entry deobf; + public T obf; + public T deobf; - public EntryPair( Entry obf, Entry deobf ) + public EntryPair( T obf, T deobf ) { this.obf = obf; this.deobf = deobf; } - - @Override - public int hashCode( ) - { - return Util.combineHashesOrdered( obf, deobf ); - } - - @Override - public boolean equals( Object other ) - { - if( other instanceof EntryPair ) - { - return equals( (EntryPair)other ); - } - return false; - } - - public boolean equals( EntryPair other ) - { - return obf.equals( other.obf ) && deobf.equals( other.deobf ); - } } diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index f2bc54d1..1cdc38a1 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -127,7 +127,7 @@ public class MethodMapping implements Serializable { buf.append( "\t\t" ); buf.append( argumentMapping.getIndex() ); - buf.append( " <-> " ); + buf.append( " -> " ); buf.append( argumentMapping.getName() ); buf.append( "\n" ); } -- cgit v1.2.3 From 57f45b0409d5363782052183bb090175c469f89a Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 4 Aug 2014 00:26:48 -0400 Subject: added stable save order for mappings to hopefully help with merging added color-coding for source identifiers redesigned rename GUI customized editor pane, added popup menu finished name validation added last-chance save on window close --- src/cuchaz/enigma/ClassFile.java | 31 +- src/cuchaz/enigma/Deobfuscator.java | 119 ++++--- src/cuchaz/enigma/analysis/SourceIndex.java | 4 +- src/cuchaz/enigma/analysis/SourcedAst.java | 15 +- src/cuchaz/enigma/gui/AboutDialog.java | 2 - src/cuchaz/enigma/gui/BoxHighlightPainter.java | 16 +- src/cuchaz/enigma/gui/BrowserCaret.java | 50 +++ src/cuchaz/enigma/gui/ClassListCellRenderer.java | 40 --- .../gui/DeobfuscatedClassListCellRenderer.java | 43 +++ .../enigma/gui/DeobfuscatedHighlightPainter.java | 22 ++ src/cuchaz/enigma/gui/Gui.java | 354 ++++++++++++++++----- src/cuchaz/enigma/gui/GuiController.java | 94 ++++-- .../gui/ObfuscatedClassListCellRenderer.java | 42 +++ .../enigma/gui/ObfuscatedHighlightPainter.java | 22 ++ src/cuchaz/enigma/mapping/ArgumentMapping.java | 12 +- src/cuchaz/enigma/mapping/ClassMapping.java | 27 +- src/cuchaz/enigma/mapping/EntryPair.java | 6 +- src/cuchaz/enigma/mapping/FieldMapping.java | 12 +- .../enigma/mapping/IllegalNameException.java | 29 ++ src/cuchaz/enigma/mapping/MappingsWriter.java | 22 +- src/cuchaz/enigma/mapping/MethodMapping.java | 12 +- src/cuchaz/enigma/mapping/NameValidator.java | 52 ++- src/cuchaz/enigma/mapping/Renamer.java | 19 -- 23 files changed, 746 insertions(+), 299 deletions(-) create mode 100644 src/cuchaz/enigma/gui/BrowserCaret.java delete mode 100644 src/cuchaz/enigma/gui/ClassListCellRenderer.java create mode 100644 src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java create mode 100644 src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java create mode 100644 src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java create mode 100644 src/cuchaz/enigma/mapping/IllegalNameException.java diff --git a/src/cuchaz/enigma/ClassFile.java b/src/cuchaz/enigma/ClassFile.java index c3c72a47..613b379d 100644 --- a/src/cuchaz/enigma/ClassFile.java +++ b/src/cuchaz/enigma/ClassFile.java @@ -13,39 +13,24 @@ package cuchaz.enigma; public class ClassFile { - private String m_obfName; - private String m_deobfName; + private String m_name; - public ClassFile( String obfName ) + public ClassFile( String name ) { - m_obfName = obfName; - } - - public String getName( ) - { - if( m_deobfName != null ) + if( name.indexOf( '.' ) >= 0 ) { - return m_deobfName; + throw new IllegalArgumentException( "Class name should be in JVM format!" ); } - return m_obfName; - } - - public String getObfName( ) - { - return m_obfName; + m_name = name; } - public String getDeobfName( ) - { - return m_deobfName; - } - public void setDeobfName( String val ) + public String getName( ) { - m_deobfName = val; + return m_name; } public String getPath( ) { - return m_deobfName.replace( ".", "/" ) + ".class"; + return m_name.replace( ".", "/" ) + ".class"; } } diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index edc29e17..a3937b4c 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -17,6 +17,7 @@ import java.io.InputStream; import java.io.StringWriter; import java.util.Enumeration; import java.util.List; +import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -32,7 +33,6 @@ import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MethodEntry; -import cuchaz.enigma.mapping.NameValidator; import cuchaz.enigma.mapping.Renamer; import cuchaz.enigma.mapping.TranslationDirection; import cuchaz.enigma.mapping.Translator; @@ -100,26 +100,28 @@ public class Deobfuscator ) ); } - public void getSortedClasses( List obfClasses, List deobfClasses ) + public void getSeparatedClasses( List obfClasses, Map deobfClasses ) { Enumeration entries = m_jar.entries(); while( entries.hasMoreElements() ) { JarEntry entry = entries.nextElement(); - // get the class name - String obfName = NameValidator.fileNameToClassName( entry.getName() ); - if( obfName == null ) + // skip everything but class files + if( !entry.getName().endsWith( ".class" ) ) { continue; } - ClassFile classFile = new ClassFile( obfName ); + // get the class name from the file + String className = entry.getName().substring( 0, entry.getName().length() - 6 ); + ClassFile classFile = new ClassFile( className ); + + // separate the classes ClassMapping classMapping = m_mappings.getClassByObf( classFile.getName() ); if( classMapping != null ) { - classFile.setDeobfName( classMapping.getDeobfName() ); - deobfClasses.add( classFile ); + deobfClasses.put( classFile, classMapping.getDeobfName() ); } else { @@ -130,84 +132,123 @@ public class Deobfuscator public String getSource( final ClassFile classFile ) { + // is this class deobfuscated? + // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out + // the decompiler only sees the deobfuscated class, so we need to load it by the deobfuscated name + String deobfName = classFile.getName(); + ClassMapping classMapping = m_mappings.getClassByObf( classFile.getName() ); + if( classMapping != null ) + { + deobfName = classMapping.getDeobfName(); + } + + // decompile it! StringWriter buf = new StringWriter(); - Decompiler.decompile( classFile.getObfName(), new PlainTextOutput( buf ), m_settings ); + Decompiler.decompile( deobfName, new PlainTextOutput( buf ), m_settings ); return buf.toString(); } // NOTE: these methods are a bit messy... oh well - public void rename( Entry entry, String newName ) + public void rename( Entry obfEntry, String newName ) { - if( entry instanceof ClassEntry ) + if( obfEntry instanceof ClassEntry ) { - m_renamer.setClassName( (ClassEntry)entry, newName ); + m_renamer.setClassName( (ClassEntry)obfEntry, newName ); } - else if( entry instanceof FieldEntry ) + else if( obfEntry instanceof FieldEntry ) { - m_renamer.setFieldName( (FieldEntry)entry, newName ); + m_renamer.setFieldName( (FieldEntry)obfEntry, newName ); } - else if( entry instanceof MethodEntry ) + else if( obfEntry instanceof MethodEntry ) { - m_renamer.setMethodName( (MethodEntry)entry, newName ); + m_renamer.setMethodName( (MethodEntry)obfEntry, newName ); } - else if( entry instanceof ArgumentEntry ) + else if( obfEntry instanceof ArgumentEntry ) { - m_renamer.setArgumentName( (ArgumentEntry)entry, newName ); + m_renamer.setArgumentName( (ArgumentEntry)obfEntry, newName ); } else { - throw new Error( "Unknown entry type: " + entry.getClass().getName() ); + throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); } } - public Entry obfuscate( Entry in ) + public Entry obfuscateEntry( Entry deobfEntry ) { Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Obfuscating ); - if( in instanceof ClassEntry ) + if( deobfEntry instanceof ClassEntry ) + { + return translator.translateEntry( (ClassEntry)deobfEntry ); + } + else if( deobfEntry instanceof FieldEntry ) + { + return translator.translateEntry( (FieldEntry)deobfEntry ); + } + else if( deobfEntry instanceof MethodEntry ) + { + return translator.translateEntry( (MethodEntry)deobfEntry ); + } + else if( deobfEntry instanceof ArgumentEntry ) + { + return translator.translateEntry( (ArgumentEntry)deobfEntry ); + } + else + { + throw new Error( "Unknown entry type: " + deobfEntry.getClass().getName() ); + } + } + + public Entry deobfuscateEntry( Entry obfEntry ) + { + Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ); + if( obfEntry instanceof ClassEntry ) { - return translator.translateEntry( (ClassEntry)in ); + return translator.translateEntry( (ClassEntry)obfEntry ); } - else if( in instanceof FieldEntry ) + else if( obfEntry instanceof FieldEntry ) { - return translator.translateEntry( (FieldEntry)in ); + return translator.translateEntry( (FieldEntry)obfEntry ); } - else if( in instanceof MethodEntry ) + else if( obfEntry instanceof MethodEntry ) { - return translator.translateEntry( (MethodEntry)in ); + return translator.translateEntry( (MethodEntry)obfEntry ); } - else if( in instanceof ArgumentEntry ) + else if( obfEntry instanceof ArgumentEntry ) { - return translator.translateEntry( (ArgumentEntry)in ); + return translator.translateEntry( (ArgumentEntry)obfEntry ); } else { - throw new Error( "Unknown entry type: " + in.getClass().getName() ); + throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); } } - public Entry deobfuscate( Entry in ) + public boolean hasMapping( Entry obfEntry ) { Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ); - if( in instanceof ClassEntry ) + if( obfEntry instanceof ClassEntry ) { - return translator.translateEntry( (ClassEntry)in ); + String deobfName = translator.translate( (ClassEntry)obfEntry ); + return deobfName != null && !deobfName.equals( obfEntry.getName() ); } - else if( in instanceof FieldEntry ) + else if( obfEntry instanceof FieldEntry ) { - return translator.translateEntry( (FieldEntry)in ); + String deobfName = translator.translate( (FieldEntry)obfEntry ); + return deobfName != null && !deobfName.equals( obfEntry.getName() ); } - else if( in instanceof MethodEntry ) + else if( obfEntry instanceof MethodEntry ) { - return translator.translateEntry( (MethodEntry)in ); + String deobfName = translator.translate( (MethodEntry)obfEntry ); + return deobfName != null && !deobfName.equals( obfEntry.getName() ); } - else if( in instanceof ArgumentEntry ) + else if( obfEntry instanceof ArgumentEntry ) { - return translator.translateEntry( (ArgumentEntry)in ); + return translator.translate( (ArgumentEntry)obfEntry ) != null; } else { - throw new Error( "Unknown entry type: " + in.getClass().getName() ); + throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); } } } diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index ee92d1ed..61c833ce 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -52,7 +52,7 @@ public class SourceIndex implements Iterable> return m_tokenToEntry.get( token ); } - public Entry getEntry( int pos ) + public Map.Entry getEntry( int pos ) { // linear search is fast enough for now for( Map.Entry entry : this ) @@ -60,7 +60,7 @@ public class SourceIndex implements Iterable> Token token = entry.getValue(); if( pos >= token.start && pos <= token.end() ) { - return entry.getKey(); + return entry; } } return null; diff --git a/src/cuchaz/enigma/analysis/SourcedAst.java b/src/cuchaz/enigma/analysis/SourcedAst.java index 52a34534..968c8804 100644 --- a/src/cuchaz/enigma/analysis/SourcedAst.java +++ b/src/cuchaz/enigma/analysis/SourcedAst.java @@ -59,14 +59,17 @@ public class SourcedAst } // index the self class using the package name - String packageName = Descriptor.toJvmName( m_tree.getPackageName().toString() ); - for( Tree typeTree : m_tree.getTypeDecls() ) + if( m_tree.getPackageName() != null ) { - if( typeTree instanceof ClassTree ) + String packageName = Descriptor.toJvmName( m_tree.getPackageName().toString() ); + for( Tree typeTree : m_tree.getTypeDecls() ) { - ClassTree classTree = (ClassTree)typeTree; - String className = classTree.getSimpleName().toString(); - m_classNameIndex.put( className, packageName + "/" + className ); + if( typeTree instanceof ClassTree ) + { + ClassTree classTree = (ClassTree)typeTree; + String className = classTree.getSimpleName().toString(); + m_classNameIndex.put( className, packageName + "/" + className ); + } } } } diff --git a/src/cuchaz/enigma/gui/AboutDialog.java b/src/cuchaz/enigma/gui/AboutDialog.java index 79b75432..a245956e 100644 --- a/src/cuchaz/enigma/gui/AboutDialog.java +++ b/src/cuchaz/enigma/gui/AboutDialog.java @@ -18,8 +18,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; -import javax.swing.Box; -import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; diff --git a/src/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/cuchaz/enigma/gui/BoxHighlightPainter.java index 22db28b7..b9474ff8 100644 --- a/src/cuchaz/enigma/gui/BoxHighlightPainter.java +++ b/src/cuchaz/enigma/gui/BoxHighlightPainter.java @@ -19,10 +19,16 @@ import javax.swing.text.BadLocationException; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; -public class BoxHighlightPainter implements Highlighter.HighlightPainter +public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter { - private static final Color FillColor = new Color( 230, 230, 230 ); - private static final Color BorderColor = new Color( 100, 100, 100 ); + private Color m_fillColor; + private Color m_borderColor; + + protected BoxHighlightPainter( Color fillColor, Color borderColor ) + { + m_fillColor = fillColor; + m_borderColor = borderColor; + } @Override public void paint( Graphics g, int start, int end, Shape shape, JTextComponent text ) @@ -39,11 +45,11 @@ public class BoxHighlightPainter implements Highlighter.HighlightPainter bounds.height -= 2; // fill the area - g.setColor( FillColor ); + g.setColor( m_fillColor ); g.fillRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); // draw a box around the area - g.setColor( BorderColor ); + g.setColor( m_borderColor ); g.drawRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); } catch( BadLocationException ex ) diff --git a/src/cuchaz/enigma/gui/BrowserCaret.java b/src/cuchaz/enigma/gui/BrowserCaret.java new file mode 100644 index 00000000..f7e608bb --- /dev/null +++ b/src/cuchaz/enigma/gui/BrowserCaret.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Graphics; +import java.awt.Shape; + +import javax.swing.text.DefaultCaret; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; + +public class BrowserCaret extends DefaultCaret +{ + private static final long serialVersionUID = 1158977422507969940L; + + private static final Highlighter.HighlightPainter m_selectionPainter = new Highlighter.HighlightPainter( ) + { + @Override + public void paint( Graphics g, int p0, int p1, Shape bounds, JTextComponent c ) + { + // don't paint anything + } + }; + + @Override + public boolean isSelectionVisible( ) + { + return false; + } + + @Override + public boolean isVisible( ) + { + return true; + } + + @Override + public Highlighter.HighlightPainter getSelectionPainter( ) + { + return m_selectionPainter; + } +} diff --git a/src/cuchaz/enigma/gui/ClassListCellRenderer.java b/src/cuchaz/enigma/gui/ClassListCellRenderer.java deleted file mode 100644 index 302f140b..00000000 --- a/src/cuchaz/enigma/gui/ClassListCellRenderer.java +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.gui; - -import java.awt.Component; - -import javax.swing.DefaultListCellRenderer; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.ListCellRenderer; - -import cuchaz.enigma.ClassFile; - -public class ClassListCellRenderer implements ListCellRenderer -{ - private DefaultListCellRenderer m_defaultRenderer; - - public ClassListCellRenderer( ) - { - m_defaultRenderer = new DefaultListCellRenderer(); - } - - @Override - public Component getListCellRendererComponent( JList list, ClassFile classFile, int index, boolean isSelected, boolean hasFocus ) - { - JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, classFile, index, isSelected, hasFocus ); - - label.setText( classFile.getName() ); - - return label; - } -} diff --git a/src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java b/src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java new file mode 100644 index 00000000..3a8729d2 --- /dev/null +++ b/src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Component; +import java.util.Map; + +import javassist.bytecode.Descriptor; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +import cuchaz.enigma.ClassFile; + +public class DeobfuscatedClassListCellRenderer implements ListCellRenderer> +{ + private DefaultListCellRenderer m_defaultRenderer; + + public DeobfuscatedClassListCellRenderer( ) + { + m_defaultRenderer = new DefaultListCellRenderer(); + } + + @Override + public Component getListCellRendererComponent( JList> list, Map.Entry entry, int index, boolean isSelected, boolean hasFocus ) + { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, entry, index, isSelected, hasFocus ); + + label.setText( Descriptor.toJavaName( entry.getValue() ) ); + + return label; + } +} diff --git a/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java new file mode 100644 index 00000000..6a428842 --- /dev/null +++ b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; + +public class DeobfuscatedHighlightPainter extends BoxHighlightPainter +{ + public DeobfuscatedHighlightPainter( ) + { + // green ish + super( new Color( 220, 255, 220 ), new Color( 80, 160, 80 ) ); + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 3f46b6ec..87b93085 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -11,6 +11,7 @@ package cuchaz.enigma.gui; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; @@ -18,18 +19,21 @@ import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Vector; import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.JButton; import javax.swing.JEditorPane; import javax.swing.JFileChooser; import javax.swing.JFrame; @@ -38,7 +42,9 @@ import javax.swing.JList; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; +import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextField; @@ -47,17 +53,19 @@ import javax.swing.WindowConstants; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter; import jsyntaxpane.DefaultSyntaxKit; +import jsyntaxpane.SyntaxDocument; import jsyntaxpane.Token; import cuchaz.enigma.ClassFile; import cuchaz.enigma.Constants; -import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.IllegalNameException; import cuchaz.enigma.mapping.MethodEntry; public class Gui @@ -86,14 +94,11 @@ public class Gui // controls private JFrame m_frame; private JList m_obfClasses; - private JList m_deobfClasses; + private JList> m_deobfClasses; private JEditorPane m_editor; - private JPanel m_actionPanel; - private JPanel m_renamePanel; - private JLabel m_typeLabel; - private JTextField m_nameField; - private JButton m_renameButton; - private BoxHighlightPainter m_highlightPainter; + private JPanel m_infoPanel; + private BoxHighlightPainter m_obfuscatedHighlightPainter; + private BoxHighlightPainter m_deobfuscatedHighlightPainter; // dynamic menu items private JMenuItem m_closeJarMenu; @@ -101,6 +106,7 @@ public class Gui private JMenuItem m_saveMappingsMenu; private JMenuItem m_saveMappingsAsMenu; private JMenuItem m_closeMappingsMenu; + private JMenuItem m_renameMenu; // state private EntryPair m_selectedEntryPair; @@ -124,7 +130,7 @@ public class Gui m_obfClasses = new JList(); m_obfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); m_obfClasses.setLayoutOrientation( JList.VERTICAL ); - m_obfClasses.setCellRenderer( new ClassListCellRenderer() ); + m_obfClasses.setCellRenderer( new ObfuscatedClassListCellRenderer() ); m_obfClasses.addMouseListener( new MouseAdapter() { public void mouseClicked( MouseEvent event ) @@ -146,20 +152,20 @@ public class Gui obfPanel.add( obfScroller, BorderLayout.CENTER ); // init deobfuscated classes list - m_deobfClasses = new JList(); + m_deobfClasses = new JList>(); m_deobfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); m_deobfClasses.setLayoutOrientation( JList.VERTICAL ); - m_deobfClasses.setCellRenderer( new ClassListCellRenderer() ); + m_deobfClasses.setCellRenderer( new DeobfuscatedClassListCellRenderer() ); m_deobfClasses.addMouseListener( new MouseAdapter() { public void mouseClicked( MouseEvent event ) { if( event.getClickCount() == 2 ) { - ClassFile selected = m_deobfClasses.getSelectedValue(); + Map.Entry selected = m_deobfClasses.getSelectedValue(); if( selected != null ) { - m_controller.deobfuscateClass( selected ); + m_controller.deobfuscateClass( selected.getKey() ); } } } @@ -170,39 +176,20 @@ public class Gui deobfPanel.add( new JLabel( "De-obfuscated Classes" ), BorderLayout.NORTH ); deobfPanel.add( deobfScroller, BorderLayout.CENTER ); - // init action panel - m_actionPanel = new JPanel(); - m_actionPanel.setLayout( new BoxLayout( m_actionPanel, BoxLayout.Y_AXIS ) ); - m_actionPanel.setPreferredSize( new Dimension( 0, 120 ) ); - m_actionPanel.setBorder( BorderFactory.createTitledBorder( "Identifier Info" ) ); - m_nameField = new JTextField( 26 ); - m_renameButton = new JButton( "Rename" ); - m_renameButton.addActionListener( new ActionListener( ) - { - @Override - public void actionPerformed( ActionEvent event ) - { - if( m_selectedEntryPair != null ) - { - m_controller.rename( m_selectedEntryPair.obf, m_nameField.getText() ); - } - } - } ); - m_renamePanel = new JPanel(); - m_renamePanel.setLayout( new FlowLayout( FlowLayout.LEFT, 6, 0 ) ); - m_typeLabel = new JLabel( "LongName:", JLabel.RIGHT ); - // NOTE: this looks ridiculous, but it fixes the label size to the size of current text - m_typeLabel.setPreferredSize( m_typeLabel.getPreferredSize() ); - m_renamePanel.add( m_typeLabel ); - m_renamePanel.add( m_nameField ); - m_renamePanel.add( m_renameButton ); + // init info panel + m_infoPanel = new JPanel(); + m_infoPanel.setLayout( new GridLayout( 4, 1, 0, 0 ) ); + m_infoPanel.setPreferredSize( new Dimension( 0, 100 ) ); + m_infoPanel.setBorder( BorderFactory.createTitledBorder( "Identifier Info" ) ); clearEntryPair(); // init editor DefaultSyntaxKit.initKit(); - m_highlightPainter = new BoxHighlightPainter(); + m_obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); + m_deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); m_editor = new JEditorPane(); m_editor.setEditable( false ); + m_editor.setCaret( new BrowserCaret() ); JScrollPane sourceScroller = new JScrollPane( m_editor ); m_editor.setContentType( "text/java" ); m_editor.addCaretListener( new CaretListener( ) @@ -214,19 +201,55 @@ public class Gui if( m_selectedEntryPair != null ) { showEntryPair( m_selectedEntryPair ); + m_renameMenu.setEnabled( true ); } else { clearEntryPair(); + m_renameMenu.setEnabled( false ); } } } ); + m_editor.addKeyListener( new KeyAdapter( ) + { + @Override + public void keyPressed( KeyEvent event ) + { + switch( event.getKeyCode() ) + { + case KeyEvent.VK_R: + startRename(); + break; + } + } + } ); + + // turn off token highlighting (it's wrong most of the time anyway...) + DefaultSyntaxKit kit = (DefaultSyntaxKit)m_editor.getEditorKit(); + kit.toggleComponent( m_editor, "jsyntaxpane.components.TokenMarker" ); + + // init editor popup menu + JPopupMenu popupMenu = new JPopupMenu(); + m_editor.setComponentPopupMenu( popupMenu ); + { + JMenuItem menu = new JMenuItem( "Rename" ); + menu.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + startRename(); + } + } ); + popupMenu.add( menu ); + m_renameMenu = menu; + } // layout controls JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); JPanel rightPanel = new JPanel(); rightPanel.setLayout( new BorderLayout() ); - rightPanel.add( m_actionPanel, BorderLayout.NORTH ); + rightPanel.add( m_infoPanel, BorderLayout.NORTH ); rightPanel.add( sourceScroller, BorderLayout.CENTER ); JSplitPane splitMain = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, rightPanel ); pane.add( splitMain, BorderLayout.CENTER ); @@ -353,6 +376,19 @@ public class Gui } ); m_closeMappingsMenu = item; } + menu.addSeparator(); + { + JMenuItem item = new JMenuItem( "Exit" ); + menu.add( item ); + item.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + close(); + } + } ); + } } { JMenu menu = new JMenu( "Help" ); @@ -374,12 +410,21 @@ public class Gui // init state onCloseJar(); + m_frame.addWindowListener( new WindowAdapter( ) + { + @Override + public void windowClosing( WindowEvent event ) + { + close(); + } + } ); + // show the frame pane.doLayout(); m_frame.setSize( 800, 600 ); m_frame.setMinimumSize( new Dimension( 640, 480 ) ); m_frame.setVisible( true ); - m_frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE ); + m_frame.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE ); } public GuiController getController( ) @@ -430,15 +475,15 @@ public class Gui } } - public void setDeobfClasses( List classes ) + public void setDeobfClasses( Map deobfClasses ) { - if( classes != null ) + if( deobfClasses != null ) { - m_deobfClasses.setListData( new Vector( classes ) ); + m_deobfClasses.setListData( new Vector>( deobfClasses.entrySet() ) ); } else { - m_deobfClasses.setListData( new Vector() ); + m_deobfClasses.setListData( new Vector>() ); } } @@ -450,48 +495,77 @@ public class Gui public void setSource( String source ) { - setSource( source, null ); + setSource( source, 0 ); } - public void setSource( String source, SourceIndex index ) + public void setSource( String source, int lineNum ) { + // remove any old highlighters + m_editor.getHighlighter().removeAllHighlights(); + m_editor.setText( source ); - setHighlightedTokens( null ); + + // count the offset of the target line + String text = m_editor.getText(); + int pos = 0; + int numLines = 0; + for( ; pos < text.length(); pos++ ) + { + if( numLines == lineNum ) + { + break; + } + if( text.charAt( pos ) == '\n' ) + { + numLines++; + } + } + + // put the caret at the line number + m_editor.setCaretPosition( pos ); + m_editor.grabFocus(); } - public void setHighlightedTokens( Iterable tokens ) + public void setHighlightedTokens( Iterable obfuscatedTokens, Iterable deobfuscatedTokens ) { // remove any old highlighters m_editor.getHighlighter().removeAllHighlights(); - if( tokens == null ) + // color things based on the index + if( obfuscatedTokens != null ) { - return; + setHighlightedTokens( obfuscatedTokens, m_obfuscatedHighlightPainter ); + } + if( deobfuscatedTokens != null ) + { + setHighlightedTokens( deobfuscatedTokens, m_deobfuscatedHighlightPainter ); } - // color things based on the index + redraw(); + } + + private void setHighlightedTokens( Iterable tokens, Highlighter.HighlightPainter painter ) + { for( Token token : tokens ) { try { - m_editor.getHighlighter().addHighlight( token.start, token.end(), m_highlightPainter ); + m_editor.getHighlighter().addHighlight( token.start, token.end(), painter ); } catch( BadLocationException ex ) { throw new IllegalArgumentException( ex ); } } - - redraw(); } private void clearEntryPair( ) { - m_actionPanel.removeAll(); + m_infoPanel.removeAll(); JLabel label = new JLabel( "No identifier selected" ); unboldLabel( label ); label.setHorizontalAlignment( JLabel.CENTER ); - m_actionPanel.add( label ); + m_infoPanel.add( label ); redraw(); } @@ -507,30 +581,22 @@ public class Gui m_selectedEntryPair = pair; - // layout the action panel - m_actionPanel.removeAll(); - m_actionPanel.add( m_renamePanel ); - m_nameField.setText( pair.deobf.getName() ); - - // layout the dynamic section - JPanel dynamicPanel = new JPanel(); - dynamicPanel.setLayout( new GridLayout( 3, 1, 0, 0 ) ); - m_actionPanel.add( dynamicPanel ); + m_infoPanel.removeAll(); if( pair.deobf instanceof ClassEntry ) { - showClassEntryPair( (EntryPair)pair, dynamicPanel ); + showClassEntryPair( (EntryPair)pair ); } else if( pair.deobf instanceof FieldEntry ) { - showFieldEntryPair( (EntryPair)pair, dynamicPanel ); + showFieldEntryPair( (EntryPair)pair ); } else if( pair.deobf instanceof MethodEntry ) { - showMethodEntryPair( (EntryPair)pair, dynamicPanel ); + showMethodEntryPair( (EntryPair)pair ); } else if( pair.deobf instanceof ArgumentEntry ) { - showArgumentEntryPair( (EntryPair)pair, dynamicPanel ); + showArgumentEntryPair( (EntryPair)pair ); } else { @@ -540,30 +606,30 @@ public class Gui redraw(); } - private void showClassEntryPair( EntryPair pair, JPanel panel ) + private void showClassEntryPair( EntryPair pair ) { - m_typeLabel.setText( "Class: " ); + addNameValue( m_infoPanel, "Class", pair.deobf.getName() ); } - private void showFieldEntryPair( EntryPair pair, JPanel panel ) + private void showFieldEntryPair( EntryPair pair ) { - m_typeLabel.setText( "Field: " ); - addNameValue( panel, "Class", pair.obf.getClassEntry().getName() + " <-> " + pair.deobf.getClassEntry().getName() ); + addNameValue( m_infoPanel, "Field", pair.deobf.getName() ); + addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() ); } - private void showMethodEntryPair( EntryPair pair, JPanel panel ) + private void showMethodEntryPair( EntryPair pair ) { - m_typeLabel.setText( "Method: " ); - addNameValue( panel, "Class", pair.obf.getClassEntry().getName() + " <-> " + pair.deobf.getClassEntry().getName() ); - addNameValue( panel, "Signature", pair.obf.getSignature() + " <-> " + pair.deobf.getSignature() ); + addNameValue( m_infoPanel, "Method", pair.deobf.getName() ); + addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() ); + addNameValue( m_infoPanel, "Signature", pair.deobf.getSignature() ); } - private void showArgumentEntryPair( EntryPair pair, JPanel panel ) + private void showArgumentEntryPair( EntryPair pair ) { - m_typeLabel.setText( "Argument: " ); - addNameValue( panel, "Class", pair.obf.getClassEntry().getName() + " <-> " + pair.deobf.getClassEntry().getName() ); - addNameValue( panel, "Method", pair.obf.getMethodEntry().getName() + " <-> " + pair.deobf.getMethodEntry().getName() ); - addNameValue( panel, "Index", Integer.toString( pair.obf.getIndex() ) ); + addNameValue( m_infoPanel, "Argument", pair.deobf.getName() ); + addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() ); + addNameValue( m_infoPanel, "Method", pair.deobf.getMethodEntry().getName() ); + addNameValue( m_infoPanel, "Index", Integer.toString( pair.deobf.getIndex() ) ); } private void addNameValue( JPanel container, String name, String value ) @@ -578,6 +644,120 @@ public class Gui panel.add( unboldLabel( new JLabel( value, JLabel.LEFT ) ) ); } + + private void startRename( ) + { + // init the text box + final JTextField text = new JTextField(); + text.setText( m_selectedEntryPair.deobf.getName() ); + text.setPreferredSize( new Dimension( 360, text.getPreferredSize().height ) ); + text.addKeyListener( new KeyAdapter( ) + { + @Override + public void keyPressed( KeyEvent event ) + { + switch( event.getKeyCode() ) + { + case KeyEvent.VK_ENTER: + finishRename( text, true ); + break; + + case KeyEvent.VK_ESCAPE: + finishRename( text, false ); + break; + } + } + } ); + + // find the label with the name and replace it with the text box + JPanel panel = (JPanel)m_infoPanel.getComponent( 0 ); + panel.remove( panel.getComponentCount() - 1 ); + panel.add( text ); + text.grabFocus(); + text.selectAll(); + + redraw(); + } + + private void finishRename( JTextField text, boolean saveName ) + { + String newName = text.getText(); + if( saveName && newName != null && newName.length() > 0 ) + { + SyntaxDocument doc = (SyntaxDocument)m_editor.getDocument(); + int lineNum = doc.getLineNumberAt( m_editor.getCaretPosition() ); + try + { + m_controller.rename( m_selectedEntryPair.obf, newName, lineNum ); + } + catch( IllegalNameException ex ) + { + text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) ); + } + return; + } + + // abort the rename + JPanel panel = (JPanel)m_infoPanel.getComponent( 0 ); + panel.remove( panel.getComponentCount() - 1 ); + panel.add( unboldLabel( new JLabel( m_selectedEntryPair.deobf.getName(), JLabel.LEFT ) ) ); + + m_editor.grabFocus(); + + redraw(); + } + + private void close( ) + { + if( !m_controller.isDirty() ) + { + // everything is saved, we can exit safely + m_frame.dispose(); + } + else + { + // ask to save before closing + String[] options = { + "Save and exit", + "Discard changes", + "Cancel" + }; + int response = JOptionPane.showOptionDialog( + m_frame, + "Your mappings have not been saved yet. Do you want to save?", + "Save your changes?", + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[2] + ); + switch( response ) + { + case JOptionPane.YES_OPTION: // save and exit + if( m_mappingsFileChooser.getSelectedFile() != null || m_mappingsFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) + { + try + { + m_controller.saveMappings( m_mappingsFileChooser.getSelectedFile() ); + m_frame.dispose(); + } + catch( IOException ex ) + { + throw new Error( ex ); + } + } + break; + + case JOptionPane.NO_OPTION: + // don't save, exit + m_frame.dispose(); + break; + + // cancel means do nothing + } + } + } private JLabel unboldLabel( JLabel label ) { diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 6704ef8a..e1ba49a6 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -14,14 +14,18 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import java.util.ArrayList; import java.util.List; +import java.util.Map; + +import jsyntaxpane.Token; + +import com.beust.jcommander.internal.Lists; +import com.beust.jcommander.internal.Maps; import cuchaz.enigma.ClassFile; import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.Analyzer; import cuchaz.enigma.analysis.SourceIndex; -import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.MappingsReader; @@ -33,6 +37,7 @@ public class GuiController private Gui m_gui; private SourceIndex m_index; private ClassFile m_currentFile; + private boolean m_isDirty; public GuiController( Gui gui ) { @@ -40,6 +45,12 @@ public class GuiController m_deobfuscator = null; m_index = null; m_currentFile = null; + m_isDirty = false; + } + + public boolean isDirty( ) + { + return m_isDirty; } public void openJar( File file ) @@ -62,6 +73,7 @@ public class GuiController FileReader in = new FileReader( file ); m_deobfuscator.setMappings( new MappingsReader().read( in ) ); in.close(); + m_isDirty = false; m_gui.setMappingsFile( file ); refreshClasses(); refreshOpenFiles(); @@ -73,12 +85,14 @@ public class GuiController FileWriter out = new FileWriter( file ); new MappingsWriter().write( out, m_deobfuscator.getMappings() ); out.close(); + m_isDirty = false; } public void closeMappings( ) { m_deobfuscator.setMappings( null ); m_gui.setMappingsFile( null ); + refreshClasses(); refreshOpenFiles(); } @@ -95,51 +109,62 @@ public class GuiController return null; } - Entry deobfEntry = m_index.getEntry( pos ); - if( deobfEntry == null ) + Map.Entry deobfEntryAndToken = m_index.getEntry( pos ); + if( deobfEntryAndToken == null ) { return null; } - return new EntryPair( m_deobfuscator.obfuscate( deobfEntry ), deobfEntry ); + Entry deobfEntry = deobfEntryAndToken.getKey(); + Token token = deobfEntryAndToken.getValue(); + return new EntryPair( m_deobfuscator.obfuscateEntry( deobfEntry ), deobfEntry, token ); } - public void rename( Entry obfsEntry, String newName ) + public boolean entryHasMapping( int pos ) { - m_deobfuscator.rename( obfsEntry, newName ); - - // did we rename the current file? - if( obfsEntry instanceof ClassEntry ) + EntryPair pair = getEntryPair( pos ); + if( pair == null || pair.obf == null ) { - ClassEntry classEntry = (ClassEntry)obfsEntry; - - // update the current file - if( classEntry.getName().equals( m_currentFile.getName() ) ) - { - m_currentFile = new ClassFile( newName ); - } + return false; } - - refreshOpenFiles(); + return m_deobfuscator.hasMapping( pair.obf ); + } + + public void rename( Entry obfsEntry, String newName, int lineNum ) + { + m_deobfuscator.rename( obfsEntry, newName ); + m_isDirty = true; + refreshClasses(); + refreshOpenFiles( lineNum ); } private void refreshClasses( ) { - List obfClasses = new ArrayList(); - List deobfClasses = new ArrayList(); - m_deobfuscator.getSortedClasses( obfClasses, deobfClasses ); + List obfClasses = Lists.newArrayList(); + Map deobfClasses = Maps.newHashMap(); + m_deobfuscator.getSeparatedClasses( obfClasses, deobfClasses ); m_gui.setObfClasses( obfClasses ); m_gui.setDeobfClasses( deobfClasses ); } - + private void refreshOpenFiles( ) + { + refreshOpenFiles( 0 ); + } + + private void refreshOpenFiles( int lineNum ) { if( m_currentFile != null ) { - deobfuscate( m_currentFile ); + deobfuscate( m_currentFile, lineNum ); } } private void deobfuscate( final ClassFile classFile ) + { + deobfuscate( classFile, 0 ); + } + + private void deobfuscate( final ClassFile classFile, final int lineNum ) { m_gui.setSource( "(deobfuscating...)" ); @@ -149,13 +174,28 @@ public class GuiController @Override public void run( ) { - // deobfuscate the bytecode + // deobfuscate,decompile the bytecode String source = m_deobfuscator.getSource( classFile ); - m_gui.setSource( source ); + m_gui.setSource( source, lineNum ); // index the source file m_index = Analyzer.analyze( classFile.getName(), source ); - m_gui.setHighlightedTokens( m_index.tokens() ); + + // set the highlighted tokens + List obfuscatedTokens = Lists.newArrayList(); + List deobfuscatedTokens = Lists.newArrayList(); + for( Token token : m_index.tokens() ) + { + if( entryHasMapping( token.start ) ) + { + deobfuscatedTokens.add( token ); + } + else + { + obfuscatedTokens.add( token ); + } + } + m_gui.setHighlightedTokens( obfuscatedTokens, deobfuscatedTokens ); } }.start(); } diff --git a/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java b/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java new file mode 100644 index 00000000..d46e1ae6 --- /dev/null +++ b/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Component; + +import javassist.bytecode.Descriptor; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +import cuchaz.enigma.ClassFile; + +public class ObfuscatedClassListCellRenderer implements ListCellRenderer +{ + private DefaultListCellRenderer m_defaultRenderer; + + public ObfuscatedClassListCellRenderer( ) + { + m_defaultRenderer = new DefaultListCellRenderer(); + } + + @Override + public Component getListCellRendererComponent( JList list, ClassFile classFile, int index, boolean isSelected, boolean hasFocus ) + { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, classFile, index, isSelected, hasFocus ); + + label.setText( Descriptor.toJavaName( classFile.getName() ) ); + + return label; + } +} diff --git a/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java new file mode 100644 index 00000000..724be34e --- /dev/null +++ b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; + +public class ObfuscatedHighlightPainter extends BoxHighlightPainter +{ + public ObfuscatedHighlightPainter( ) + { + // red ish + super( new Color( 255, 220, 220 ), new Color( 160, 80, 80 ) ); + } +} diff --git a/src/cuchaz/enigma/mapping/ArgumentMapping.java b/src/cuchaz/enigma/mapping/ArgumentMapping.java index d5e020a6..168306a2 100644 --- a/src/cuchaz/enigma/mapping/ArgumentMapping.java +++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java @@ -12,7 +12,7 @@ package cuchaz.enigma.mapping; import java.io.Serializable; -public class ArgumentMapping implements Serializable +public class ArgumentMapping implements Serializable, Comparable { private static final long serialVersionUID = 8610742471440861315L; @@ -23,7 +23,7 @@ public class ArgumentMapping implements Serializable public ArgumentMapping( int index, String name ) { m_index = index; - m_name = name; + m_name = NameValidator.validateArgumentName( name ); } public int getIndex( ) @@ -37,6 +37,12 @@ public class ArgumentMapping implements Serializable } public void setName( String val ) { - m_name = val; + m_name = NameValidator.validateArgumentName( val ); + } + + @Override + public int compareTo( ArgumentMapping other ) + { + return Integer.compare( m_index, other.m_index ); } } diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 3ba3569f..a1cc775f 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -15,7 +15,7 @@ import java.util.Map; import com.beust.jcommander.internal.Maps; -public class ClassMapping implements Serializable +public class ClassMapping implements Serializable, Comparable { private static final long serialVersionUID = -5148491146902340107L; @@ -30,7 +30,7 @@ public class ClassMapping implements Serializable public ClassMapping( String obfName, String deobfName ) { m_obfName = obfName; - m_deobfName = deobfName; + m_deobfName = NameValidator.validateClassName( deobfName ); m_fieldsByObf = Maps.newHashMap(); m_fieldsByDeobf = Maps.newHashMap(); m_methodsByObf = Maps.newHashMap(); @@ -48,7 +48,7 @@ public class ClassMapping implements Serializable } public void setDeobfName( String val ) { - m_deobfName = val; + m_deobfName = NameValidator.validateClassName( val ); } public Iterable fields( ) @@ -97,11 +97,6 @@ public class ClassMapping implements Serializable public void setFieldName( String obfName, String deobfName ) { - if( deobfName == null ) - { - throw new IllegalArgumentException( "deobf name cannot be null!" ); - } - FieldMapping fieldMapping = m_fieldsByObf.get( obfName ); if( fieldMapping == null ) { @@ -140,11 +135,6 @@ public class ClassMapping implements Serializable public void setMethodNameAndSignature( String obfName, String obfSignature, String deobfName, String deobfSignature ) { - if( deobfName == null ) - { - throw new IllegalArgumentException( "deobf name cannot be null!" ); - } - MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) ); if( methodIndex == null ) { @@ -167,11 +157,6 @@ public class ClassMapping implements Serializable public void setArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName ) { - if( argumentName == null ) - { - throw new IllegalArgumentException( "argument name cannot be null!" ); - } - MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ); if( methodIndex == null ) { @@ -214,4 +199,10 @@ public class ClassMapping implements Serializable } return buf.toString(); } + + @Override + public int compareTo( ClassMapping other ) + { + return m_obfName.compareTo( other.m_obfName ); + } } diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java index e3325b37..1bf9be09 100644 --- a/src/cuchaz/enigma/mapping/EntryPair.java +++ b/src/cuchaz/enigma/mapping/EntryPair.java @@ -10,15 +10,19 @@ ******************************************************************************/ package cuchaz.enigma.mapping; +import jsyntaxpane.Token; + public class EntryPair { public T obf; public T deobf; + public Token token; - public EntryPair( T obf, T deobf ) + public EntryPair( T obf, T deobf, Token token ) { this.obf = obf; this.deobf = deobf; + this.token = token; } } diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java index 618f45c6..ae0855a8 100644 --- a/src/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -12,7 +12,7 @@ package cuchaz.enigma.mapping; import java.io.Serializable; -public class FieldMapping implements Serializable +public class FieldMapping implements Serializable, Comparable { private static final long serialVersionUID = 8610742471440861315L; @@ -22,7 +22,7 @@ public class FieldMapping implements Serializable public FieldMapping( String obfName, String deobfName ) { m_obfName = obfName; - m_deobfName = deobfName; + m_deobfName = NameValidator.validateFieldName( deobfName ); } public String getObfName( ) @@ -36,6 +36,12 @@ public class FieldMapping implements Serializable } public void setDeobfName( String val ) { - m_deobfName = val; + m_deobfName = NameValidator.validateFieldName( val ); + } + + @Override + public int compareTo( FieldMapping other ) + { + return m_obfName.compareTo( other.m_obfName ); } } diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java new file mode 100644 index 00000000..560e5d93 --- /dev/null +++ b/src/cuchaz/enigma/mapping/IllegalNameException.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public class IllegalNameException extends RuntimeException +{ + private static final long serialVersionUID = -2279910052561114323L; + + private String m_name; + + public IllegalNameException( String name ) + { + m_name = name; + } + + @Override + public String getMessage( ) + { + return "Illegal name: " + m_name; + } +} diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java index 20863687..a97052fa 100644 --- a/src/cuchaz/enigma/mapping/MappingsWriter.java +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -13,6 +13,9 @@ package cuchaz.enigma.mapping; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; public class MappingsWriter { @@ -25,7 +28,7 @@ public class MappingsWriter public void write( PrintWriter out, Mappings mappings ) throws IOException { - for( ClassMapping classMapping : mappings.classes() ) + for( ClassMapping classMapping : sorted( mappings.classes() ) ) { write( out, classMapping ); } @@ -36,12 +39,12 @@ public class MappingsWriter { out.format( "CLASS %s %s\n", classMapping.getObfName(), classMapping.getDeobfName() ); - for( FieldMapping fieldMapping : classMapping.fields() ) + for( FieldMapping fieldMapping : sorted( classMapping.fields() ) ) { write( out, fieldMapping ); } - for( MethodMapping methodMapping : classMapping.methods() ) + for( MethodMapping methodMapping : sorted( classMapping.methods() ) ) { write( out, methodMapping ); } @@ -61,7 +64,7 @@ public class MappingsWriter methodMapping.getObfSignature(), methodMapping.getDeobfSignature() ); - for( ArgumentMapping argumentMapping : methodMapping.arguments() ) + for( ArgumentMapping argumentMapping : sorted( methodMapping.arguments() ) ) { write( out, argumentMapping ); } @@ -72,4 +75,15 @@ public class MappingsWriter { out.format( "\t\tARG %d %s\n", argumentMapping.getIndex(), argumentMapping.getName() ); } + + private > List sorted( Iterable classes ) + { + List out = new ArrayList(); + for( T t : classes ) + { + out.add( t ); + } + Collections.sort( out ); + return out; + } } diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index 1cdc38a1..7857ea7e 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -14,7 +14,7 @@ import java.io.Serializable; import java.util.Map; import java.util.TreeMap; -public class MethodMapping implements Serializable +public class MethodMapping implements Serializable, Comparable { private static final long serialVersionUID = -4409570216084263978L; @@ -28,7 +28,7 @@ public class MethodMapping implements Serializable public MethodMapping( String obfName, String deobfName, String obfSignature, String deobfSignature ) { m_obfName = obfName; - m_deobfName = deobfName; + m_deobfName = NameValidator.validateMethodName( deobfName ); m_obfSignature = obfSignature; m_deobfSignature = deobfSignature; m_arguments = new TreeMap(); @@ -45,7 +45,7 @@ public class MethodMapping implements Serializable } public void setDeobfName( String val ) { - m_deobfName = val; + m_deobfName = NameValidator.validateMethodName( val ); } public String getObfSignature( ) @@ -133,4 +133,10 @@ public class MethodMapping implements Serializable } return buf.toString(); } + + @Override + public int compareTo( MethodMapping other ) + { + return m_obfName.compareTo( other.m_obfName ); + } } diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java index 30982dc6..a8421fa5 100644 --- a/src/cuchaz/enigma/mapping/NameValidator.java +++ b/src/cuchaz/enigma/mapping/NameValidator.java @@ -10,12 +10,28 @@ ******************************************************************************/ package cuchaz.enigma.mapping; +import java.util.Arrays; +import java.util.List; import java.util.regex.Pattern; +import javassist.bytecode.Descriptor; + public class NameValidator { - private static final String IdentifierPattern; + private static final Pattern IdentifierPattern; private static final Pattern ClassPattern; + private static final List ReservedWords = Arrays.asList( + "abstract", "continue", "for", "new", "switch", + "assert", "default", "goto", "package", "synchronized", + "boolean", "do", "if", "private", "this", + "break", "double", "implements", "protected", "throw", + "byte", "else", "import", "public", "throws", + "case", "enum", "instanceof", "return", "transient", + "catch", "extends", "int", "short", "try", + "char", "final", "interface", "static", "void", + "class", "finally", "long", "strictfp", "volatile", + "const", "float", "native", "super", "while" + ); static { @@ -34,34 +50,36 @@ public class NameValidator } } - IdentifierPattern = String.format( "[\\Q%s\\E][\\Q%s\\E]*", startChars.toString(), partChars.toString() ); - ClassPattern = Pattern.compile( String.format( "^(%s(\\.|/))*(%s)$", IdentifierPattern, IdentifierPattern ) ); + String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; + IdentifierPattern = Pattern.compile( identifierRegex ); + ClassPattern = Pattern.compile( String.format( "^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex ) ); } - public String validateClassName( String name ) + public static String validateClassName( String name ) { - if( !ClassPattern.matcher( name ).matches() ) + if( name == null || !ClassPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) { - throw new IllegalArgumentException( "Illegal name: " + name ); + throw new IllegalNameException( name ); } - - return classNameToJavaName( name ); + return Descriptor.toJvmName( name ); } - public static String fileNameToClassName( String fileName ) + public static String validateFieldName( String name ) { - final String suffix = ".class"; - - if( !fileName.endsWith( suffix ) ) + if( name == null || !IdentifierPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) { - return null; + throw new IllegalNameException( name ); } - - return fileName.substring( 0, fileName.length() - suffix.length() ).replace( "/", "." ); + return name; + } + + public static String validateMethodName( String name ) + { + return validateFieldName( name ); } - public static String classNameToJavaName( String className ) + public static String validateArgumentName( String name ) { - return className.replace( ".", "/" ); + return validateFieldName( name ); } } diff --git a/src/cuchaz/enigma/mapping/Renamer.java b/src/cuchaz/enigma/mapping/Renamer.java index 4a648ad3..42ac17de 100644 --- a/src/cuchaz/enigma/mapping/Renamer.java +++ b/src/cuchaz/enigma/mapping/Renamer.java @@ -39,10 +39,6 @@ public class Renamer m_mappings.m_classesByDeobf.put( deobfName, classMapping ); updateDeobfMethodSignatures(); - - // TEMP - String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf ); - assert( translatedName != null && translatedName.equals( deobfName ) ); } public void setFieldName( FieldEntry obf, String deobfName ) @@ -54,11 +50,6 @@ public class Renamer } classMapping.setFieldName( obf.getName(), deobfName ); - - // TEMP - System.out.println( classMapping ); - String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf ); - assert( translatedName != null && translatedName.equals( deobfName ) ); } public void setMethodName( MethodEntry obf, String deobfName ) @@ -73,11 +64,6 @@ public class Renamer classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); // TODO: update ancestor/descendant methods in other classes in the inheritance hierarchy too - - // TEMP - System.out.println( classMapping ); - String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf ); - assert( translatedName != null && translatedName.equals( deobfName ) ); } public void setArgumentName( ArgumentEntry obf, String deobfName ) @@ -89,11 +75,6 @@ public class Renamer } classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName ); - - // TEMP - System.out.println( classMapping ); - String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf ); - assert( translatedName != null && translatedName.equals( deobfName ) ); } public void write( OutputStream out ) -- cgit v1.2.3 From 699544e76e63e044d290a75f802d082aa0834ad7 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 4 Aug 2014 23:24:48 -0400 Subject: fixed bug with renaming classes --- src/cuchaz/enigma/gui/Gui.java | 1 + src/cuchaz/enigma/mapping/Renamer.java | 4 ++++ src/cuchaz/enigma/mapping/Translator.java | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 87b93085..1f967c0c 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -451,6 +451,7 @@ public class Gui // update gui m_frame.setTitle( Constants.Name ); setObfClasses( null ); + setDeobfClasses( null ); setSource( null ); // update menu diff --git a/src/cuchaz/enigma/mapping/Renamer.java b/src/cuchaz/enigma/mapping/Renamer.java index 42ac17de..b7aa35ca 100644 --- a/src/cuchaz/enigma/mapping/Renamer.java +++ b/src/cuchaz/enigma/mapping/Renamer.java @@ -28,6 +28,7 @@ public class Renamer public void setClassName( ClassEntry obf, String deobfName ) { + deobfName = NameValidator.validateClassName( deobfName ); ClassMapping classMapping = m_mappings.m_classesByObf.get( obf.getName() ); if( classMapping == null ) { @@ -43,6 +44,7 @@ public class Renamer public void setFieldName( FieldEntry obf, String deobfName ) { + deobfName = NameValidator.validateFieldName( deobfName ); ClassMapping classMapping = m_mappings.m_classesByObf.get( obf.getClassName() ); if( classMapping == null ) { @@ -54,6 +56,7 @@ public class Renamer public void setMethodName( MethodEntry obf, String deobfName ) { + deobfName = NameValidator.validateMethodName( deobfName ); ClassMapping classMapping = m_mappings.m_classesByObf.get( obf.getClassName() ); if( classMapping == null ) { @@ -68,6 +71,7 @@ public class Renamer public void setArgumentName( ArgumentEntry obf, String deobfName ) { + deobfName = NameValidator.validateArgumentName( deobfName ); ClassMapping classMapping = m_mappings.m_classesByObf.get( obf.getClassName() ); if( classMapping == null ) { diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 3dbc103b..4ab53bc5 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -19,7 +19,7 @@ import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class Translator { private TranslationDirection m_direction; - private Map m_classes; + /* TEMP */ public Map m_classes; private Ancestries m_ancestries; protected Translator( TranslationDirection direction, Map classes, Ancestries ancestries ) -- cgit v1.2.3 From 17ec7a6fdf2dc76b9d89b6ef3168bacb1cb3fbd1 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 4 Aug 2014 23:57:08 -0400 Subject: started on inheritance viewer --- src/cuchaz/enigma/gui/Gui.java | 85 +++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 1f967c0c..f9afb64b 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -47,6 +47,7 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.WindowConstants; @@ -99,6 +100,7 @@ public class Gui private JPanel m_infoPanel; private BoxHighlightPainter m_obfuscatedHighlightPainter; private BoxHighlightPainter m_deobfuscatedHighlightPainter; + private JPanel m_inheritancePanel; // dynamic menu items private JMenuItem m_closeJarMenu; @@ -107,6 +109,7 @@ public class Gui private JMenuItem m_saveMappingsAsMenu; private JMenuItem m_closeMappingsMenu; private JMenuItem m_renameMenu; + private JMenuItem m_inheritanceMenu; // state private EntryPair m_selectedEntryPair; @@ -197,17 +200,7 @@ public class Gui @Override public void caretUpdate( CaretEvent event ) { - m_selectedEntryPair = m_controller.getEntryPair( event.getDot() ); - if( m_selectedEntryPair != null ) - { - showEntryPair( m_selectedEntryPair ); - m_renameMenu.setEnabled( true ); - } - else - { - clearEntryPair(); - m_renameMenu.setEnabled( false ); - } + onCaretMove( event.getDot() ); } } ); m_editor.addKeyListener( new KeyAdapter( ) @@ -244,15 +237,39 @@ public class Gui popupMenu.add( menu ); m_renameMenu = menu; } + { + JMenuItem menu = new JMenuItem( "Show Inheritance" ); + menu.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + showInheritance(); + } + } ); + popupMenu.add( menu ); + m_inheritanceMenu = menu; + } + + // init inheritance panel + m_inheritancePanel = new JPanel(); // layout controls JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); - JPanel rightPanel = new JPanel(); - rightPanel.setLayout( new BorderLayout() ); - rightPanel.add( m_infoPanel, BorderLayout.NORTH ); - rightPanel.add( sourceScroller, BorderLayout.CENTER ); - JSplitPane splitMain = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, rightPanel ); - pane.add( splitMain, BorderLayout.CENTER ); + splitLeft.setPreferredSize( new Dimension( 200, 0 ) ); + JPanel centerPanel = new JPanel(); + centerPanel.setLayout( new BorderLayout() ); + centerPanel.add( m_infoPanel, BorderLayout.NORTH ); + centerPanel.add( sourceScroller, BorderLayout.CENTER ); + JTabbedPane tabbedPane = new JTabbedPane(); + tabbedPane.setPreferredSize( new Dimension( 200, 0 ) ); + tabbedPane.addTab( "Inheritance", m_inheritancePanel ); + JSplitPane splitRight = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, tabbedPane ); + splitRight.setResizeWeight( 1 ); // let the left side take all the slack + splitRight.resetToPreferredSizes(); + JSplitPane splitCenter = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight ); + splitCenter.setResizeWeight( 0 ); // let the right side take all the slack + pane.add( splitCenter, BorderLayout.CENTER ); // init menus JMenuBar menuBar = new JMenuBar(); @@ -421,7 +438,7 @@ public class Gui // show the frame pane.doLayout(); - m_frame.setSize( 800, 600 ); + m_frame.setSize( 1024, 576 ); m_frame.setMinimumSize( new Dimension( 640, 480 ) ); m_frame.setVisible( true ); m_frame.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE ); @@ -646,6 +663,27 @@ public class Gui panel.add( unboldLabel( new JLabel( value, JLabel.LEFT ) ) ); } + private void onCaretMove( int pos ) + { + m_selectedEntryPair = m_controller.getEntryPair( pos ); + if( m_selectedEntryPair != null ) + { + showEntryPair( m_selectedEntryPair ); + + boolean isClassEntry = m_selectedEntryPair.obf instanceof ClassEntry; + boolean isMethodEntry = m_selectedEntryPair.obf instanceof MethodEntry; + + m_renameMenu.setEnabled( true ); + m_inheritanceMenu.setEnabled( isClassEntry || isMethodEntry ); + } + else + { + clearEntryPair(); + m_renameMenu.setEnabled( false ); + m_inheritanceMenu.setEnabled( false ); + } + } + private void startRename( ) { // init the text box @@ -708,6 +746,17 @@ public class Gui redraw(); } + private void showInheritance( ) + { + m_inheritancePanel.removeAll(); + + // TEMP + m_inheritancePanel.add( new JLabel( m_selectedEntryPair.obf.getName() ) ); + m_inheritancePanel.add( new JLabel( m_selectedEntryPair.deobf.getName() ) ); + + redraw(); + } + private void close( ) { if( !m_controller.isDirty() ) -- cgit v1.2.3 From d71c0af8ed298bfb4b35b4e3e61b5678bc1c7d9f Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 6 Aug 2014 00:22:25 -0400 Subject: added simple class inheritance browsing --- src/cuchaz/enigma/Deobfuscator.java | 5 ++ src/cuchaz/enigma/TranslatingTypeLoader.java | 16 ++++-- .../enigma/gui/ClassInheritanceTreeNode.java | 56 ++++++++++++++++++++ src/cuchaz/enigma/gui/Gui.java | 61 ++++++++++++++++++---- src/cuchaz/enigma/gui/GuiController.java | 22 ++++++++ src/cuchaz/enigma/mapping/Ancestries.java | 22 +++++++- 6 files changed, 169 insertions(+), 13 deletions(-) create mode 100644 src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index a3937b4c..65e618a4 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -79,6 +79,11 @@ public class Deobfuscator return m_file.getName(); } + public Ancestries getAncestries( ) + { + return m_ancestries; + } + public Mappings getMappings( ) { return m_mappings; diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index e57a09de..44fe9805 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -10,6 +10,7 @@ ******************************************************************************/ package cuchaz.enigma; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.jar.JarEntry; @@ -59,10 +60,19 @@ public class TranslatingTypeLoader implements ITypeLoader try { // read the class file into a buffer - byte[] buf = new byte[(int)entry.getSize()]; + ByteArrayOutputStream data = new ByteArrayOutputStream(); + byte[] buf = new byte[1024*1024]; // 1 KiB InputStream in = m_jar.getInputStream( entry ); - int bytesRead = in.read( buf ); - assert( bytesRead == buf.length ); + while( true ) + { + int bytesRead = in.read( buf ); + if( bytesRead <= 0 ) + { + break; + } + data.write( buf, 0, bytesRead ); + } + buf = data.toByteArray(); // translate the class ClassPool classPool = new ClassPool(); diff --git a/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java new file mode 100644 index 00000000..921a1e98 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java @@ -0,0 +1,56 @@ +package cuchaz.enigma.gui; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.beust.jcommander.internal.Lists; + +import cuchaz.enigma.mapping.Ancestries; + +public class ClassInheritanceTreeNode extends DefaultMutableTreeNode +{ + private static final long serialVersionUID = 4432367405826178490L; + + String m_className; + + public ClassInheritanceTreeNode( String className ) + { + m_className = className; + } + + public String getClassName( ) + { + return m_className; + } + + @Override + public String toString( ) + { + return m_className; + } + + public void load( Ancestries ancestries, boolean recurse ) + { + // get all the child nodes + List nodes = Lists.newArrayList(); + for( String subclassName : ancestries.getSubclasses( m_className ) ) + { + nodes.add( new ClassInheritanceTreeNode( subclassName ) ); + } + + // add then to this node + for( ClassInheritanceTreeNode node : nodes ) + { + this.add( node ); + } + + if( recurse ) + { + for( ClassInheritanceTreeNode node : nodes ) + { + node.load( ancestries, true ); + } + } + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index f9afb64b..2002a4d2 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -49,12 +49,18 @@ import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JTextField; +import javax.swing.JTree; import javax.swing.ListSelectionModel; import javax.swing.WindowConstants; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.text.BadLocationException; import javax.swing.text.Highlighter; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import com.beust.jcommander.internal.Lists; import jsyntaxpane.DefaultSyntaxKit; import jsyntaxpane.SyntaxDocument; @@ -100,7 +106,7 @@ public class Gui private JPanel m_infoPanel; private BoxHighlightPainter m_obfuscatedHighlightPainter; private BoxHighlightPainter m_deobfuscatedHighlightPainter; - private JPanel m_inheritancePanel; + private JTree m_inheritanceTree; // dynamic menu items private JMenuItem m_closeJarMenu; @@ -136,6 +142,7 @@ public class Gui m_obfClasses.setCellRenderer( new ObfuscatedClassListCellRenderer() ); m_obfClasses.addMouseListener( new MouseAdapter() { + @Override public void mouseClicked( MouseEvent event ) { if( event.getClickCount() == 2 ) @@ -161,6 +168,7 @@ public class Gui m_deobfClasses.setCellRenderer( new DeobfuscatedClassListCellRenderer() ); m_deobfClasses.addMouseListener( new MouseAdapter() { + @Override public void mouseClicked( MouseEvent event ) { if( event.getClickCount() == 2 ) @@ -252,7 +260,26 @@ public class Gui } // init inheritance panel - m_inheritancePanel = new JPanel(); + m_inheritanceTree = new JTree(); + m_inheritanceTree.setModel( null ); + m_inheritanceTree.addMouseListener( new MouseAdapter( ) + { + @Override + public void mouseClicked( MouseEvent event ) + { + if( event.getClickCount() == 2 ) + { + ClassInheritanceTreeNode node = (ClassInheritanceTreeNode)m_inheritanceTree.getSelectionPath().getLastPathComponent(); + if( node != null ) + { + m_controller.deobfuscateClass( new ClassFile( node.getClassName() ) ); + } + } + } + } ); + JPanel inheritancePanel = new JPanel(); + inheritancePanel.setLayout( new BorderLayout() ); + inheritancePanel.add( new JScrollPane( m_inheritanceTree ) ); // layout controls JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); @@ -263,7 +290,7 @@ public class Gui centerPanel.add( sourceScroller, BorderLayout.CENTER ); JTabbedPane tabbedPane = new JTabbedPane(); tabbedPane.setPreferredSize( new Dimension( 200, 0 ) ); - tabbedPane.addTab( "Inheritance", m_inheritancePanel ); + tabbedPane.addTab( "Inheritance", inheritancePanel ); JSplitPane splitRight = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, tabbedPane ); splitRight.setResizeWeight( 1 ); // let the left side take all the slack splitRight.resetToPreferredSizes(); @@ -748,12 +775,28 @@ public class Gui private void showInheritance( ) { - m_inheritancePanel.removeAll(); - - // TEMP - m_inheritancePanel.add( new JLabel( m_selectedEntryPair.obf.getName() ) ); - m_inheritancePanel.add( new JLabel( m_selectedEntryPair.deobf.getName() ) ); - + // get the current class + if( m_selectedEntryPair.obf instanceof ClassEntry ) + { + ClassInheritanceTreeNode classNode = m_controller.getClassInheritance( (ClassEntry)m_selectedEntryPair.obf ); + + // build the path from the root to the class node + List nodes = Lists.newArrayList(); + TreeNode node = classNode; + do + { + nodes.add( node ); + node = node.getParent(); + } + while( node != null ); + Collections.reverse( nodes ); + TreePath path = new TreePath( nodes.toArray() ); + + // show the tree at the root + m_inheritanceTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) ); + m_inheritanceTree.expandPath( path ); + m_inheritanceTree.setSelectionRow( m_inheritanceTree.getRowForPath( path ) ); + } redraw(); } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index e1ba49a6..452632f3 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -26,6 +26,7 @@ import cuchaz.enigma.ClassFile; import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.Analyzer; import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.MappingsReader; @@ -129,6 +130,27 @@ public class GuiController return m_deobfuscator.hasMapping( pair.obf ); } + public ClassInheritanceTreeNode getClassInheritance( ClassEntry classEntry ) + { + // create a node for this class + ClassInheritanceTreeNode thisNode = new ClassInheritanceTreeNode( classEntry.getName() ); + + // expand all children recursively + thisNode.load( m_deobfuscator.getAncestries(), true ); + + // get the ancestors too + ClassInheritanceTreeNode node = thisNode; + for( String superclassName : m_deobfuscator.getAncestries().getAncestry( classEntry.getName() ) ) + { + // add the parent node + ClassInheritanceTreeNode parentNode = new ClassInheritanceTreeNode( superclassName ); + parentNode.add( node ); + node = parentNode; + } + + return thisNode; + } + public void rename( Entry obfsEntry, String newName, int lineNum ) { m_deobfuscator.rename( obfsEntry, newName ); diff --git a/src/cuchaz/enigma/mapping/Ancestries.java b/src/cuchaz/enigma/mapping/Ancestries.java index b7a5e249..f77a00eb 100644 --- a/src/cuchaz/enigma/mapping/Ancestries.java +++ b/src/cuchaz/enigma/mapping/Ancestries.java @@ -26,6 +26,7 @@ import javassist.CtClass; import javassist.NotFoundException; import javassist.bytecode.Descriptor; +import com.beust.jcommander.internal.Lists; import com.google.common.collect.Maps; import cuchaz.enigma.Constants; @@ -121,11 +122,30 @@ public class Ancestries implements Serializable while( className != null ) { className = getSuperclassName( className ); - ancestors.add( className ); + if( className != null ) + { + ancestors.add( className ); + } } return ancestors; } + public List getSubclasses( String className ) + { + // linear search is fast enough for now + List subclasses = Lists.newArrayList(); + for( Map.Entry entry : m_superclasses.entrySet() ) + { + String subclass = entry.getKey(); + String superclass = entry.getValue(); + if( className.equals( superclass ) ) + { + subclasses.add( subclass ); + } + } + return subclasses; + } + private boolean isJre( String className ) { return className.startsWith( "java/" ) -- cgit v1.2.3 From 0c836a74972173d4ab991f0d6d79195b4377fe4c Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 6 Aug 2014 00:22:51 -0400 Subject: added copyright --- src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java index 921a1e98..91000ed3 100644 --- a/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java +++ b/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.gui; import java.util.List; -- cgit v1.2.3 From 75d462daee9c24e19e28b4e969f92ae83a026e7b Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 6 Aug 2014 22:18:12 -0400 Subject: show deobfuscated names in class inheritance --- src/cuchaz/enigma/Deobfuscator.java | 15 +++++++---- .../enigma/gui/ClassInheritanceTreeNode.java | 29 ++++++++++++++++------ src/cuchaz/enigma/gui/Gui.java | 2 +- src/cuchaz/enigma/gui/GuiController.java | 8 ++++-- 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 65e618a4..2fceef1c 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -100,11 +100,16 @@ public class Deobfuscator // update decompiler options m_settings.setTypeLoader( new TranslatingTypeLoader( m_jar, - m_mappings.getTranslator( m_ancestries, TranslationDirection.Obfuscating ), - m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ) + getTranslator( TranslationDirection.Obfuscating ), + getTranslator( TranslationDirection.Deobfuscating ) ) ); } + public Translator getTranslator( TranslationDirection direction ) + { + return m_mappings.getTranslator( m_ancestries, direction ); + } + public void getSeparatedClasses( List obfClasses, Map deobfClasses ) { Enumeration entries = m_jar.entries(); @@ -181,7 +186,7 @@ public class Deobfuscator public Entry obfuscateEntry( Entry deobfEntry ) { - Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Obfuscating ); + Translator translator = getTranslator( TranslationDirection.Obfuscating ); if( deobfEntry instanceof ClassEntry ) { return translator.translateEntry( (ClassEntry)deobfEntry ); @@ -206,7 +211,7 @@ public class Deobfuscator public Entry deobfuscateEntry( Entry obfEntry ) { - Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ); + Translator translator = getTranslator( TranslationDirection.Deobfuscating ); if( obfEntry instanceof ClassEntry ) { return translator.translateEntry( (ClassEntry)obfEntry ); @@ -231,7 +236,7 @@ public class Deobfuscator public boolean hasMapping( Entry obfEntry ) { - Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ); + Translator translator = getTranslator( TranslationDirection.Deobfuscating ); if( obfEntry instanceof ClassEntry ) { String deobfName = translator.translate( (ClassEntry)obfEntry ); diff --git a/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java index 91000ed3..d8e67554 100644 --- a/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java +++ b/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java @@ -17,36 +17,49 @@ import javax.swing.tree.DefaultMutableTreeNode; import com.beust.jcommander.internal.Lists; import cuchaz.enigma.mapping.Ancestries; +import cuchaz.enigma.mapping.Translator; public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { private static final long serialVersionUID = 4432367405826178490L; - String m_className; + private Translator m_deobfuscatingTranslator; + private String m_obfClassName; - public ClassInheritanceTreeNode( String className ) + public ClassInheritanceTreeNode( Translator deobfuscatingTranslator, String obfClassName ) { - m_className = className; + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_obfClassName = obfClassName; } - public String getClassName( ) + public String getObfClassName( ) { - return m_className; + return m_obfClassName; + } + + public String getDeobfClassName( ) + { + return m_deobfuscatingTranslator.translateClass( m_obfClassName ); } @Override public String toString( ) { - return m_className; + String deobfClassName = getDeobfClassName(); + if( deobfClassName != null ) + { + return deobfClassName; + } + return m_obfClassName; } public void load( Ancestries ancestries, boolean recurse ) { // get all the child nodes List nodes = Lists.newArrayList(); - for( String subclassName : ancestries.getSubclasses( m_className ) ) + for( String subclassName : ancestries.getSubclasses( m_obfClassName ) ) { - nodes.add( new ClassInheritanceTreeNode( subclassName ) ); + nodes.add( new ClassInheritanceTreeNode( m_deobfuscatingTranslator, subclassName ) ); } // add then to this node diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 2002a4d2..e2ccb7d2 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -272,7 +272,7 @@ public class Gui ClassInheritanceTreeNode node = (ClassInheritanceTreeNode)m_inheritanceTree.getSelectionPath().getLastPathComponent(); if( node != null ) { - m_controller.deobfuscateClass( new ClassFile( node.getClassName() ) ); + m_controller.deobfuscateClass( new ClassFile( node.getObfClassName() ) ); } } } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 452632f3..7d37febf 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -31,6 +31,8 @@ import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; +import cuchaz.enigma.mapping.TranslationDirection; +import cuchaz.enigma.mapping.Translator; public class GuiController { @@ -132,8 +134,10 @@ public class GuiController public ClassInheritanceTreeNode getClassInheritance( ClassEntry classEntry ) { + Translator deobfuscatingTranslator = m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ); + // create a node for this class - ClassInheritanceTreeNode thisNode = new ClassInheritanceTreeNode( classEntry.getName() ); + ClassInheritanceTreeNode thisNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, classEntry.getName() ); // expand all children recursively thisNode.load( m_deobfuscator.getAncestries(), true ); @@ -143,7 +147,7 @@ public class GuiController for( String superclassName : m_deobfuscator.getAncestries().getAncestry( classEntry.getName() ) ) { // add the parent node - ClassInheritanceTreeNode parentNode = new ClassInheritanceTreeNode( superclassName ); + ClassInheritanceTreeNode parentNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, superclassName ); parentNode.add( node ); node = parentNode; } -- cgit v1.2.3 From 4863d9e001e3073a51e63351828c09df39cef393 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 6 Aug 2014 22:28:17 -0400 Subject: added open entry function to popup menu --- src/cuchaz/enigma/gui/Gui.java | 52 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index e2ccb7d2..57c6ef7b 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -116,6 +116,7 @@ public class Gui private JMenuItem m_closeMappingsMenu; private JMenuItem m_renameMenu; private JMenuItem m_inheritanceMenu; + private JMenuItem m_openEntryMenu; // state private EntryPair m_selectedEntryPair; @@ -258,6 +259,19 @@ public class Gui popupMenu.add( menu ); m_inheritanceMenu = menu; } + { + JMenuItem menu = new JMenuItem( "Open Class" ); + menu.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + openEntry(); + } + } ); + popupMenu.add( menu ); + m_openEntryMenu = menu; + } // init inheritance panel m_inheritanceTree = new JTree(); @@ -693,22 +707,23 @@ public class Gui private void onCaretMove( int pos ) { m_selectedEntryPair = m_controller.getEntryPair( pos ); - if( m_selectedEntryPair != null ) + + boolean isEntry = m_selectedEntryPair != null; + boolean isClassEntry = isEntry && m_selectedEntryPair.obf instanceof ClassEntry; + boolean isMethodEntry = isEntry && m_selectedEntryPair.obf instanceof MethodEntry; + + if( isEntry ) { showEntryPair( m_selectedEntryPair ); - - boolean isClassEntry = m_selectedEntryPair.obf instanceof ClassEntry; - boolean isMethodEntry = m_selectedEntryPair.obf instanceof MethodEntry; - - m_renameMenu.setEnabled( true ); - m_inheritanceMenu.setEnabled( isClassEntry || isMethodEntry ); } else { clearEntryPair(); - m_renameMenu.setEnabled( false ); - m_inheritanceMenu.setEnabled( false ); } + + m_renameMenu.setEnabled( isEntry ); + m_inheritanceMenu.setEnabled( isClassEntry || isMethodEntry ); + m_openEntryMenu.setEnabled( isClassEntry ); } private void startRename( ) @@ -775,6 +790,11 @@ public class Gui private void showInheritance( ) { + if( m_selectedEntryPair == null ) + { + return; + } + // get the current class if( m_selectedEntryPair.obf instanceof ClassEntry ) { @@ -800,6 +820,20 @@ public class Gui redraw(); } + private void openEntry( ) + { + if( m_selectedEntryPair == null ) + { + return; + } + + // get the current class + if( m_selectedEntryPair.obf instanceof ClassEntry ) + { + m_controller.deobfuscateClass( new ClassFile( m_selectedEntryPair.obf.getName() ) ); + } + } + private void close( ) { if( !m_controller.isDirty() ) -- cgit v1.2.3 From 13c1b15a9278eeb62f6a4a58b89f4336d02dbf73 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 6 Aug 2014 23:00:10 -0400 Subject: added un-obfuscated classes to the deobfuscated classes list --- src/cuchaz/enigma/ClassFile.java | 5 +++++ src/cuchaz/enigma/Deobfuscator.java | 4 ++++ src/cuchaz/enigma/TranslatingTypeLoader.java | 6 ++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/cuchaz/enigma/ClassFile.java b/src/cuchaz/enigma/ClassFile.java index 613b379d..043558d0 100644 --- a/src/cuchaz/enigma/ClassFile.java +++ b/src/cuchaz/enigma/ClassFile.java @@ -33,4 +33,9 @@ public class ClassFile { return m_name.replace( ".", "/" ) + ".class"; } + + public boolean isInPackage( ) + { + return m_name.indexOf( '/' ) >= 0; + } } diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 2fceef1c..e067183e 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -133,6 +133,10 @@ public class Deobfuscator { deobfClasses.put( classFile, classMapping.getDeobfName() ); } + else if( classFile.isInPackage() ) + { + deobfClasses.put( classFile, classFile.getName() ); + } else { obfClasses.add( classFile ); diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 44fe9805..f5112e0f 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -19,6 +19,7 @@ import java.util.jar.JarFile; import javassist.ByteArrayClassPath; import javassist.ClassPool; import javassist.CtClass; +import javassist.bytecode.Descriptor; import com.strobel.assembler.metadata.Buffer; import com.strobel.assembler.metadata.ITypeLoader; @@ -75,11 +76,12 @@ public class TranslatingTypeLoader implements ITypeLoader buf = data.toByteArray(); // translate the class + String javaName = Descriptor.toJavaName( name ); ClassPool classPool = new ClassPool(); - classPool.insertClassPath( new ByteArrayClassPath( name, buf ) ); + classPool.insertClassPath( new ByteArrayClassPath( javaName, buf ) ); try { - CtClass c = classPool.get( name ); + CtClass c = classPool.get( javaName ); new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); buf = c.toBytecode(); -- cgit v1.2.3 From 6aa7c6121a2ecbe78f14f8c3d7ddb55b8ddb10bd Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 7 Aug 2014 00:55:43 -0400 Subject: started working on recognition of non-class member identifiers in the source got class extends,implements working and argument,field types added filtering to make sure highlighted class names are actually classes in the jar --- src/cuchaz/enigma/Deobfuscator.java | 87 ++++++++++++++++++++++----- src/cuchaz/enigma/analysis/Analyzer.java | 91 ++++++++++++++++------------- src/cuchaz/enigma/analysis/Lexer.java | 42 ++++++++++++- src/cuchaz/enigma/analysis/SourceIndex.java | 34 ++++++----- src/cuchaz/enigma/analysis/SourcedAst.java | 43 +++++++++----- src/cuchaz/enigma/gui/GuiController.java | 12 +++- src/cuchaz/enigma/gui/SourceFormatter.java | 62 -------------------- 7 files changed, 223 insertions(+), 148 deletions(-) delete mode 100644 src/cuchaz/enigma/gui/SourceFormatter.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index e067183e..e6e647e9 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -10,10 +10,12 @@ ******************************************************************************/ package cuchaz.enigma; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.StringReader; import java.io.StringWriter; import java.util.Enumeration; import java.util.List; @@ -21,6 +23,7 @@ import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import com.beust.jcommander.internal.Lists; import com.strobel.decompiler.Decompiler; import com.strobel.decompiler.DecompilerSettings; import com.strobel.decompiler.PlainTextOutput; @@ -45,6 +48,7 @@ public class Deobfuscator private Ancestries m_ancestries; private Mappings m_mappings; private Renamer m_renamer; + private List m_obfClassNames; public Deobfuscator( File file ) throws IOException @@ -65,6 +69,26 @@ public class Deobfuscator Util.closeQuietly( jarIn ); } + // get the obf class names + m_obfClassNames = Lists.newArrayList(); + { + Enumeration entries = m_jar.entries(); + while( entries.hasMoreElements() ) + { + JarEntry entry = entries.nextElement(); + + // skip everything but class files + if( !entry.getName().endsWith( ".class" ) ) + { + continue; + } + + // get the class name from the file + String className = entry.getName().substring( 0, entry.getName().length() - 6 ); + m_obfClassNames.add( className ); + } + } + // config the decompiler m_settings = DecompilerSettings.javaDefaults(); m_settings.setForceExplicitImports( true ); @@ -112,20 +136,9 @@ public class Deobfuscator public void getSeparatedClasses( List obfClasses, Map deobfClasses ) { - Enumeration entries = m_jar.entries(); - while( entries.hasMoreElements() ) + for( String obfClassName : m_obfClassNames ) { - JarEntry entry = entries.nextElement(); - - // skip everything but class files - if( !entry.getName().endsWith( ".class" ) ) - { - continue; - } - - // get the class name from the file - String className = entry.getName().substring( 0, entry.getName().length() - 6 ); - ClassFile classFile = new ClassFile( className ); + ClassFile classFile = new ClassFile( obfClassName ); // separate the classes ClassMapping classMapping = m_mappings.getClassByObf( classFile.getName() ); @@ -159,7 +172,41 @@ public class Deobfuscator // decompile it! StringWriter buf = new StringWriter(); Decompiler.decompile( deobfName, new PlainTextOutput( buf ), m_settings ); - return buf.toString(); + return fixSource( buf.toString() ); + } + + private String fixSource( String source ) + { + // fix the imports from the default package in the source + try + { + StringBuilder buf = new StringBuilder(); + BufferedReader reader = new BufferedReader( new StringReader( source ) ); + String line = null; + while( ( line = reader.readLine() ) != null ) + { + String[] parts = line.trim().split( " " ); + if( parts.length == 2 && parts[0].equals( "import" ) ) + { + // is this an (illegal) import from the default package? + String className = parts[1]; + if( className.indexOf( '.' ) < 0 ) + { + // this is an illegal import, replace it + line = "import __DEFAULT__." + parts[1]; + } + } + + buf.append( line ); + buf.append( "\n" ); + } + return buf.toString(); + } + catch( IOException ex ) + { + // dealing with IOExceptions on StringReaders is silly... + throw new Error( ex ); + } } // NOTE: these methods are a bit messy... oh well @@ -265,4 +312,16 @@ public class Deobfuscator throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); } } + + public boolean entryIsObfuscatedIdenfitier( Entry obfEntry ) + { + if( obfEntry instanceof ClassEntry ) + { + // obf classes must be in the list + return m_obfClassNames.contains( obfEntry.getName() ); + } + + // assume everything else is an identifier + return true; + } } diff --git a/src/cuchaz/enigma/analysis/Analyzer.java b/src/cuchaz/enigma/analysis/Analyzer.java index dad8dc52..2b7e0b0b 100644 --- a/src/cuchaz/enigma/analysis/Analyzer.java +++ b/src/cuchaz/enigma/analysis/Analyzer.java @@ -35,6 +35,7 @@ import com.sun.source.util.Trees; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; @@ -78,39 +79,31 @@ class TreeVisitor extends TreeScanner private ClassEntry indexClass( ClassTree classTree, SourcedAst ast ) { - // build the entry - ClassEntry entry = new ClassEntry( ast.getFullClassName( classTree.getSimpleName().toString() ) ); + // index the class name + ClassEntry classEntry = indexClassIdentifier( classTree, ast ); + assert( classEntry != null ); - // lex the source at this tree node - for( Token token : new Lexer( ast.getSource( classTree ).toString() ) ) + // index the extends clause + indexClassIdentifier( classTree.getExtendsClause(), ast ); + + // index the implements clauses + for( Tree implementsTree : classTree.getImplementsClause() ) { - // scan until we get the first identifier - if( token.type == TokenType.IDENTIFIER ) - { - m_index.add( entry, offsetToken( token, ast.getStart( classTree ) ) ); - break; - } + indexClassIdentifier( implementsTree, ast ); } - return entry; + return classEntry; } private FieldEntry indexField( VariableTree variableTree, SourcedAst ast, ClassEntry classEntry ) { - // build the entry + // index the field name FieldEntry entry = new FieldEntry( classEntry, variableTree.getName().toString() ); + Token nameToken = new Lexer( ast.getSource( variableTree ) ).getFirstIdentifierMatching( variableTree.getName() ); + addToken( entry, nameToken, variableTree, ast ); - // lex the source at this tree node - Lexer lexer = new Lexer( ast.getSource( variableTree ).toString() ); - for( Token token : lexer ) - { - // scan until we find an identifier that matches the field name - if( token.type == TokenType.IDENTIFIER && lexer.getText( token ).equals( entry.getName() ) ) - { - m_index.add( entry, offsetToken( token, ast.getStart( variableTree ) ) ); - break; - } - } + // index the field type + indexClassIdentifier( variableTree.getType(), ast ); return entry; } @@ -136,13 +129,13 @@ class TreeVisitor extends TreeScanner MethodEntry entry = new MethodEntry( classEntry, methodTree.getName().toString(), signature.toString() ); // lex the source at this tree node - Lexer lexer = new Lexer( ast.getSource( methodTree ).toString() ); + Lexer lexer = new Lexer( ast.getSource( methodTree ) ); for( Token token : lexer ) { // scan until we find an identifier that matches the method name if( token.type == TokenType.IDENTIFIER && lexer.getText( token ).equals( entry.getName() ) ) { - m_index.add( entry, offsetToken( token, ast.getStart( methodTree ) ) ); + addToken( entry, token, methodTree, ast ); break; } } @@ -152,27 +145,47 @@ class TreeVisitor extends TreeScanner private void indexArgument( VariableTree variableTree, SourcedAst ast, MethodEntry methodEntry, int index ) { - // build the entry + // index argument name ArgumentEntry entry = new ArgumentEntry( methodEntry, index, variableTree.getName().toString() ); + Token token = new Lexer( ast.getSource( variableTree ) ).getLastIdentifier(); + addToken( entry, token, variableTree, ast ); - // lex the source at this tree node - Lexer lexer = new Lexer( ast.getSource( variableTree ).toString() ); - for( Token token : lexer ) + // index argument type + indexClassIdentifier( variableTree.getType(), ast ); + } + + private ClassEntry indexClassIdentifier( Tree tree, SourcedAst ast ) + { + if( tree == null ) { - // scan until we find an identifier that matches the variable name - if( token.type == TokenType.IDENTIFIER && lexer.getText( token ).equals( entry.getName() ) ) - { - m_index.add( entry, offsetToken( token, ast.getStart( variableTree ) ) ); - break; - } + return null; + } + + Lexer lexer = new Lexer( ast.getSource( tree ) ); + Token token = lexer.getFirstIdentifier(); + if( token == null ) + { + return null; } + + ClassEntry classEntry = new ClassEntry( ast.getFullClassName( lexer.getText( token ) ) ); + addToken( classEntry, token, tree, ast ); + return classEntry; } - - private Token offsetToken( Token in, int offset ) + + private void addToken( Entry entry, Token token, Tree tree, SourcedAst ast ) { - return new Token( in.type, in.start + offset, in.length ); + if( token == null ) + { + throw new IllegalArgumentException( "token cannot be null!" ); + } + + // offset the token by the tree + Token offsetToken = new Token( token.type, token.start + ast.getStart( tree ), token.length ); + + m_index.add( entry, offsetToken ); } - + private String toJvmType( Tree tree, SourcedAst ast ) { switch( tree.getKind() ) diff --git a/src/cuchaz/enigma/analysis/Lexer.java b/src/cuchaz/enigma/analysis/Lexer.java index acb52bf8..602e3a9b 100644 --- a/src/cuchaz/enigma/analysis/Lexer.java +++ b/src/cuchaz/enigma/analysis/Lexer.java @@ -14,6 +14,7 @@ import java.util.Iterator; import jsyntaxpane.SyntaxDocument; import jsyntaxpane.Token; +import jsyntaxpane.TokenType; import jsyntaxpane.lexers.JavaLexer; public class Lexer implements Iterable @@ -21,10 +22,10 @@ public class Lexer implements Iterable private SyntaxDocument m_doc; private Iterator m_iter; - public Lexer( String source ) + public Lexer( CharSequence source ) { m_doc = new SyntaxDocument( new JavaLexer() ); - m_doc.append( source ); + m_doc.append( source.toString() ); m_iter = m_doc.getTokens( 0, m_doc.getLength() ); } @@ -38,4 +39,41 @@ public class Lexer implements Iterable { return token.getString( m_doc ); } + + public Token getFirstIdentifier( ) + { + for( Token token : this ) + { + if( token.type == TokenType.IDENTIFIER ) + { + return token; + } + } + return null; + } + + public Token getFirstIdentifierMatching( CharSequence val ) + { + for( Token token : this ) + { + if( token.type == TokenType.IDENTIFIER && getText( token ).equals( val.toString() ) ) + { + return token; + } + } + return null; + } + + public Token getLastIdentifier( ) + { + Token lastToken = null; + for( Token token : this ) + { + if( token.type == TokenType.IDENTIFIER ) + { + lastToken = token; + } + } + return lastToken; + } } diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 61c833ce..de163087 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -10,46 +10,52 @@ ******************************************************************************/ package cuchaz.enigma.analysis; +import java.util.Collection; import java.util.Iterator; import java.util.Map; -import java.util.Set; import jsyntaxpane.Token; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; import cuchaz.enigma.mapping.Entry; public class SourceIndex implements Iterable> { - private BiMap m_entryToToken; - private BiMap m_tokenToEntry; + private Multimap m_entryToTokens; public SourceIndex( ) { - m_entryToToken = HashBiMap.create(); - m_tokenToEntry = m_entryToToken.inverse(); + m_entryToTokens = HashMultimap.create(); } public void add( Entry entry, Token token ) { - m_entryToToken.put( entry, token ); + m_entryToTokens.put( entry, token ); } public Iterator> iterator( ) { - return m_entryToToken.entrySet().iterator(); + return m_entryToTokens.entries().iterator(); } - public Set tokens( ) + public Collection tokens( ) { - return m_entryToToken.values(); + return m_entryToTokens.values(); } public Entry getEntry( Token token ) { - return m_tokenToEntry.get( token ); + // linear search is fast enough for now + for( Map.Entry entry : this ) + { + if( entry.getValue().equals( token ) ) + { + return entry.getKey(); + } + } + return null; } public Map.Entry getEntry( int pos ) @@ -66,8 +72,8 @@ public class SourceIndex implements Iterable> return null; } - public Token getToken( Entry entry ) + public Collection getTokens( Entry entry ) { - return m_entryToToken.get( entry ); + return m_entryToTokens.get( entry ); } } diff --git a/src/cuchaz/enigma/analysis/SourcedAst.java b/src/cuchaz/enigma/analysis/SourcedAst.java index 968c8804..a88cc754 100644 --- a/src/cuchaz/enigma/analysis/SourcedAst.java +++ b/src/cuchaz/enigma/analysis/SourcedAst.java @@ -16,7 +16,6 @@ import java.util.HashMap; import javassist.bytecode.Descriptor; import com.google.common.collect.Maps; -import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.ImportTree; import com.sun.source.tree.Tree; @@ -29,6 +28,7 @@ public class SourcedAst private Trees m_trees; private SourcePositions m_positions; private HashMap m_classNameIndex; + private String m_packageName; public SourcedAst( CompilationUnitTree tree, Trees trees ) { @@ -49,6 +49,13 @@ public class SourcedAst // get the full and simple class names String fullName = Descriptor.toJvmName( importTree.getQualifiedIdentifier().toString() ); String simpleName = fullName; + + if( fullName.startsWith( "__DEFAULT__/" ) ) + { + // remove the default package flag + fullName = fullName.substring( 12 ); + } + String[] parts = fullName.split( "/" ); if( parts.length > 0 ) { @@ -58,30 +65,26 @@ public class SourcedAst m_classNameIndex.put( simpleName, fullName ); } - // index the self class using the package name + // get the package name + m_packageName = null; if( m_tree.getPackageName() != null ) { - String packageName = Descriptor.toJvmName( m_tree.getPackageName().toString() ); - for( Tree typeTree : m_tree.getTypeDecls() ) - { - if( typeTree instanceof ClassTree ) - { - ClassTree classTree = (ClassTree)typeTree; - String className = classTree.getSimpleName().toString(); - m_classNameIndex.put( className, packageName + "/" + className ); - } - } + m_packageName = Descriptor.toJvmName( m_tree.getPackageName().toString() ); } } public int getStart( Tree node ) { - return (int)m_positions.getStartPosition( m_tree, node ); + int pos = (int)m_positions.getStartPosition( m_tree, node ); + assert( pos >= 0 ); + return pos; } public int getEnd( Tree node ) { - return (int)m_positions.getEndPosition( m_tree, node ); + int pos = (int)m_positions.getEndPosition( m_tree, node ); + assert( pos >= 0 ); + return pos; } public int getLine( Tree node ) @@ -121,8 +124,16 @@ public class SourcedAst String fullClassName = m_classNameIndex.get( simpleClassName ); if( fullClassName == null ) { - // no mapping was found, the name is probably already fully-qualified - fullClassName = simpleClassName; + if( m_packageName != null ) + { + // no mapping was found, assume it's in the package + fullClassName = m_packageName + "/" + simpleClassName; + } + else + { + // this must be in the default package + fullClassName = simpleClassName; + } } return fullClassName; } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 7d37febf..2219e05b 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -132,6 +132,16 @@ public class GuiController return m_deobfuscator.hasMapping( pair.obf ); } + public boolean entryIsObfuscatedIdenfitier( int pos ) + { + EntryPair pair = getEntryPair( pos ); + if( pair == null || pair.obf == null ) + { + return false; + } + return m_deobfuscator.entryIsObfuscatedIdenfitier( pair.obf ); + } + public ClassInheritanceTreeNode getClassInheritance( ClassEntry classEntry ) { Translator deobfuscatingTranslator = m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ); @@ -216,7 +226,7 @@ public class GuiController { deobfuscatedTokens.add( token ); } - else + else if( entryIsObfuscatedIdenfitier( token.start ) ) { obfuscatedTokens.add( token ); } diff --git a/src/cuchaz/enigma/gui/SourceFormatter.java b/src/cuchaz/enigma/gui/SourceFormatter.java deleted file mode 100644 index f3878405..00000000 --- a/src/cuchaz/enigma/gui/SourceFormatter.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.gui; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.StringReader; - -public class SourceFormatter -{ - public static String format( String in ) - { - return collapseNewlines( in ); - } - - private static String collapseNewlines( String in ) - { - StringBuffer buf = new StringBuffer(); - int numBlankLines = 0; - - BufferedReader reader = new BufferedReader( new StringReader( in ) ); - String line = null; - try - { - while( ( line = reader.readLine() ) != null ) - { - // how blank lines is this? - boolean isBlank = line.trim().length() == 0; - if( isBlank ) - { - numBlankLines++; - - // stop printing blank lines after the first one - if( numBlankLines < 2 ) - { - buf.append( line ); - buf.append( "\n" ); - } - } - else - { - numBlankLines = 0; - buf.append( line ); - buf.append( "\n" ); - } - } - } - catch( IOException ex ) - { - // StringReader will never throw an IOExecption here... - } - return buf.toString(); - } -} -- cgit v1.2.3 From b10af1dbfceb913689226c8fb75641f275eb11b4 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 9 Aug 2014 16:42:25 -0400 Subject: added sorting for deobfuscated classes --- src/cuchaz/enigma/gui/Gui.java | 46 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 57c6ef7b..61e66e04 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -60,11 +60,12 @@ import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; -import com.beust.jcommander.internal.Lists; - import jsyntaxpane.DefaultSyntaxKit; import jsyntaxpane.SyntaxDocument; import jsyntaxpane.Token; + +import com.beust.jcommander.internal.Lists; + import cuchaz.enigma.ClassFile; import cuchaz.enigma.Constants; import cuchaz.enigma.mapping.ArgumentEntry; @@ -78,6 +79,7 @@ import cuchaz.enigma.mapping.MethodEntry; public class Gui { private static Comparator m_obfuscatedClassSorter; + private static Comparator> m_deobfuscatedClassSorter; static { @@ -94,6 +96,36 @@ public class Gui return a.getName().compareTo( b.getName() ); } }; + + m_deobfuscatedClassSorter = new Comparator>( ) + { + @Override + public int compare( Map.Entry a, Map.Entry b ) + { + // I can never keep this rule straight when writing these damn things... + // a < b => -1, a == b => 0, a > b => +1 + + String[] aparts = a.getValue().split( "\\." ); + String[] bparts = b.getValue().split( "\\." ); + for( int i=0; true; i++ ) + { + if( i >= aparts.length ) + { + return -1; + } + else if( i >= bparts.length ) + { + return 1; + } + + int result = aparts[i].compareTo( bparts[i] ); + if( result != 0 ) + { + return result; + } + } + } + }; } private GuiController m_controller; @@ -520,11 +552,11 @@ public class Gui m_closeMappingsMenu.setEnabled( false ); } - public void setObfClasses( List classes ) + public void setObfClasses( List obfClasses ) { - if( classes != null ) + if( obfClasses != null ) { - Vector sortedClasses = new Vector( classes ); + Vector sortedClasses = new Vector( obfClasses ); Collections.sort( sortedClasses, m_obfuscatedClassSorter ); m_obfClasses.setListData( sortedClasses ); } @@ -538,7 +570,9 @@ public class Gui { if( deobfClasses != null ) { - m_deobfClasses.setListData( new Vector>( deobfClasses.entrySet() ) ); + Vector> sortedClasses = new Vector>( deobfClasses.entrySet() ); + Collections.sort( sortedClasses, m_deobfuscatedClassSorter ); + m_deobfClasses.setListData( sortedClasses ); } else { -- cgit v1.2.3 From d24d2b9ad9b5c895020b56f700a72906346482e5 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 10 Aug 2014 01:03:40 -0400 Subject: completely re-wrote token recognizer to bootstrap from Procyon's AST changed imports to guava instead of whatever collections library happened to be on my classpath --- src/cuchaz/enigma/Deobfuscator.java | 114 ++-- src/cuchaz/enigma/analysis/Analyzer.java | 263 --------- src/cuchaz/enigma/analysis/ClassNameIndex.java | 19 - .../enigma/analysis/JavaSourceFromString.java | 31 -- src/cuchaz/enigma/analysis/Lexer.java | 79 --- src/cuchaz/enigma/analysis/SourceIndex.java | 99 ++-- src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 618 +++++++++++++++++++++ src/cuchaz/enigma/analysis/SourcedAst.java | 140 ----- src/cuchaz/enigma/analysis/Token.java | 49 ++ .../enigma/gui/ClassInheritanceTreeNode.java | 2 +- src/cuchaz/enigma/gui/Gui.java | 29 +- src/cuchaz/enigma/gui/GuiController.java | 56 +- src/cuchaz/enigma/mapping/Ancestries.java | 2 +- src/cuchaz/enigma/mapping/ClassMapping.java | 24 +- src/cuchaz/enigma/mapping/EntryPair.java | 5 +- src/cuchaz/enigma/mapping/Mappings.java | 2 +- 16 files changed, 865 insertions(+), 667 deletions(-) delete mode 100644 src/cuchaz/enigma/analysis/Analyzer.java delete mode 100644 src/cuchaz/enigma/analysis/ClassNameIndex.java delete mode 100644 src/cuchaz/enigma/analysis/JavaSourceFromString.java delete mode 100644 src/cuchaz/enigma/analysis/Lexer.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndexVisitor.java delete mode 100644 src/cuchaz/enigma/analysis/SourcedAst.java create mode 100644 src/cuchaz/enigma/analysis/Token.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index e6e647e9..8eda889f 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -10,12 +10,10 @@ ******************************************************************************/ package cuchaz.enigma; -import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.io.StringReader; import java.io.StringWriter; import java.util.Enumeration; import java.util.List; @@ -23,11 +21,26 @@ import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import com.beust.jcommander.internal.Lists; -import com.strobel.decompiler.Decompiler; +import com.google.common.collect.Lists; +import com.strobel.assembler.metadata.MemberReference; +import com.strobel.assembler.metadata.MetadataSystem; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.componentmodel.Key; +import com.strobel.decompiler.DecompilerContext; import com.strobel.decompiler.DecompilerSettings; import com.strobel.decompiler.PlainTextOutput; +import com.strobel.decompiler.languages.java.JavaOutputVisitor; +import com.strobel.decompiler.languages.java.ast.AstBuilder; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.SourceIndexVisitor; import cuchaz.enigma.mapping.Ancestries; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; @@ -91,7 +104,6 @@ public class Deobfuscator // config the decompiler m_settings = DecompilerSettings.javaDefaults(); - m_settings.setForceExplicitImports( true ); m_settings.setShowSyntheticMembers( true ); // init mappings @@ -157,7 +169,7 @@ public class Deobfuscator } } - public String getSource( final ClassFile classFile ) + public SourceIndex getSource( final ClassFile classFile ) { // is this class deobfuscated? // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out @@ -170,45 +182,79 @@ public class Deobfuscator } // decompile it! + TypeDefinition resolvedType = new MetadataSystem( m_settings.getTypeLoader() ).lookupType( deobfName ).resolve(); + DecompilerContext context = new DecompilerContext(); + context.setCurrentType( resolvedType ); + context.setSettings( m_settings ); + AstBuilder builder = new AstBuilder( context ); + builder.addType( resolvedType ); + builder.runTransformations( null ); + CompilationUnit root = builder.getCompilationUnit(); + + // render the AST into source StringWriter buf = new StringWriter(); - Decompiler.decompile( deobfName, new PlainTextOutput( buf ), m_settings ); - return fixSource( buf.toString() ); + root.acceptVisitor( new InsertParenthesesVisitor(), null ); + root.acceptVisitor( new JavaOutputVisitor( new PlainTextOutput( buf ), m_settings ), null ); + + // build the source index + SourceIndex index = new SourceIndex( buf.toString() ); + root.acceptVisitor( new SourceIndexVisitor(), index ); + + return index; } - private String fixSource( String source ) + private void dump( AstNode node, int depth ) { - // fix the imports from the default package in the source - try + StringBuilder buf = new StringBuilder(); + for( int i=0; i %s)", memberRef.getDeclaringType(), memberRef.getName(), memberRef.getSignature() ) ); + } + + for( Key key : Keys.ALL_KEYS ) + { + if( key == Keys.MEMBER_REFERENCE ) { - String[] parts = line.trim().split( " " ); - if( parts.length == 2 && parts[0].equals( "import" ) ) - { - // is this an (illegal) import from the default package? - String className = parts[1]; - if( className.indexOf( '.' ) < 0 ) - { - // this is an illegal import, replace it - line = "import __DEFAULT__." + parts[1]; - } - } - - buf.append( line ); - buf.append( "\n" ); + continue; + } + Object val = node.getUserData( key ); + if( val != null ) + { + buf.append( String.format( " (%s=%s)", key, val ) ); } - return buf.toString(); } - catch( IOException ex ) + + + if( node instanceof Identifier ) + { + Identifier n = (Identifier)node; + buf.append( ": " + n.getName() ); + } + else if( node instanceof MemberReferenceExpression ) + { + MemberReferenceExpression n = (MemberReferenceExpression)node; + buf.append( ": " + n.getTarget() + "." + n.getMemberName() ); + } + else if( node instanceof InvocationExpression ) { - // dealing with IOExceptions on StringReaders is silly... - throw new Error( ex ); + + } + + System.out.println( buf ); + + for( AstNode child : node.getChildren() ) + { + dump( child, depth + 1 ); } } - + // NOTE: these methods are a bit messy... oh well public void rename( Entry obfEntry, String newName ) diff --git a/src/cuchaz/enigma/analysis/Analyzer.java b/src/cuchaz/enigma/analysis/Analyzer.java deleted file mode 100644 index 2b7e0b0b..00000000 --- a/src/cuchaz/enigma/analysis/Analyzer.java +++ /dev/null @@ -1,263 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import java.io.IOException; -import java.util.Arrays; - -import javax.tools.JavaCompiler; -import javax.tools.StandardJavaFileManager; -import javax.tools.ToolProvider; - -import jsyntaxpane.Token; -import jsyntaxpane.TokenType; - -import com.sun.source.tree.ArrayTypeTree; -import com.sun.source.tree.ClassTree; -import com.sun.source.tree.CompilationUnitTree; -import com.sun.source.tree.IdentifierTree; -import com.sun.source.tree.MethodTree; -import com.sun.source.tree.PrimitiveTypeTree; -import com.sun.source.tree.Tree; -import com.sun.source.tree.Tree.Kind; -import com.sun.source.tree.VariableTree; -import com.sun.source.util.JavacTask; -import com.sun.source.util.TreeScanner; -import com.sun.source.util.Trees; - -import cuchaz.enigma.mapping.ArgumentEntry; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.Entry; -import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.MethodEntry; - -class TreeVisitor extends TreeScanner -{ - private SourceIndex m_index; - - public TreeVisitor( SourceIndex index ) - { - m_index = index; - } - - @Override - public CompilationUnitTree visitClass( ClassTree classTree, SourcedAst ast ) - { - ClassEntry classEntry = indexClass( classTree, ast ); - - // look at the class members - for( Tree memberTree : classTree.getMembers() ) - { - if( memberTree.getKind() == Kind.VARIABLE ) - { - indexField( (VariableTree)memberTree, ast, classEntry ); - } - else if( memberTree.getKind() == Kind.METHOD ) - { - MethodTree methodTree = (MethodTree)memberTree; - MethodEntry methodEntry = indexMethod( methodTree, ast, classEntry ); - - // look at method arguments - int argNum = 0; - for( VariableTree variableTree : methodTree.getParameters() ) - { - indexArgument( variableTree, ast, methodEntry, argNum++ ); - } - } - } - - return super.visitClass( classTree, ast ); - } - - private ClassEntry indexClass( ClassTree classTree, SourcedAst ast ) - { - // index the class name - ClassEntry classEntry = indexClassIdentifier( classTree, ast ); - assert( classEntry != null ); - - // index the extends clause - indexClassIdentifier( classTree.getExtendsClause(), ast ); - - // index the implements clauses - for( Tree implementsTree : classTree.getImplementsClause() ) - { - indexClassIdentifier( implementsTree, ast ); - } - - return classEntry; - } - - private FieldEntry indexField( VariableTree variableTree, SourcedAst ast, ClassEntry classEntry ) - { - // index the field name - FieldEntry entry = new FieldEntry( classEntry, variableTree.getName().toString() ); - Token nameToken = new Lexer( ast.getSource( variableTree ) ).getFirstIdentifierMatching( variableTree.getName() ); - addToken( entry, nameToken, variableTree, ast ); - - // index the field type - indexClassIdentifier( variableTree.getType(), ast ); - - return entry; - } - - private MethodEntry indexMethod( MethodTree methodTree, SourcedAst ast, ClassEntry classEntry ) - { - // build the entry - StringBuilder signature = new StringBuilder(); - signature.append( "(" ); - for( VariableTree variableTree : methodTree.getParameters() ) - { - signature.append( toJvmType( variableTree.getType(), ast ) ); - } - signature.append( ")" ); - if( methodTree.getReturnType() != null ) - { - signature.append( toJvmType( methodTree.getReturnType(), ast ) ); - } - else - { - signature.append( "V" ); - } - MethodEntry entry = new MethodEntry( classEntry, methodTree.getName().toString(), signature.toString() ); - - // lex the source at this tree node - Lexer lexer = new Lexer( ast.getSource( methodTree ) ); - for( Token token : lexer ) - { - // scan until we find an identifier that matches the method name - if( token.type == TokenType.IDENTIFIER && lexer.getText( token ).equals( entry.getName() ) ) - { - addToken( entry, token, methodTree, ast ); - break; - } - } - - return entry; - } - - private void indexArgument( VariableTree variableTree, SourcedAst ast, MethodEntry methodEntry, int index ) - { - // index argument name - ArgumentEntry entry = new ArgumentEntry( methodEntry, index, variableTree.getName().toString() ); - Token token = new Lexer( ast.getSource( variableTree ) ).getLastIdentifier(); - addToken( entry, token, variableTree, ast ); - - // index argument type - indexClassIdentifier( variableTree.getType(), ast ); - } - - private ClassEntry indexClassIdentifier( Tree tree, SourcedAst ast ) - { - if( tree == null ) - { - return null; - } - - Lexer lexer = new Lexer( ast.getSource( tree ) ); - Token token = lexer.getFirstIdentifier(); - if( token == null ) - { - return null; - } - - ClassEntry classEntry = new ClassEntry( ast.getFullClassName( lexer.getText( token ) ) ); - addToken( classEntry, token, tree, ast ); - return classEntry; - } - - private void addToken( Entry entry, Token token, Tree tree, SourcedAst ast ) - { - if( token == null ) - { - throw new IllegalArgumentException( "token cannot be null!" ); - } - - // offset the token by the tree - Token offsetToken = new Token( token.type, token.start + ast.getStart( tree ), token.length ); - - m_index.add( entry, offsetToken ); - } - - private String toJvmType( Tree tree, SourcedAst ast ) - { - switch( tree.getKind() ) - { - case PRIMITIVE_TYPE: - { - PrimitiveTypeTree primitiveTypeTree = (PrimitiveTypeTree)tree; - switch( primitiveTypeTree.getPrimitiveTypeKind() ) - { - case BOOLEAN: return "Z"; - case BYTE: return "B"; - case CHAR: return "C"; - case DOUBLE: return "D"; - case FLOAT: return "F"; - case INT: return "I"; - case LONG: return "J"; - case SHORT: return "S"; - case VOID: return "V"; - - default: - throw new Error( "Unsupported primitive type: " + primitiveTypeTree.getPrimitiveTypeKind() ); - } - } - - case IDENTIFIER: - { - IdentifierTree identifierTree = (IdentifierTree)tree; - String className = identifierTree.getName().toString(); - className = ast.getFullClassName( className ); - return "L" + className.replace( ".", "/" ) + ";"; - } - - case ARRAY_TYPE: - { - ArrayTypeTree arrayTree = (ArrayTypeTree)tree; - return "[" + toJvmType( arrayTree.getType(), ast ); - } - - - default: - throw new Error( "Unsupported type kind: " + tree.getKind() ); - } - } -} - -public class Analyzer -{ - public static SourceIndex analyze( String className, String source ) - { - SourceIndex index = new SourceIndex(); - SourcedAst ast = getAst( className, source ); - ast.visit( new TreeVisitor( index ) ); - return index; - } - - private static SourcedAst getAst( String className, String source ) - { - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null ); - JavaSourceFromString unit = new JavaSourceFromString( className, source ); - JavacTask task = (JavacTask)compiler.getTask( null, fileManager, null, null, null, Arrays.asList( unit ) ); - - try - { - return new SourcedAst( - task.parse().iterator().next(), - Trees.instance( task ) - ); - } - catch( IOException ex ) - { - throw new Error( ex ); - } - } -} diff --git a/src/cuchaz/enigma/analysis/ClassNameIndex.java b/src/cuchaz/enigma/analysis/ClassNameIndex.java deleted file mode 100644 index ea3e2cae..00000000 --- a/src/cuchaz/enigma/analysis/ClassNameIndex.java +++ /dev/null @@ -1,19 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import com.sun.source.tree.CompilationUnitTree; -import com.sun.source.util.TreeScanner; - -public class ClassNameIndex extends TreeScanner -{ - -} diff --git a/src/cuchaz/enigma/analysis/JavaSourceFromString.java b/src/cuchaz/enigma/analysis/JavaSourceFromString.java deleted file mode 100644 index cf5c4c27..00000000 --- a/src/cuchaz/enigma/analysis/JavaSourceFromString.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import java.net.URI; - -import javax.tools.SimpleJavaFileObject; - -public class JavaSourceFromString extends SimpleJavaFileObject -{ - private final String m_source; - - JavaSourceFromString( String name, String source ) - { - super( URI.create( "string:///" + name.replace( '.', '/' ) + Kind.SOURCE.extension ), Kind.SOURCE ); - m_source = source; - } - - public CharSequence getCharContent( boolean ignoreEncodingErrors ) - { - return m_source; - } -} diff --git a/src/cuchaz/enigma/analysis/Lexer.java b/src/cuchaz/enigma/analysis/Lexer.java deleted file mode 100644 index 602e3a9b..00000000 --- a/src/cuchaz/enigma/analysis/Lexer.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import java.util.Iterator; - -import jsyntaxpane.SyntaxDocument; -import jsyntaxpane.Token; -import jsyntaxpane.TokenType; -import jsyntaxpane.lexers.JavaLexer; - -public class Lexer implements Iterable -{ - private SyntaxDocument m_doc; - private Iterator m_iter; - - public Lexer( CharSequence source ) - { - m_doc = new SyntaxDocument( new JavaLexer() ); - m_doc.append( source.toString() ); - m_iter = m_doc.getTokens( 0, m_doc.getLength() ); - } - - @Override - public Iterator iterator( ) - { - return m_iter; - } - - public String getText( Token token ) - { - return token.getString( m_doc ); - } - - public Token getFirstIdentifier( ) - { - for( Token token : this ) - { - if( token.type == TokenType.IDENTIFIER ) - { - return token; - } - } - return null; - } - - public Token getFirstIdentifierMatching( CharSequence val ) - { - for( Token token : this ) - { - if( token.type == TokenType.IDENTIFIER && getText( token ).equals( val.toString() ) ) - { - return token; - } - } - return null; - } - - public Token getLastIdentifier( ) - { - Token lastToken = null; - for( Token token : this ) - { - if( token.type == TokenType.IDENTIFIER ) - { - lastToken = token; - } - } - return lastToken; - } -} diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index de163087..398a50d1 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -10,70 +10,101 @@ ******************************************************************************/ package cuchaz.enigma.analysis; -import java.util.Collection; -import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.TreeMap; -import jsyntaxpane.Token; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.strobel.decompiler.languages.Region; +import com.strobel.decompiler.languages.java.ast.AstNode; import cuchaz.enigma.mapping.Entry; -public class SourceIndex implements Iterable> +public class SourceIndex { - private Multimap m_entryToTokens; + private String m_source; + private TreeMap m_tokens; + private List m_lineOffsets; - public SourceIndex( ) + public SourceIndex( String source ) { - m_entryToTokens = HashMultimap.create(); + m_source = source; + m_tokens = Maps.newTreeMap(); + m_lineOffsets = Lists.newArrayList(); + + // count the lines + m_lineOffsets.add( 0 ); + for( int i=0; i> iterator( ) + public Token getToken( AstNode node ) { - return m_entryToTokens.entries().iterator(); + // get a token for this node's region + Region region = node.getRegion(); + if( region.getBeginLine() == 0 || region.getEndLine() == 0 ) + { + throw new IllegalArgumentException( "Invalid region: " + region ); + } + return new Token( + toPos( region.getBeginLine(), region.getBeginColumn() ), + toPos( region.getEndLine(), region.getEndColumn() ) + ); } - public Collection tokens( ) + public void add( AstNode node, Entry entry ) { - return m_entryToTokens.values(); + m_tokens.put( getToken( node ), entry ); } - public Entry getEntry( Token token ) + public void add( Token token, Entry entry ) + { + m_tokens.put( token, entry ); + } + + public Token getToken( int pos ) { - // linear search is fast enough for now - for( Map.Entry entry : this ) + Map.Entry mapEntry = m_tokens.floorEntry( new Token( pos, pos ) ); + if( mapEntry == null ) { - if( entry.getValue().equals( token ) ) - { - return entry.getKey(); - } + return null; + } + Token token = mapEntry.getKey(); + if( token.contains( pos ) ) + { + return token; } return null; } - public Map.Entry getEntry( int pos ) + public Entry getEntry( Token token ) { - // linear search is fast enough for now - for( Map.Entry entry : this ) + if( token == null ) { - Token token = entry.getValue(); - if( pos >= token.start && pos <= token.end() ) - { - return entry; - } + return null; } - return null; + return m_tokens.get( token ); + } + + public Iterable tokens( ) + { + return m_tokens.keySet(); } - public Collection getTokens( Entry entry ) + private int toPos( int line, int col ) { - return m_entryToTokens.get( entry ); + // line and col are 1-based + return m_lineOffsets.get( line - 1 ) + col - 1; } } diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java new file mode 100644 index 00000000..2a26c85f --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -0,0 +1,618 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.strobel.assembler.metadata.FieldDefinition; +import com.strobel.assembler.metadata.MemberReference; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.ParameterDefinition; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.componentmodel.Key; +import com.strobel.decompiler.languages.TextLocation; +import com.strobel.decompiler.languages.java.ast.Annotation; +import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; +import com.strobel.decompiler.languages.java.ast.ArraySpecifier; +import com.strobel.decompiler.languages.java.ast.AssertStatement; +import com.strobel.decompiler.languages.java.ast.AssignmentExpression; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.BinaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.BlockStatement; +import com.strobel.decompiler.languages.java.ast.BreakStatement; +import com.strobel.decompiler.languages.java.ast.CaseLabel; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.CatchClause; +import com.strobel.decompiler.languages.java.ast.ClassOfExpression; +import com.strobel.decompiler.languages.java.ast.Comment; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; +import com.strobel.decompiler.languages.java.ast.ComposedType; +import com.strobel.decompiler.languages.java.ast.ConditionalExpression; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.ContinueStatement; +import com.strobel.decompiler.languages.java.ast.DoWhileStatement; +import com.strobel.decompiler.languages.java.ast.EmptyStatement; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.Expression; +import com.strobel.decompiler.languages.java.ast.ExpressionStatement; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.ForEachStatement; +import com.strobel.decompiler.languages.java.ast.ForStatement; +import com.strobel.decompiler.languages.java.ast.GotoStatement; +import com.strobel.decompiler.languages.java.ast.IAstVisitor; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.IfElseStatement; +import com.strobel.decompiler.languages.java.ast.ImportDeclaration; +import com.strobel.decompiler.languages.java.ast.IndexerExpression; +import com.strobel.decompiler.languages.java.ast.InstanceInitializer; +import com.strobel.decompiler.languages.java.ast.InstanceOfExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.JavaTokenNode; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.LabelStatement; +import com.strobel.decompiler.languages.java.ast.LabeledStatement; +import com.strobel.decompiler.languages.java.ast.LambdaExpression; +import com.strobel.decompiler.languages.java.ast.LocalTypeDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.MethodGroupExpression; +import com.strobel.decompiler.languages.java.ast.NewLineNode; +import com.strobel.decompiler.languages.java.ast.NullReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.PackageDeclaration; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.ParenthesizedExpression; +import com.strobel.decompiler.languages.java.ast.PrimitiveExpression; +import com.strobel.decompiler.languages.java.ast.ReturnStatement; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.SwitchSection; +import com.strobel.decompiler.languages.java.ast.SwitchStatement; +import com.strobel.decompiler.languages.java.ast.SynchronizedStatement; +import com.strobel.decompiler.languages.java.ast.TextNode; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThrowStatement; +import com.strobel.decompiler.languages.java.ast.TryCatchStatement; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; +import com.strobel.decompiler.languages.java.ast.UnaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; +import com.strobel.decompiler.languages.java.ast.WhileStatement; +import com.strobel.decompiler.languages.java.ast.WildcardType; +import com.strobel.decompiler.patterns.Pattern; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class SourceIndexVisitor implements IAstVisitor +{ + @Override + public Void visitComment( Comment node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitPatternPlaceholder( AstNode node, Pattern pattern, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitInvocationExpression( InvocationExpression node, SourceIndex index ) + { + MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); + ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); + MethodEntry methodEntry = new MethodEntry( classEntry, ref.getName(), ref.getSignature() ); + if( node.getTarget() instanceof MemberReferenceExpression ) + { + index.add( ((MemberReferenceExpression)node.getTarget()).getMemberNameToken(), methodEntry ); + } + + return recurse( node, index ); + } + + @Override + public Void visitTypeReference( TypeReferenceExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitJavaTokenNode( JavaTokenNode node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitMemberReferenceExpression( MemberReferenceExpression node, SourceIndex index ) + { + MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); + if( ref != null ) + { + ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); + FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); + index.add( node.getMemberNameToken(), fieldEntry ); + } + + return recurse( node, index ); + } + + @Override + public Void visitIdentifier( Identifier node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitNullReferenceExpression( NullReferenceExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitThisReferenceExpression( ThisReferenceExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitSuperReferenceExpression( SuperReferenceExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitClassOfExpression( ClassOfExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitBlockStatement( BlockStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitExpressionStatement( ExpressionStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitBreakStatement( BreakStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitContinueStatement( ContinueStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitDoWhileStatement( DoWhileStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitEmptyStatement( EmptyStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitIfElseStatement( IfElseStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitLabelStatement( LabelStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitLabeledStatement( LabeledStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitReturnStatement( ReturnStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitSwitchStatement( SwitchStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitSwitchSection( SwitchSection node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitCaseLabel( CaseLabel node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitThrowStatement( ThrowStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitCatchClause( CatchClause node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitAnnotation( Annotation node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitNewLine( NewLineNode node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitVariableDeclaration( VariableDeclarationStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitVariableInitializer( VariableInitializer node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitText( TextNode node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitImportDeclaration( ImportDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitSimpleType( SimpleType node, SourceIndex index ) + { + TypeReference ref = node.getUserData( Keys.TYPE_REFERENCE ); + if( node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY ) + { + index.add( node.getIdentifierToken(), new ClassEntry( ref.getInternalName() ) ); + } + + return recurse( node, index ); + } + + @Override + public Void visitMethodDeclaration( MethodDeclaration node, SourceIndex index ) + { + MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + MethodEntry methodEntry = new MethodEntry( classEntry, def.getName(), def.getSignature() ); + index.add( node.getNameToken(), methodEntry ); + + return recurse( node, index ); + } + + @Override + public Void visitInitializerBlock( InstanceInitializer node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitConstructorDeclaration( ConstructorDeclaration node, SourceIndex index ) + { + MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); + index.add( node.getNameToken(), new ClassEntry( def.getDeclaringType().getInternalName() ) ); + + return recurse( node, index ); + } + + @Override + public Void visitTypeParameterDeclaration( TypeParameterDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitParameterDeclaration( ParameterDeclaration node, SourceIndex index ) + { + ParameterDefinition def = node.getUserData( Keys.PARAMETER_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + MethodDefinition methodDef = (MethodDefinition)def.getMethod(); + MethodEntry methodEntry = new MethodEntry( classEntry, methodDef.getName(), methodDef.getSignature() ); + ArgumentEntry argumentEntry = new ArgumentEntry( methodEntry, def.getPosition(), def.getName() ); + index.add( node.getNameToken(), argumentEntry ); + + return recurse( node, index ); + } + + @Override + public Void visitFieldDeclaration( FieldDeclaration node, SourceIndex index ) + { + FieldDefinition def = node.getUserData( Keys.FIELD_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + FieldEntry fieldEntry = new FieldEntry( classEntry, def.getName() ); + assert( node.getVariables().size() == 1 ); + VariableInitializer variable = node.getVariables().firstOrNullObject(); + index.add( variable.getNameToken(), fieldEntry ); + + return recurse( node, index ); + } + + @Override + public Void visitTypeDeclaration( TypeDeclaration node, SourceIndex index ) + { + TypeDefinition def = node.getUserData( Keys.TYPE_DEFINITION ); + index.add( node.getNameToken(), new ClassEntry( def.getInternalName() ) ); + + return recurse( node, index ); + } + + @Override + public Void visitCompilationUnit( CompilationUnit node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitPackageDeclaration( PackageDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitArraySpecifier( ArraySpecifier node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitComposedType( ComposedType node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitWhileStatement( WhileStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitPrimitiveExpression( PrimitiveExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitCastExpression( CastExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitBinaryOperatorExpression( BinaryOperatorExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitInstanceOfExpression( InstanceOfExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitIndexerExpression( IndexerExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitIdentifierExpression( IdentifierExpression node, SourceIndex index ) + { + // TODO + return recurse( node, index ); + } + + @Override + public Void visitUnaryOperatorExpression( UnaryOperatorExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitConditionalExpression( ConditionalExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitArrayInitializerExpression( ArrayInitializerExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitObjectCreationExpression( ObjectCreationExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitArrayCreationExpression( ArrayCreationExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitAssignmentExpression( AssignmentExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitForStatement( ForStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitForEachStatement( ForEachStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitTryCatchStatement( TryCatchStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitGotoStatement( GotoStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitParenthesizedExpression( ParenthesizedExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitSynchronizedStatement( SynchronizedStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitAnonymousObjectCreationExpression( AnonymousObjectCreationExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitWildcardType( WildcardType node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitMethodGroupExpression( MethodGroupExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitEnumValueDeclaration( EnumValueDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitAssertStatement( AssertStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitLambdaExpression( LambdaExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitLocalTypeDeclarationStatement( LocalTypeDeclarationStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + private Void recurse( AstNode node, SourceIndex index ) + { + // TEMP: show the tree + System.out.println( getIndent( node ) + node.getClass().getSimpleName() + dumpUserData( node ) + " " + node.getRegion() ); + + for( final AstNode child : node.getChildren() ) + { + child.acceptVisitor( this, index ); + } + return null; + } + + private String dumpUserData( AstNode node ) + { + StringBuilder buf = new StringBuilder(); + for( Key key : Keys.ALL_KEYS ) + { + Object val = node.getUserData( key ); + if( val != null ) + { + buf.append( String.format( " [%s=%s]", key, val ) ); + } + } + return buf.toString(); + } + + private String getIndent( AstNode node ) + { + StringBuilder buf = new StringBuilder(); + int depth = getDepth( node ); + for( int i = 0; i < depth; i++ ) + { + buf.append( "\t" ); + } + return buf.toString(); + } + + private int getDepth( AstNode node ) + { + int depth = -1; + while( node != null ) + { + depth++; + node = node.getParent(); + } + return depth; + } +} diff --git a/src/cuchaz/enigma/analysis/SourcedAst.java b/src/cuchaz/enigma/analysis/SourcedAst.java deleted file mode 100644 index a88cc754..00000000 --- a/src/cuchaz/enigma/analysis/SourcedAst.java +++ /dev/null @@ -1,140 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import java.io.IOException; -import java.util.HashMap; - -import javassist.bytecode.Descriptor; - -import com.google.common.collect.Maps; -import com.sun.source.tree.CompilationUnitTree; -import com.sun.source.tree.ImportTree; -import com.sun.source.tree.Tree; -import com.sun.source.util.SourcePositions; -import com.sun.source.util.Trees; - -public class SourcedAst -{ - private CompilationUnitTree m_tree; - private Trees m_trees; - private SourcePositions m_positions; - private HashMap m_classNameIndex; - private String m_packageName; - - public SourcedAst( CompilationUnitTree tree, Trees trees ) - { - m_tree = tree; - m_trees = trees; - m_positions = m_trees.getSourcePositions(); - m_classNameIndex = Maps.newHashMap(); - - // index all the class names from package imports - for( ImportTree importTree : m_tree.getImports() ) - { - // ignore static imports for now - if( importTree.isStatic() ) - { - continue; - } - - // get the full and simple class names - String fullName = Descriptor.toJvmName( importTree.getQualifiedIdentifier().toString() ); - String simpleName = fullName; - - if( fullName.startsWith( "__DEFAULT__/" ) ) - { - // remove the default package flag - fullName = fullName.substring( 12 ); - } - - String[] parts = fullName.split( "/" ); - if( parts.length > 0 ) - { - simpleName = parts[parts.length - 1]; - } - - m_classNameIndex.put( simpleName, fullName ); - } - - // get the package name - m_packageName = null; - if( m_tree.getPackageName() != null ) - { - m_packageName = Descriptor.toJvmName( m_tree.getPackageName().toString() ); - } - } - - public int getStart( Tree node ) - { - int pos = (int)m_positions.getStartPosition( m_tree, node ); - assert( pos >= 0 ); - return pos; - } - - public int getEnd( Tree node ) - { - int pos = (int)m_positions.getEndPosition( m_tree, node ); - assert( pos >= 0 ); - return pos; - } - - public int getLine( Tree node ) - { - return getLine( getStart( node ) ); - } - - public int getLine( int pos ) - { - return (int)m_tree.getLineMap().getLineNumber( pos ); - } - - public CharSequence getSource( ) - { - try - { - return m_tree.getSourceFile().getCharContent( true ); - } - catch( IOException ex ) - { - throw new Error( ex ); - } - } - - public CharSequence getSource( Tree node ) - { - return getSource().subSequence( getStart( node ), getEnd( node ) ); - } - - public void visit( TreeVisitor visitor ) - { - m_tree.accept( visitor, this ); - } - - public String getFullClassName( String simpleClassName ) - { - String fullClassName = m_classNameIndex.get( simpleClassName ); - if( fullClassName == null ) - { - if( m_packageName != null ) - { - // no mapping was found, assume it's in the package - fullClassName = m_packageName + "/" + simpleClassName; - } - else - { - // this must be in the default package - fullClassName = simpleClassName; - } - } - return fullClassName; - } -} diff --git a/src/cuchaz/enigma/analysis/Token.java b/src/cuchaz/enigma/analysis/Token.java new file mode 100644 index 00000000..74023e32 --- /dev/null +++ b/src/cuchaz/enigma/analysis/Token.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +public class Token implements Comparable +{ + public int start; + public int end; + + public Token( int start, int end ) + { + this.start = start; + this.end = end; + } + + public boolean contains( int pos ) + { + return pos >= start && pos <= end; + } + + @Override + public int compareTo( Token other ) + { + return start - other.start; + } + + @Override + public boolean equals( Object other ) + { + if( other instanceof Token ) + { + return equals( (Token)other ); + } + return false; + } + + public boolean equals( Token other ) + { + return start == other.start && end == other.end; + } +} diff --git a/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java index d8e67554..61e582df 100644 --- a/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java +++ b/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java @@ -14,7 +14,7 @@ import java.util.List; import javax.swing.tree.DefaultMutableTreeNode; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import cuchaz.enigma.mapping.Ancestries; import cuchaz.enigma.mapping.Translator; diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 61e66e04..187852a2 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -62,12 +62,12 @@ import javax.swing.tree.TreePath; import jsyntaxpane.DefaultSyntaxKit; import jsyntaxpane.SyntaxDocument; -import jsyntaxpane.Token; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import cuchaz.enigma.ClassFile; import cuchaz.enigma.Constants; +import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; @@ -643,7 +643,7 @@ public class Gui { try { - m_editor.getHighlighter().addHighlight( token.start, token.end(), painter ); + m_editor.getHighlighter().addHighlight( token.start, token.end, painter ); } catch( BadLocationException ex ) { @@ -740,22 +740,20 @@ public class Gui private void onCaretMove( int pos ) { - m_selectedEntryPair = m_controller.getEntryPair( pos ); - - boolean isEntry = m_selectedEntryPair != null; - boolean isClassEntry = isEntry && m_selectedEntryPair.obf instanceof ClassEntry; - boolean isMethodEntry = isEntry && m_selectedEntryPair.obf instanceof MethodEntry; - - if( isEntry ) - { - showEntryPair( m_selectedEntryPair ); - } - else + Token token = m_controller.getToken( pos ); + m_renameMenu.setEnabled( token != null ); + if( token == null ) { clearEntryPair(); + return; } - m_renameMenu.setEnabled( isEntry ); + m_selectedEntryPair = m_controller.getEntryPair( token ); + boolean isClassEntry = m_selectedEntryPair.obf instanceof ClassEntry; + boolean isMethodEntry = m_selectedEntryPair.obf instanceof MethodEntry; + + showEntryPair( m_selectedEntryPair ); + m_inheritanceMenu.setEnabled( isClassEntry || isMethodEntry ); m_openEntryMenu.setEnabled( isClassEntry ); } @@ -803,6 +801,7 @@ public class Gui int lineNum = doc.getLineNumberAt( m_editor.getCaretPosition() ); try { + // TODO: give token to the controller so we can put the caret back there m_controller.rename( m_selectedEntryPair.obf, newName, lineNum ); } catch( IllegalNameException ex ) diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 2219e05b..e0aad860 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -17,15 +17,13 @@ import java.io.IOException; import java.util.List; import java.util.Map; -import jsyntaxpane.Token; - -import com.beust.jcommander.internal.Lists; -import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import cuchaz.enigma.ClassFile; import cuchaz.enigma.Deobfuscator; -import cuchaz.enigma.analysis.Analyzer; import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; @@ -105,41 +103,35 @@ public class GuiController deobfuscate( m_currentFile ); } - public EntryPair getEntryPair( int pos ) + public Token getToken( int pos ) { if( m_index == null ) { return null; } - Map.Entry deobfEntryAndToken = m_index.getEntry( pos ); - if( deobfEntryAndToken == null ) + return m_index.getToken( pos ); + } + + public EntryPair getEntryPair( Token token ) + { + if( m_index == null ) { return null; } - Entry deobfEntry = deobfEntryAndToken.getKey(); - Token token = deobfEntryAndToken.getValue(); - return new EntryPair( m_deobfuscator.obfuscateEntry( deobfEntry ), deobfEntry, token ); + + Entry deobfEntry = m_index.getEntry( token ); + return new EntryPair( m_deobfuscator.obfuscateEntry( deobfEntry ), deobfEntry ); } - public boolean entryHasMapping( int pos ) + public boolean entryHasMapping( Entry deobfEntry ) { - EntryPair pair = getEntryPair( pos ); - if( pair == null || pair.obf == null ) - { - return false; - } - return m_deobfuscator.hasMapping( pair.obf ); + return m_deobfuscator.hasMapping( m_deobfuscator.obfuscateEntry( deobfEntry ) ); } - public boolean entryIsObfuscatedIdenfitier( int pos ) + public boolean entryIsObfuscatedIdenfitier( Entry deobfEntry ) { - EntryPair pair = getEntryPair( pos ); - if( pair == null || pair.obf == null ) - { - return false; - } - return m_deobfuscator.entryIsObfuscatedIdenfitier( pair.obf ); + return m_deobfuscator.entryIsObfuscatedIdenfitier( m_deobfuscator.obfuscateEntry( deobfEntry ) ); } public ClassInheritanceTreeNode getClassInheritance( ClassEntry classEntry ) @@ -210,23 +202,21 @@ public class GuiController @Override public void run( ) { - // deobfuscate,decompile the bytecode - String source = m_deobfuscator.getSource( classFile ); - m_gui.setSource( source, lineNum ); - - // index the source file - m_index = Analyzer.analyze( classFile.getName(), source ); + // decopmile,deobfuscate the bytecode + m_index = m_deobfuscator.getSource( classFile ); + m_gui.setSource( m_index.getSource(), lineNum ); // set the highlighted tokens List obfuscatedTokens = Lists.newArrayList(); List deobfuscatedTokens = Lists.newArrayList(); for( Token token : m_index.tokens() ) { - if( entryHasMapping( token.start ) ) + Entry entry = m_index.getEntry( token ); + if( entryHasMapping( entry ) ) { deobfuscatedTokens.add( token ); } - else if( entryIsObfuscatedIdenfitier( token.start ) ) + else if( entryIsObfuscatedIdenfitier( entry ) ) { obfuscatedTokens.add( token ); } diff --git a/src/cuchaz/enigma/mapping/Ancestries.java b/src/cuchaz/enigma/mapping/Ancestries.java index f77a00eb..894cf802 100644 --- a/src/cuchaz/enigma/mapping/Ancestries.java +++ b/src/cuchaz/enigma/mapping/Ancestries.java @@ -26,7 +26,7 @@ import javassist.CtClass; import javassist.NotFoundException; import javassist.bytecode.Descriptor; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import cuchaz.enigma.Constants; diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index a1cc775f..c6826f31 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -13,7 +13,7 @@ package cuchaz.enigma.mapping; import java.io.Serializable; import java.util.Map; -import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Maps; public class ClassMapping implements Serializable, Comparable { @@ -135,16 +135,16 @@ public class ClassMapping implements Serializable, Comparable public void setMethodNameAndSignature( String obfName, String obfSignature, String deobfName, String deobfSignature ) { - MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) ); - if( methodIndex == null ) + MethodMapping methodMapping = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) ); + if( methodMapping == null ) { - methodIndex = createMethodIndex( obfName, obfSignature ); + methodMapping = createMethodIndex( obfName, obfSignature ); } - m_methodsByDeobf.remove( getMethodKey( methodIndex.getDeobfName(), methodIndex.getDeobfSignature() ) ); - methodIndex.setDeobfName( deobfName ); - methodIndex.setDeobfSignature( deobfSignature ); - m_methodsByDeobf.put( getMethodKey( deobfName, deobfSignature ), methodIndex ); + m_methodsByDeobf.remove( getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ) ); + methodMapping.setDeobfName( deobfName ); + methodMapping.setDeobfSignature( deobfSignature ); + m_methodsByDeobf.put( getMethodKey( deobfName, deobfSignature ), methodMapping ); } public void updateDeobfMethodSignatures( Translator translator ) @@ -167,11 +167,11 @@ public class ClassMapping implements Serializable, Comparable private MethodMapping createMethodIndex( String obfName, String obfSignature ) { - MethodMapping methodIndex = new MethodMapping( obfName, obfName, obfSignature, obfSignature ); + MethodMapping methodMapping = new MethodMapping( obfName, obfName, obfSignature, obfSignature ); String key = getMethodKey( obfName, obfSignature ); - m_methodsByObf.put( key, methodIndex ); - m_methodsByDeobf.put( key, methodIndex ); - return methodIndex; + m_methodsByObf.put( key, methodMapping ); + m_methodsByDeobf.put( key, methodMapping ); + return methodMapping; } @Override diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java index 1bf9be09..f94d77ea 100644 --- a/src/cuchaz/enigma/mapping/EntryPair.java +++ b/src/cuchaz/enigma/mapping/EntryPair.java @@ -10,19 +10,16 @@ ******************************************************************************/ package cuchaz.enigma.mapping; -import jsyntaxpane.Token; public class EntryPair { public T obf; public T deobf; - public Token token; - public EntryPair( T obf, T deobf, Token token ) + public EntryPair( T obf, T deobf ) { this.obf = obf; this.deobf = deobf; - this.token = token; } } diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 2a39057a..4dff6935 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -17,7 +17,7 @@ import java.io.Serializable; import java.util.Map; import java.util.zip.GZIPInputStream; -import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Maps; import cuchaz.enigma.Util; -- cgit v1.2.3 From 044f109bfa9ae55430456b1cf21ed56a3902bff2 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 10 Aug 2014 16:58:49 -0400 Subject: fixed recognition of static initializers fixed identifier off-by-one error --- src/cuchaz/enigma/analysis/SourceIndex.java | 16 +- src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 271 +++++++++++---------- 2 files changed, 153 insertions(+), 134 deletions(-) diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 398a50d1..7981f879 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -57,10 +57,24 @@ public class SourceIndex { throw new IllegalArgumentException( "Invalid region: " + region ); } - return new Token( + Token token = new Token( toPos( region.getBeginLine(), region.getBeginColumn() ), toPos( region.getEndLine(), region.getEndColumn() ) ); + + // HACKHACK: sometimes node regions are off by one + // I think this is a bug in Procyon, but it's easy to work around + if( !Character.isJavaIdentifierStart( m_source.charAt( token.start ) ) ) + { + token.start++; + token.end++; + if( !Character.isJavaIdentifierStart( m_source.charAt( token.start ) ) ) + { + throw new IllegalArgumentException( "Region " + region + " does not describe valid token: '" + m_source.substring( token.start, token.end ) + "'" ); + } + } + + return token; } public void add( AstNode node, Entry entry ) diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java index 2a26c85f..5a64e4e4 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -42,7 +42,6 @@ import com.strobel.decompiler.languages.java.ast.ContinueStatement; import com.strobel.decompiler.languages.java.ast.DoWhileStatement; import com.strobel.decompiler.languages.java.ast.EmptyStatement; import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; -import com.strobel.decompiler.languages.java.ast.Expression; import com.strobel.decompiler.languages.java.ast.ExpressionStatement; import com.strobel.decompiler.languages.java.ast.FieldDeclaration; import com.strobel.decompiler.languages.java.ast.ForEachStatement; @@ -101,289 +100,344 @@ import cuchaz.enigma.mapping.MethodEntry; public class SourceIndexVisitor implements IAstVisitor { @Override - public Void visitComment( Comment node, SourceIndex index ) + public Void visitInvocationExpression( InvocationExpression node, SourceIndex index ) { + MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); + ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); + MethodEntry methodEntry = new MethodEntry( classEntry, ref.getName(), ref.getSignature() ); + if( node.getTarget() instanceof MemberReferenceExpression ) + { + index.add( ((MemberReferenceExpression)node.getTarget()).getMemberNameToken(), methodEntry ); + } + return recurse( node, index ); } @Override - public Void visitPatternPlaceholder( AstNode node, Pattern pattern, SourceIndex index ) + public Void visitMemberReferenceExpression( MemberReferenceExpression node, SourceIndex index ) { + MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); + if( ref != null ) + { + ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); + FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); + index.add( node.getMemberNameToken(), fieldEntry ); + } + return recurse( node, index ); } @Override - public Void visitInvocationExpression( InvocationExpression node, SourceIndex index ) + public Void visitSimpleType( SimpleType node, SourceIndex index ) { - MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); - ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); - MethodEntry methodEntry = new MethodEntry( classEntry, ref.getName(), ref.getSignature() ); - if( node.getTarget() instanceof MemberReferenceExpression ) + TypeReference ref = node.getUserData( Keys.TYPE_REFERENCE ); + if( node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY ) { - index.add( ((MemberReferenceExpression)node.getTarget()).getMemberNameToken(), methodEntry ); + index.add( node.getIdentifierToken(), new ClassEntry( ref.getInternalName() ) ); } return recurse( node, index ); } @Override - public Void visitTypeReference( TypeReferenceExpression node, SourceIndex index ) + public Void visitMethodDeclaration( MethodDeclaration node, SourceIndex index ) { + MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); + + // static initializers don't have identifier tokens + if( !def.getName().equals( "" ) ) + { + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + MethodEntry methodEntry = new MethodEntry( classEntry, def.getName(), def.getSignature() ); + index.add( node.getNameToken(), methodEntry ); + } + return recurse( node, index ); } @Override - public Void visitJavaTokenNode( JavaTokenNode node, SourceIndex index ) + public Void visitConstructorDeclaration( ConstructorDeclaration node, SourceIndex index ) { + MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); + index.add( node.getNameToken(), new ClassEntry( def.getDeclaringType().getInternalName() ) ); + return recurse( node, index ); } @Override - public Void visitMemberReferenceExpression( MemberReferenceExpression node, SourceIndex index ) + public Void visitParameterDeclaration( ParameterDeclaration node, SourceIndex index ) { - MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); - if( ref != null ) - { - ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); - FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); - index.add( node.getMemberNameToken(), fieldEntry ); - } + ParameterDefinition def = node.getUserData( Keys.PARAMETER_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + MethodDefinition methodDef = (MethodDefinition)def.getMethod(); + MethodEntry methodEntry = new MethodEntry( classEntry, methodDef.getName(), methodDef.getSignature() ); + ArgumentEntry argumentEntry = new ArgumentEntry( methodEntry, def.getPosition(), def.getName() ); + index.add( node.getNameToken(), argumentEntry ); return recurse( node, index ); } @Override - public Void visitIdentifier( Identifier node, SourceIndex index ) + public Void visitFieldDeclaration( FieldDeclaration node, SourceIndex index ) { + FieldDefinition def = node.getUserData( Keys.FIELD_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + FieldEntry fieldEntry = new FieldEntry( classEntry, def.getName() ); + assert( node.getVariables().size() == 1 ); + VariableInitializer variable = node.getVariables().firstOrNullObject(); + index.add( variable.getNameToken(), fieldEntry ); + return recurse( node, index ); } @Override - public Void visitNullReferenceExpression( NullReferenceExpression node, SourceIndex index ) + public Void visitTypeDeclaration( TypeDeclaration node, SourceIndex index ) { + TypeDefinition def = node.getUserData( Keys.TYPE_DEFINITION ); + index.add( node.getNameToken(), new ClassEntry( def.getInternalName() ) ); + return recurse( node, index ); } + private Void recurse( AstNode node, SourceIndex index ) + { + // TEMP: show the tree + System.out.println( getIndent( node ) + node.getClass().getSimpleName() + dumpUserData( node ) + " " + node.getRegion() ); + + for( final AstNode child : node.getChildren() ) + { + child.acceptVisitor( this, index ); + } + return null; + } + + private String dumpUserData( AstNode node ) + { + StringBuilder buf = new StringBuilder(); + for( Key key : Keys.ALL_KEYS ) + { + Object val = node.getUserData( key ); + if( val != null ) + { + buf.append( String.format( " [%s=%s]", key, val ) ); + } + } + return buf.toString(); + } + + private String getIndent( AstNode node ) + { + StringBuilder buf = new StringBuilder(); + int depth = getDepth( node ); + for( int i = 0; i < depth; i++ ) + { + buf.append( "\t" ); + } + return buf.toString(); + } + + private int getDepth( AstNode node ) + { + int depth = -1; + while( node != null ) + { + depth++; + node = node.getParent(); + } + return depth; + } + + // OVERRIDES WE DON'T CARE ABOUT + @Override - public Void visitThisReferenceExpression( ThisReferenceExpression node, SourceIndex index ) + public Void visitComment( Comment node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitSuperReferenceExpression( SuperReferenceExpression node, SourceIndex index ) + public Void visitPatternPlaceholder( AstNode node, Pattern pattern, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitClassOfExpression( ClassOfExpression node, SourceIndex index ) + public Void visitTypeReference( TypeReferenceExpression node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitBlockStatement( BlockStatement node, SourceIndex index ) + public Void visitJavaTokenNode( JavaTokenNode node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitExpressionStatement( ExpressionStatement node, SourceIndex index ) + public Void visitIdentifier( Identifier node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitBreakStatement( BreakStatement node, SourceIndex index ) + public Void visitNullReferenceExpression( NullReferenceExpression node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitContinueStatement( ContinueStatement node, SourceIndex index ) + public Void visitThisReferenceExpression( ThisReferenceExpression node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitDoWhileStatement( DoWhileStatement node, SourceIndex index ) + public Void visitSuperReferenceExpression( SuperReferenceExpression node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitEmptyStatement( EmptyStatement node, SourceIndex index ) + public Void visitClassOfExpression( ClassOfExpression node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitIfElseStatement( IfElseStatement node, SourceIndex index ) + public Void visitBlockStatement( BlockStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitLabelStatement( LabelStatement node, SourceIndex index ) + public Void visitExpressionStatement( ExpressionStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitLabeledStatement( LabeledStatement node, SourceIndex index ) + public Void visitBreakStatement( BreakStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitReturnStatement( ReturnStatement node, SourceIndex index ) + public Void visitContinueStatement( ContinueStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitSwitchStatement( SwitchStatement node, SourceIndex index ) + public Void visitDoWhileStatement( DoWhileStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitSwitchSection( SwitchSection node, SourceIndex index ) + public Void visitEmptyStatement( EmptyStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitCaseLabel( CaseLabel node, SourceIndex index ) + public Void visitIfElseStatement( IfElseStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitThrowStatement( ThrowStatement node, SourceIndex index ) + public Void visitLabelStatement( LabelStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitCatchClause( CatchClause node, SourceIndex index ) + public Void visitLabeledStatement( LabeledStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitAnnotation( Annotation node, SourceIndex index ) + public Void visitReturnStatement( ReturnStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitNewLine( NewLineNode node, SourceIndex index ) + public Void visitSwitchStatement( SwitchStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitVariableDeclaration( VariableDeclarationStatement node, SourceIndex index ) + public Void visitSwitchSection( SwitchSection node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitVariableInitializer( VariableInitializer node, SourceIndex index ) + public Void visitCaseLabel( CaseLabel node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitText( TextNode node, SourceIndex index ) + public Void visitThrowStatement( ThrowStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitImportDeclaration( ImportDeclaration node, SourceIndex index ) + public Void visitCatchClause( CatchClause node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitSimpleType( SimpleType node, SourceIndex index ) + public Void visitAnnotation( Annotation node, SourceIndex index ) { - TypeReference ref = node.getUserData( Keys.TYPE_REFERENCE ); - if( node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY ) - { - index.add( node.getIdentifierToken(), new ClassEntry( ref.getInternalName() ) ); - } - return recurse( node, index ); } @Override - public Void visitMethodDeclaration( MethodDeclaration node, SourceIndex index ) + public Void visitNewLine( NewLineNode node, SourceIndex index ) { - MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - MethodEntry methodEntry = new MethodEntry( classEntry, def.getName(), def.getSignature() ); - index.add( node.getNameToken(), methodEntry ); - return recurse( node, index ); } @Override - public Void visitInitializerBlock( InstanceInitializer node, SourceIndex index ) + public Void visitVariableDeclaration( VariableDeclarationStatement node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitConstructorDeclaration( ConstructorDeclaration node, SourceIndex index ) + public Void visitVariableInitializer( VariableInitializer node, SourceIndex index ) { - MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); - index.add( node.getNameToken(), new ClassEntry( def.getDeclaringType().getInternalName() ) ); - return recurse( node, index ); } @Override - public Void visitTypeParameterDeclaration( TypeParameterDeclaration node, SourceIndex index ) + public Void visitText( TextNode node, SourceIndex index ) { return recurse( node, index ); } @Override - public Void visitParameterDeclaration( ParameterDeclaration node, SourceIndex index ) + public Void visitImportDeclaration( ImportDeclaration node, SourceIndex index ) { - ParameterDefinition def = node.getUserData( Keys.PARAMETER_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - MethodDefinition methodDef = (MethodDefinition)def.getMethod(); - MethodEntry methodEntry = new MethodEntry( classEntry, methodDef.getName(), methodDef.getSignature() ); - ArgumentEntry argumentEntry = new ArgumentEntry( methodEntry, def.getPosition(), def.getName() ); - index.add( node.getNameToken(), argumentEntry ); - return recurse( node, index ); } @Override - public Void visitFieldDeclaration( FieldDeclaration node, SourceIndex index ) + public Void visitInitializerBlock( InstanceInitializer node, SourceIndex index ) { - FieldDefinition def = node.getUserData( Keys.FIELD_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - FieldEntry fieldEntry = new FieldEntry( classEntry, def.getName() ); - assert( node.getVariables().size() == 1 ); - VariableInitializer variable = node.getVariables().firstOrNullObject(); - index.add( variable.getNameToken(), fieldEntry ); - return recurse( node, index ); } @Override - public Void visitTypeDeclaration( TypeDeclaration node, SourceIndex index ) + public Void visitTypeParameterDeclaration( TypeParameterDeclaration node, SourceIndex index ) { - TypeDefinition def = node.getUserData( Keys.TYPE_DEFINITION ); - index.add( node.getNameToken(), new ClassEntry( def.getInternalName() ) ); - return recurse( node, index ); } @@ -450,7 +504,6 @@ public class SourceIndexVisitor implements IAstVisitor @Override public Void visitIdentifierExpression( IdentifierExpression node, SourceIndex index ) { - // TODO return recurse( node, index ); } @@ -567,52 +620,4 @@ public class SourceIndexVisitor implements IAstVisitor { return recurse( node, index ); } - - private Void recurse( AstNode node, SourceIndex index ) - { - // TEMP: show the tree - System.out.println( getIndent( node ) + node.getClass().getSimpleName() + dumpUserData( node ) + " " + node.getRegion() ); - - for( final AstNode child : node.getChildren() ) - { - child.acceptVisitor( this, index ); - } - return null; - } - - private String dumpUserData( AstNode node ) - { - StringBuilder buf = new StringBuilder(); - for( Key key : Keys.ALL_KEYS ) - { - Object val = node.getUserData( key ); - if( val != null ) - { - buf.append( String.format( " [%s=%s]", key, val ) ); - } - } - return buf.toString(); - } - - private String getIndent( AstNode node ) - { - StringBuilder buf = new StringBuilder(); - int depth = getDepth( node ); - for( int i = 0; i < depth; i++ ) - { - buf.append( "\t" ); - } - return buf.toString(); - } - - private int getDepth( AstNode node ) - { - int depth = -1; - while( node != null ) - { - depth++; - node = node.getParent(); - } - return depth; - } } -- cgit v1.2.3 From 417689c02241ae4b44d4a892c442689bb053a436 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 10 Aug 2014 17:24:14 -0400 Subject: improved keyboard shortcuts --- src/cuchaz/enigma/gui/Gui.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 187852a2..79becf86 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -19,6 +19,7 @@ import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.InputEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; @@ -50,6 +51,7 @@ import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.JTree; +import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; import javax.swing.WindowConstants; import javax.swing.event.CaretEvent; @@ -254,6 +256,10 @@ public class Gui case KeyEvent.VK_R: startRename(); break; + + case KeyEvent.VK_O: + openEntry(); + break; } } } ); @@ -275,6 +281,7 @@ public class Gui startRename(); } } ); + menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_R, 0 ) ); popupMenu.add( menu ); m_renameMenu = menu; } @@ -301,6 +308,7 @@ public class Gui openEntry(); } } ); + menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_O, 0 ) ); popupMenu.add( menu ); m_openEntryMenu = menu; } @@ -427,6 +435,7 @@ public class Gui } } } ); + item.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK ) ); m_saveMappingsMenu = item; } { @@ -451,6 +460,7 @@ public class Gui } } } ); + item.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK ) ); m_saveMappingsAsMenu = item; } { -- cgit v1.2.3 From 1ea27e61b915e944f6015d1711b89b9809ddff9f Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 10 Aug 2014 18:12:20 -0400 Subject: filter out tokens that are not obfuscated --- src/cuchaz/enigma/Deobfuscator.java | 77 ++++++++----------------------------- 1 file changed, 16 insertions(+), 61 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 8eda889f..16c11d3f 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -22,22 +22,15 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import com.google.common.collect.Lists; -import com.strobel.assembler.metadata.MemberReference; import com.strobel.assembler.metadata.MetadataSystem; import com.strobel.assembler.metadata.TypeDefinition; -import com.strobel.componentmodel.Key; import com.strobel.decompiler.DecompilerContext; import com.strobel.decompiler.DecompilerSettings; import com.strobel.decompiler.PlainTextOutput; import com.strobel.decompiler.languages.java.JavaOutputVisitor; import com.strobel.decompiler.languages.java.ast.AstBuilder; -import com.strobel.decompiler.languages.java.ast.AstNode; import com.strobel.decompiler.languages.java.ast.CompilationUnit; -import com.strobel.decompiler.languages.java.ast.Identifier; import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; -import com.strobel.decompiler.languages.java.ast.InvocationExpression; -import com.strobel.decompiler.languages.java.ast.Keys; -import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; @@ -203,58 +196,6 @@ public class Deobfuscator return index; } - private void dump( AstNode node, int depth ) - { - StringBuilder buf = new StringBuilder(); - for( int i=0; i %s)", memberRef.getDeclaringType(), memberRef.getName(), memberRef.getSignature() ) ); - } - - for( Key key : Keys.ALL_KEYS ) - { - if( key == Keys.MEMBER_REFERENCE ) - { - continue; - } - Object val = node.getUserData( key ); - if( val != null ) - { - buf.append( String.format( " (%s=%s)", key, val ) ); - } - } - - - if( node instanceof Identifier ) - { - Identifier n = (Identifier)node; - buf.append( ": " + n.getName() ); - } - else if( node instanceof MemberReferenceExpression ) - { - MemberReferenceExpression n = (MemberReferenceExpression)node; - buf.append( ": " + n.getTarget() + "." + n.getMemberName() ); - } - else if( node instanceof InvocationExpression ) - { - - } - - System.out.println( buf ); - - for( AstNode child : node.getChildren() ) - { - dump( child, depth + 1 ); - } - } - // NOTE: these methods are a bit messy... oh well public void rename( Entry obfEntry, String newName ) @@ -366,8 +307,22 @@ public class Deobfuscator // obf classes must be in the list return m_obfClassNames.contains( obfEntry.getName() ); } + else if( obfEntry instanceof FieldEntry ) + { + return m_obfClassNames.contains( ((FieldEntry)obfEntry).getClassName() ); + } + else if( obfEntry instanceof MethodEntry ) + { + return m_obfClassNames.contains( ((MethodEntry)obfEntry).getClassName() ); + } + else if( obfEntry instanceof ArgumentEntry ) + { + // arguments only appear in method delcarations + // since we only show declrations for obf classes, these are always obfuscated + return true; + } - // assume everything else is an identifier - return true; + // assume everything else is not obfuscated + return false; } } -- cgit v1.2.3 From e7febe4549c9fcdf1e82239959b3c6a83fad8934 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 10 Aug 2014 19:29:43 -0400 Subject: added go to entry feature --- src/cuchaz/enigma/analysis/SourceIndex.java | 11 +- src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 48 +- src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 550 +++++++++++++++++++++ src/cuchaz/enigma/gui/Gui.java | 52 +- src/cuchaz/enigma/gui/GuiController.java | 47 +- src/cuchaz/enigma/mapping/ArgumentEntry.java | 1 + src/cuchaz/enigma/mapping/ClassEntry.java | 6 + src/cuchaz/enigma/mapping/Entry.java | 1 + src/cuchaz/enigma/mapping/FieldEntry.java | 1 + src/cuchaz/enigma/mapping/MethodEntry.java | 1 + 10 files changed, 621 insertions(+), 97 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/TreeDumpVisitor.java diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 7981f879..ad94cf00 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -25,12 +25,14 @@ public class SourceIndex { private String m_source; private TreeMap m_tokens; + private Map m_declarations; private List m_lineOffsets; public SourceIndex( String source ) { m_source = source; m_tokens = Maps.newTreeMap(); + m_declarations = Maps.newHashMap(); m_lineOffsets = Lists.newArrayList(); // count the lines @@ -82,9 +84,11 @@ public class SourceIndex m_tokens.put( getToken( node ), entry ); } - public void add( Token token, Entry entry ) + public void addDeclaration( AstNode node, Entry entry ) { + Token token = getToken( node ); m_tokens.put( token, entry ); + m_declarations.put( entry, token ); } public Token getToken( int pos ) @@ -116,6 +120,11 @@ public class SourceIndex return m_tokens.keySet(); } + public Token getDeclarationToken( Entry entry ) + { + return m_declarations.get( entry ); + } + private int toPos( int line, int col ) { // line and col are 1-based diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java index 5a64e4e4..0ba5996c 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -16,7 +16,6 @@ import com.strobel.assembler.metadata.MethodDefinition; import com.strobel.assembler.metadata.ParameterDefinition; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.assembler.metadata.TypeReference; -import com.strobel.componentmodel.Key; import com.strobel.decompiler.languages.TextLocation; import com.strobel.decompiler.languages.java.ast.Annotation; import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; @@ -149,7 +148,7 @@ public class SourceIndexVisitor implements IAstVisitor { ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); MethodEntry methodEntry = new MethodEntry( classEntry, def.getName(), def.getSignature() ); - index.add( node.getNameToken(), methodEntry ); + index.addDeclaration( node.getNameToken(), methodEntry ); } return recurse( node, index ); @@ -172,7 +171,7 @@ public class SourceIndexVisitor implements IAstVisitor MethodDefinition methodDef = (MethodDefinition)def.getMethod(); MethodEntry methodEntry = new MethodEntry( classEntry, methodDef.getName(), methodDef.getSignature() ); ArgumentEntry argumentEntry = new ArgumentEntry( methodEntry, def.getPosition(), def.getName() ); - index.add( node.getNameToken(), argumentEntry ); + index.addDeclaration( node.getNameToken(), argumentEntry ); return recurse( node, index ); } @@ -185,7 +184,7 @@ public class SourceIndexVisitor implements IAstVisitor FieldEntry fieldEntry = new FieldEntry( classEntry, def.getName() ); assert( node.getVariables().size() == 1 ); VariableInitializer variable = node.getVariables().firstOrNullObject(); - index.add( variable.getNameToken(), fieldEntry ); + index.addDeclaration( variable.getNameToken(), fieldEntry ); return recurse( node, index ); } @@ -194,16 +193,13 @@ public class SourceIndexVisitor implements IAstVisitor public Void visitTypeDeclaration( TypeDeclaration node, SourceIndex index ) { TypeDefinition def = node.getUserData( Keys.TYPE_DEFINITION ); - index.add( node.getNameToken(), new ClassEntry( def.getInternalName() ) ); + index.addDeclaration( node.getNameToken(), new ClassEntry( def.getInternalName() ) ); return recurse( node, index ); } private Void recurse( AstNode node, SourceIndex index ) { - // TEMP: show the tree - System.out.println( getIndent( node ) + node.getClass().getSimpleName() + dumpUserData( node ) + " " + node.getRegion() ); - for( final AstNode child : node.getChildren() ) { child.acceptVisitor( this, index ); @@ -211,42 +207,6 @@ public class SourceIndexVisitor implements IAstVisitor return null; } - private String dumpUserData( AstNode node ) - { - StringBuilder buf = new StringBuilder(); - for( Key key : Keys.ALL_KEYS ) - { - Object val = node.getUserData( key ); - if( val != null ) - { - buf.append( String.format( " [%s=%s]", key, val ) ); - } - } - return buf.toString(); - } - - private String getIndent( AstNode node ) - { - StringBuilder buf = new StringBuilder(); - int depth = getDepth( node ); - for( int i = 0; i < depth; i++ ) - { - buf.append( "\t" ); - } - return buf.toString(); - } - - private int getDepth( AstNode node ) - { - int depth = -1; - while( node != null ) - { - depth++; - node = node.getParent(); - } - return depth; - } - // OVERRIDES WE DON'T CARE ABOUT @Override diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java new file mode 100644 index 00000000..32607db9 --- /dev/null +++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -0,0 +1,550 @@ +package cuchaz.enigma.analysis; + +import com.strobel.componentmodel.Key; +import com.strobel.decompiler.languages.java.ast.Annotation; +import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; +import com.strobel.decompiler.languages.java.ast.ArraySpecifier; +import com.strobel.decompiler.languages.java.ast.AssertStatement; +import com.strobel.decompiler.languages.java.ast.AssignmentExpression; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.BinaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.BlockStatement; +import com.strobel.decompiler.languages.java.ast.BreakStatement; +import com.strobel.decompiler.languages.java.ast.CaseLabel; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.CatchClause; +import com.strobel.decompiler.languages.java.ast.ClassOfExpression; +import com.strobel.decompiler.languages.java.ast.Comment; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; +import com.strobel.decompiler.languages.java.ast.ComposedType; +import com.strobel.decompiler.languages.java.ast.ConditionalExpression; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.ContinueStatement; +import com.strobel.decompiler.languages.java.ast.DoWhileStatement; +import com.strobel.decompiler.languages.java.ast.EmptyStatement; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.ExpressionStatement; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.ForEachStatement; +import com.strobel.decompiler.languages.java.ast.ForStatement; +import com.strobel.decompiler.languages.java.ast.GotoStatement; +import com.strobel.decompiler.languages.java.ast.IAstVisitor; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.IfElseStatement; +import com.strobel.decompiler.languages.java.ast.ImportDeclaration; +import com.strobel.decompiler.languages.java.ast.IndexerExpression; +import com.strobel.decompiler.languages.java.ast.InstanceInitializer; +import com.strobel.decompiler.languages.java.ast.InstanceOfExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.JavaTokenNode; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.LabelStatement; +import com.strobel.decompiler.languages.java.ast.LabeledStatement; +import com.strobel.decompiler.languages.java.ast.LambdaExpression; +import com.strobel.decompiler.languages.java.ast.LocalTypeDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.MethodGroupExpression; +import com.strobel.decompiler.languages.java.ast.NewLineNode; +import com.strobel.decompiler.languages.java.ast.NullReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.PackageDeclaration; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.ParenthesizedExpression; +import com.strobel.decompiler.languages.java.ast.PrimitiveExpression; +import com.strobel.decompiler.languages.java.ast.ReturnStatement; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.SwitchSection; +import com.strobel.decompiler.languages.java.ast.SwitchStatement; +import com.strobel.decompiler.languages.java.ast.SynchronizedStatement; +import com.strobel.decompiler.languages.java.ast.TextNode; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThrowStatement; +import com.strobel.decompiler.languages.java.ast.TryCatchStatement; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; +import com.strobel.decompiler.languages.java.ast.UnaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; +import com.strobel.decompiler.languages.java.ast.WhileStatement; +import com.strobel.decompiler.languages.java.ast.WildcardType; +import com.strobel.decompiler.patterns.Pattern; + +public class TreeDumpVisitor implements IAstVisitor +{ + private Void recurse( AstNode node, Void ignored ) + { + // show the tree + System.out.println( getIndent( node ) + node.getClass().getSimpleName() + dumpUserData( node ) + " " + node.getRegion() ); + + // recurse + for( final AstNode child : node.getChildren() ) + { + child.acceptVisitor( this, ignored ); + } + return null; + } + + private String dumpUserData( AstNode node ) + { + StringBuilder buf = new StringBuilder(); + for( Key key : Keys.ALL_KEYS ) + { + Object val = node.getUserData( key ); + if( val != null ) + { + buf.append( String.format( " [%s=%s]", key, val ) ); + } + } + return buf.toString(); + } + + private String getIndent( AstNode node ) + { + StringBuilder buf = new StringBuilder(); + int depth = getDepth( node ); + for( int i = 0; i < depth; i++ ) + { + buf.append( "\t" ); + } + return buf.toString(); + } + + private int getDepth( AstNode node ) + { + int depth = -1; + while( node != null ) + { + depth++; + node = node.getParent(); + } + return depth; + } + + // OVERRIDES WE DON'T CARE ABOUT + + @Override + public Void visitInvocationExpression( InvocationExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitMemberReferenceExpression( MemberReferenceExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitSimpleType( SimpleType node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitMethodDeclaration( MethodDeclaration node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitConstructorDeclaration( ConstructorDeclaration node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitParameterDeclaration( ParameterDeclaration node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitFieldDeclaration( FieldDeclaration node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitTypeDeclaration( TypeDeclaration node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitComment( Comment node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitPatternPlaceholder( AstNode node, Pattern pattern, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitTypeReference( TypeReferenceExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitJavaTokenNode( JavaTokenNode node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitIdentifier( Identifier node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitNullReferenceExpression( NullReferenceExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitThisReferenceExpression( ThisReferenceExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitSuperReferenceExpression( SuperReferenceExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitClassOfExpression( ClassOfExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitBlockStatement( BlockStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitExpressionStatement( ExpressionStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitBreakStatement( BreakStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitContinueStatement( ContinueStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitDoWhileStatement( DoWhileStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitEmptyStatement( EmptyStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitIfElseStatement( IfElseStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitLabelStatement( LabelStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitLabeledStatement( LabeledStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitReturnStatement( ReturnStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitSwitchStatement( SwitchStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitSwitchSection( SwitchSection node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitCaseLabel( CaseLabel node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitThrowStatement( ThrowStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitCatchClause( CatchClause node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitAnnotation( Annotation node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitNewLine( NewLineNode node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitVariableDeclaration( VariableDeclarationStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitVariableInitializer( VariableInitializer node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitText( TextNode node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitImportDeclaration( ImportDeclaration node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitInitializerBlock( InstanceInitializer node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitTypeParameterDeclaration( TypeParameterDeclaration node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitCompilationUnit( CompilationUnit node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitPackageDeclaration( PackageDeclaration node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitArraySpecifier( ArraySpecifier node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitComposedType( ComposedType node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitWhileStatement( WhileStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitPrimitiveExpression( PrimitiveExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitCastExpression( CastExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitBinaryOperatorExpression( BinaryOperatorExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitInstanceOfExpression( InstanceOfExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitIndexerExpression( IndexerExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitIdentifierExpression( IdentifierExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitUnaryOperatorExpression( UnaryOperatorExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitConditionalExpression( ConditionalExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitArrayInitializerExpression( ArrayInitializerExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitObjectCreationExpression( ObjectCreationExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitArrayCreationExpression( ArrayCreationExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitAssignmentExpression( AssignmentExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitForStatement( ForStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitForEachStatement( ForEachStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitTryCatchStatement( TryCatchStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitGotoStatement( GotoStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitParenthesizedExpression( ParenthesizedExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitSynchronizedStatement( SynchronizedStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitAnonymousObjectCreationExpression( AnonymousObjectCreationExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitWildcardType( WildcardType node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitMethodGroupExpression( MethodGroupExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitEnumValueDeclaration( EnumValueDeclaration node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitAssertStatement( AssertStatement node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitLambdaExpression( LambdaExpression node, Void ignored ) + { + return recurse( node, ignored ); + } + + @Override + public Void visitLocalTypeDeclarationStatement( LocalTypeDeclarationStatement node, Void ignored ) + { + return recurse( node, ignored ); + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 79becf86..bf72c85d 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -63,7 +63,6 @@ import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import jsyntaxpane.DefaultSyntaxKit; -import jsyntaxpane.SyntaxDocument; import com.google.common.collect.Lists; @@ -257,6 +256,10 @@ public class Gui startRename(); break; + case KeyEvent.VK_I: + showInheritance(); + break; + case KeyEvent.VK_O: openEntry(); break; @@ -296,10 +299,11 @@ public class Gui } } ); popupMenu.add( menu ); + menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_I, 0 ) ); m_inheritanceMenu = menu; } { - JMenuItem menu = new JMenuItem( "Open Class" ); + JMenuItem menu = new JMenuItem( "Go to Declaration" ); menu.addActionListener( new ActionListener( ) { @Override @@ -598,34 +602,13 @@ public class Gui public void setSource( String source ) { - setSource( source, 0 ); + m_editor.getHighlighter().removeAllHighlights(); + m_editor.setText( source ); } - public void setSource( String source, int lineNum ) + public void showToken( Token token ) { - // remove any old highlighters - m_editor.getHighlighter().removeAllHighlights(); - - m_editor.setText( source ); - - // count the offset of the target line - String text = m_editor.getText(); - int pos = 0; - int numLines = 0; - for( ; pos < text.length(); pos++ ) - { - if( numLines == lineNum ) - { - break; - } - if( text.charAt( pos ) == '\n' ) - { - numLines++; - } - } - - // put the caret at the line number - m_editor.setCaretPosition( pos ); + m_editor.setCaretPosition( token.start ); m_editor.grabFocus(); } @@ -760,12 +743,13 @@ public class Gui m_selectedEntryPair = m_controller.getEntryPair( token ); boolean isClassEntry = m_selectedEntryPair.obf instanceof ClassEntry; + boolean isFieldEntry = m_selectedEntryPair.obf instanceof FieldEntry; boolean isMethodEntry = m_selectedEntryPair.obf instanceof MethodEntry; showEntryPair( m_selectedEntryPair ); m_inheritanceMenu.setEnabled( isClassEntry || isMethodEntry ); - m_openEntryMenu.setEnabled( isClassEntry ); + m_openEntryMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry ); } private void startRename( ) @@ -807,12 +791,9 @@ public class Gui String newName = text.getText(); if( saveName && newName != null && newName.length() > 0 ) { - SyntaxDocument doc = (SyntaxDocument)m_editor.getDocument(); - int lineNum = doc.getLineNumberAt( m_editor.getCaretPosition() ); try { - // TODO: give token to the controller so we can put the caret back there - m_controller.rename( m_selectedEntryPair.obf, newName, lineNum ); + m_controller.rename( m_selectedEntryPair.obf, newName ); } catch( IllegalNameException ex ) { @@ -869,12 +850,7 @@ public class Gui { return; } - - // get the current class - if( m_selectedEntryPair.obf instanceof ClassEntry ) - { - m_controller.deobfuscateClass( new ClassFile( m_selectedEntryPair.obf.getName() ) ); - } + m_controller.openEntry( m_selectedEntryPair.obf ); } private void close( ) diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index e0aad860..834afecc 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -77,7 +77,7 @@ public class GuiController m_isDirty = false; m_gui.setMappingsFile( file ); refreshClasses(); - refreshOpenFiles(); + refreshCurrentClass(); } public void saveMappings( File file ) @@ -94,7 +94,7 @@ public class GuiController m_deobfuscator.setMappings( null ); m_gui.setMappingsFile( null ); refreshClasses(); - refreshOpenFiles(); + refreshCurrentClass(); } public void deobfuscateClass( ClassFile classFile ) @@ -157,12 +157,26 @@ public class GuiController return thisNode; } - public void rename( Entry obfsEntry, String newName, int lineNum ) + public void rename( Entry obfEntry, String newName ) { - m_deobfuscator.rename( obfsEntry, newName ); + m_deobfuscator.rename( obfEntry, newName ); m_isDirty = true; refreshClasses(); - refreshOpenFiles( lineNum ); + refreshCurrentClass( m_deobfuscator.deobfuscateEntry( obfEntry ) ); + } + + public void openEntry( Entry obfEntry ) + { + Entry deobfEntry = m_deobfuscator.deobfuscateEntry( obfEntry ); + if( !m_currentFile.getName().equals( obfEntry.getClassName() ) ) + { + m_currentFile = new ClassFile( obfEntry.getClassName() ); + deobfuscate( m_currentFile, deobfEntry ); + } + else + { + m_gui.showToken( m_index.getDeclarationToken( deobfEntry ) ); + } } private void refreshClasses( ) @@ -173,27 +187,28 @@ public class GuiController m_gui.setObfClasses( obfClasses ); m_gui.setDeobfClasses( deobfClasses ); } - - private void refreshOpenFiles( ) + + private void refreshCurrentClass( ) { - refreshOpenFiles( 0 ); + refreshCurrentClass( null ); } - private void refreshOpenFiles( int lineNum ) + private void refreshCurrentClass( Entry entryToShow ) { if( m_currentFile != null ) { - deobfuscate( m_currentFile, lineNum ); + deobfuscate( m_currentFile, entryToShow ); } } private void deobfuscate( final ClassFile classFile ) { - deobfuscate( classFile, 0 ); + deobfuscate( classFile, null ); } - private void deobfuscate( final ClassFile classFile, final int lineNum ) + private void deobfuscate( final ClassFile classFile, final Entry entryToShow ) { + m_currentFile = classFile; m_gui.setSource( "(deobfuscating...)" ); // run the deobfuscator in a separate thread so we don't block the GUI event queue @@ -202,9 +217,13 @@ public class GuiController @Override public void run( ) { - // decopmile,deobfuscate the bytecode + // decompile,deobfuscate the bytecode m_index = m_deobfuscator.getSource( classFile ); - m_gui.setSource( m_index.getSource(), lineNum ); + m_gui.setSource( m_index.getSource() ); + if( entryToShow != null ) + { + m_gui.showToken( m_index.getDeclarationToken( entryToShow ) ); + } // set the highlighted tokens List obfuscatedTokens = Lists.newArrayList(); diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java index c1624a83..0c25c4d3 100644 --- a/src/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -70,6 +70,7 @@ public class ArgumentEntry implements Entry, Serializable return m_methodEntry.getClassEntry(); } + @Override public String getClassName( ) { return m_methodEntry.getClassName(); diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 0968e955..513862d9 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -44,6 +44,12 @@ public class ClassEntry implements Entry, Serializable return m_name; } + @Override + public String getClassName( ) + { + return m_name; + } + @Override public int hashCode( ) { diff --git a/src/cuchaz/enigma/mapping/Entry.java b/src/cuchaz/enigma/mapping/Entry.java index 14435324..3ff80276 100644 --- a/src/cuchaz/enigma/mapping/Entry.java +++ b/src/cuchaz/enigma/mapping/Entry.java @@ -13,4 +13,5 @@ package cuchaz.enigma.mapping; public interface Entry { String getName( ); + String getClassName( ); } diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java index eefc4c4c..6148c84a 100644 --- a/src/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -60,6 +60,7 @@ public class FieldEntry implements Entry, Serializable return m_name; } + @Override public String getClassName( ) { return m_classEntry.getName(); diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java index 9ea2d08e..ff232c59 100644 --- a/src/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -72,6 +72,7 @@ public class MethodEntry implements Entry, Serializable return m_signature; } + @Override public String getClassName( ) { return m_classEntry.getName(); -- cgit v1.2.3 From 22b6c861df68557352fb5a87948f3e065f395ef3 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 10 Aug 2014 22:13:01 -0400 Subject: refactored to remove ClassFile class to prep for upcoming stack navigation. It wasn't really necessary anymore. --- src/cuchaz/enigma/ClassFile.java | 41 -------------- src/cuchaz/enigma/Deobfuscator.java | 25 ++++----- .../enigma/gui/ClassInheritanceTreeNode.java | 2 +- src/cuchaz/enigma/gui/ClassListCellRenderer.java | 38 +++++++++++++ src/cuchaz/enigma/gui/ClassSelectionListener.java | 18 ------- .../gui/DeobfuscatedClassListCellRenderer.java | 43 --------------- src/cuchaz/enigma/gui/Gui.java | 63 +++++++++++----------- src/cuchaz/enigma/gui/GuiController.java | 37 ++++--------- .../gui/ObfuscatedClassListCellRenderer.java | 42 --------------- src/cuchaz/enigma/mapping/ArgumentEntry.java | 1 + src/cuchaz/enigma/mapping/ClassEntry.java | 6 +++ src/cuchaz/enigma/mapping/Entry.java | 1 + src/cuchaz/enigma/mapping/FieldEntry.java | 1 + src/cuchaz/enigma/mapping/MethodEntry.java | 1 + 14 files changed, 101 insertions(+), 218 deletions(-) delete mode 100644 src/cuchaz/enigma/ClassFile.java create mode 100644 src/cuchaz/enigma/gui/ClassListCellRenderer.java delete mode 100644 src/cuchaz/enigma/gui/ClassSelectionListener.java delete mode 100644 src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java delete mode 100644 src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java diff --git a/src/cuchaz/enigma/ClassFile.java b/src/cuchaz/enigma/ClassFile.java deleted file mode 100644 index 043558d0..00000000 --- a/src/cuchaz/enigma/ClassFile.java +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma; - - -public class ClassFile -{ - private String m_name; - - public ClassFile( String name ) - { - if( name.indexOf( '.' ) >= 0 ) - { - throw new IllegalArgumentException( "Class name should be in JVM format!" ); - } - m_name = name; - } - - public String getName( ) - { - return m_name; - } - - public String getPath( ) - { - return m_name.replace( ".", "/" ) + ".class"; - } - - public boolean isInPackage( ) - { - return m_name.indexOf( '/' ) >= 0; - } -} diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 16c11d3f..8d4394cd 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -17,7 +17,6 @@ import java.io.InputStream; import java.io.StringWriter; import java.util.Enumeration; import java.util.List; -import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -139,43 +138,41 @@ public class Deobfuscator return m_mappings.getTranslator( m_ancestries, direction ); } - public void getSeparatedClasses( List obfClasses, Map deobfClasses ) + public void getSeparatedClasses( List obfClasses, List deobfClasses ) { for( String obfClassName : m_obfClassNames ) { - ClassFile classFile = new ClassFile( obfClassName ); - // separate the classes - ClassMapping classMapping = m_mappings.getClassByObf( classFile.getName() ); + ClassMapping classMapping = m_mappings.getClassByObf( obfClassName ); if( classMapping != null ) { - deobfClasses.put( classFile, classMapping.getDeobfName() ); + deobfClasses.add( classMapping.getDeobfName() ); } - else if( classFile.isInPackage() ) + else if( obfClassName.indexOf( '/' ) >= 0 ) { - deobfClasses.put( classFile, classFile.getName() ); + // this class is in a package and therefore is not obfuscated + deobfClasses.add( obfClassName ); } else { - obfClasses.add( classFile ); + obfClasses.add( obfClassName ); } } } - public SourceIndex getSource( final ClassFile classFile ) + public SourceIndex getSource( String className ) { // is this class deobfuscated? // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out // the decompiler only sees the deobfuscated class, so we need to load it by the deobfuscated name - String deobfName = classFile.getName(); - ClassMapping classMapping = m_mappings.getClassByObf( classFile.getName() ); + ClassMapping classMapping = m_mappings.getClassByObf( className ); if( classMapping != null ) { - deobfName = classMapping.getDeobfName(); + className = classMapping.getDeobfName(); } // decompile it! - TypeDefinition resolvedType = new MetadataSystem( m_settings.getTypeLoader() ).lookupType( deobfName ).resolve(); + TypeDefinition resolvedType = new MetadataSystem( m_settings.getTypeLoader() ).lookupType( className ).resolve(); DecompilerContext context = new DecompilerContext(); context.setCurrentType( resolvedType ); context.setSettings( m_settings ); diff --git a/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java index 61e582df..295bebe4 100644 --- a/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java +++ b/src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java @@ -62,7 +62,7 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode nodes.add( new ClassInheritanceTreeNode( m_deobfuscatingTranslator, subclassName ) ); } - // add then to this node + // add them to this node for( ClassInheritanceTreeNode node : nodes ) { this.add( node ); diff --git a/src/cuchaz/enigma/gui/ClassListCellRenderer.java b/src/cuchaz/enigma/gui/ClassListCellRenderer.java new file mode 100644 index 00000000..d9d65788 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassListCellRenderer.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Component; + +import javassist.bytecode.Descriptor; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +public class ClassListCellRenderer implements ListCellRenderer +{ + private DefaultListCellRenderer m_defaultRenderer; + + public ClassListCellRenderer( ) + { + m_defaultRenderer = new DefaultListCellRenderer(); + } + + @Override + public Component getListCellRendererComponent( JList list, String className, int index, boolean isSelected, boolean hasFocus ) + { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, className, index, isSelected, hasFocus ); + label.setText( Descriptor.toJavaName( className ) ); + return label; + } +} diff --git a/src/cuchaz/enigma/gui/ClassSelectionListener.java b/src/cuchaz/enigma/gui/ClassSelectionListener.java deleted file mode 100644 index 4a2aa8d0..00000000 --- a/src/cuchaz/enigma/gui/ClassSelectionListener.java +++ /dev/null @@ -1,18 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.gui; - -import cuchaz.enigma.ClassFile; - -public interface ClassSelectionListener -{ - void classSelected( ClassFile classFile ); -} diff --git a/src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java b/src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java deleted file mode 100644 index 3a8729d2..00000000 --- a/src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.gui; - -import java.awt.Component; -import java.util.Map; - -import javassist.bytecode.Descriptor; - -import javax.swing.DefaultListCellRenderer; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.ListCellRenderer; - -import cuchaz.enigma.ClassFile; - -public class DeobfuscatedClassListCellRenderer implements ListCellRenderer> -{ - private DefaultListCellRenderer m_defaultRenderer; - - public DeobfuscatedClassListCellRenderer( ) - { - m_defaultRenderer = new DefaultListCellRenderer(); - } - - @Override - public Component getListCellRendererComponent( JList> list, Map.Entry entry, int index, boolean isSelected, boolean hasFocus ) - { - JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, entry, index, isSelected, hasFocus ); - - label.setText( Descriptor.toJavaName( entry.getValue() ) ); - - return label; - } -} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index bf72c85d..6666b4c4 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -31,7 +31,6 @@ import java.io.IOException; import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.Vector; import javax.swing.BorderFactory; @@ -66,7 +65,6 @@ import jsyntaxpane.DefaultSyntaxKit; import com.google.common.collect.Lists; -import cuchaz.enigma.ClassFile; import cuchaz.enigma.Constants; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ArgumentEntry; @@ -79,35 +77,34 @@ import cuchaz.enigma.mapping.MethodEntry; public class Gui { - private static Comparator m_obfuscatedClassSorter; - private static Comparator> m_deobfuscatedClassSorter; + private static Comparator m_obfClassSorter; + private static Comparator m_deobfClassSorter; static { - m_obfuscatedClassSorter = new Comparator( ) + m_obfClassSorter = new Comparator( ) { @Override - public int compare( ClassFile a, ClassFile b ) + public int compare( String a, String b ) { - if( a.getName().length() != b.getName().length() ) + if( a.length() != b.length() ) { - return a.getName().length() - b.getName().length(); + return a.length() - b.length(); } - - return a.getName().compareTo( b.getName() ); + return a.compareTo( b ); } }; - m_deobfuscatedClassSorter = new Comparator>( ) + m_deobfClassSorter = new Comparator( ) { @Override - public int compare( Map.Entry a, Map.Entry b ) + public int compare( String a, String b ) { // I can never keep this rule straight when writing these damn things... // a < b => -1, a == b => 0, a > b => +1 - String[] aparts = a.getValue().split( "\\." ); - String[] bparts = b.getValue().split( "\\." ); + String[] aparts = a.split( "\\." ); + String[] bparts = b.split( "\\." ); for( int i=0; true; i++ ) { if( i >= aparts.length ) @@ -133,8 +130,8 @@ public class Gui // controls private JFrame m_frame; - private JList m_obfClasses; - private JList> m_deobfClasses; + private JList m_obfClasses; + private JList m_deobfClasses; private JEditorPane m_editor; private JPanel m_infoPanel; private BoxHighlightPainter m_obfuscatedHighlightPainter; @@ -170,10 +167,10 @@ public class Gui pane.setLayout( new BorderLayout() ); // init obfuscated classes list - m_obfClasses = new JList(); + m_obfClasses = new JList(); m_obfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); m_obfClasses.setLayoutOrientation( JList.VERTICAL ); - m_obfClasses.setCellRenderer( new ObfuscatedClassListCellRenderer() ); + m_obfClasses.setCellRenderer( new ClassListCellRenderer() ); m_obfClasses.addMouseListener( new MouseAdapter() { @Override @@ -181,10 +178,10 @@ public class Gui { if( event.getClickCount() == 2 ) { - ClassFile selected = m_obfClasses.getSelectedValue(); + String selected = m_obfClasses.getSelectedValue(); if( selected != null ) { - m_controller.deobfuscateClass( selected ); + m_controller.openEntry( new ClassEntry( selected ) ); } } } @@ -196,10 +193,10 @@ public class Gui obfPanel.add( obfScroller, BorderLayout.CENTER ); // init deobfuscated classes list - m_deobfClasses = new JList>(); + m_deobfClasses = new JList(); m_deobfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); m_deobfClasses.setLayoutOrientation( JList.VERTICAL ); - m_deobfClasses.setCellRenderer( new DeobfuscatedClassListCellRenderer() ); + m_deobfClasses.setCellRenderer( new ClassListCellRenderer() ); m_deobfClasses.addMouseListener( new MouseAdapter() { @Override @@ -207,10 +204,10 @@ public class Gui { if( event.getClickCount() == 2 ) { - Map.Entry selected = m_deobfClasses.getSelectedValue(); + String selected = m_deobfClasses.getSelectedValue(); if( selected != null ) { - m_controller.deobfuscateClass( selected.getKey() ); + m_controller.openEntry( new ClassEntry( selected ) ); } } } @@ -330,7 +327,7 @@ public class Gui ClassInheritanceTreeNode node = (ClassInheritanceTreeNode)m_inheritanceTree.getSelectionPath().getLastPathComponent(); if( node != null ) { - m_controller.deobfuscateClass( new ClassFile( node.getObfClassName() ) ); + m_controller.openEntry( new ClassEntry( node.getDeobfClassName() ) ); } } } @@ -566,31 +563,31 @@ public class Gui m_closeMappingsMenu.setEnabled( false ); } - public void setObfClasses( List obfClasses ) + public void setObfClasses( List obfClasses ) { if( obfClasses != null ) { - Vector sortedClasses = new Vector( obfClasses ); - Collections.sort( sortedClasses, m_obfuscatedClassSorter ); + Vector sortedClasses = new Vector( obfClasses ); + Collections.sort( sortedClasses, m_obfClassSorter ); m_obfClasses.setListData( sortedClasses ); } else { - m_obfClasses.setListData( new Vector() ); + m_obfClasses.setListData( new Vector() ); } } - public void setDeobfClasses( Map deobfClasses ) + public void setDeobfClasses( List deobfClasses ) { if( deobfClasses != null ) { - Vector> sortedClasses = new Vector>( deobfClasses.entrySet() ); - Collections.sort( sortedClasses, m_deobfuscatedClassSorter ); + Vector sortedClasses = new Vector( deobfClasses ); + Collections.sort( sortedClasses, m_deobfClassSorter ); m_deobfClasses.setListData( sortedClasses ); } else { - m_deobfClasses.setListData( new Vector>() ); + m_deobfClasses.setListData( new Vector() ); } } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 834afecc..6a7a7da6 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -15,12 +15,9 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.List; -import java.util.Map; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import cuchaz.enigma.ClassFile; import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.Token; @@ -37,7 +34,7 @@ public class GuiController private Deobfuscator m_deobfuscator; private Gui m_gui; private SourceIndex m_index; - private ClassFile m_currentFile; + private ClassEntry m_currentClass; private boolean m_isDirty; public GuiController( Gui gui ) @@ -45,7 +42,7 @@ public class GuiController m_gui = gui; m_deobfuscator = null; m_index = null; - m_currentFile = null; + m_currentClass = null; m_isDirty = false; } @@ -97,12 +94,6 @@ public class GuiController refreshCurrentClass(); } - public void deobfuscateClass( ClassFile classFile ) - { - m_currentFile = classFile; - deobfuscate( m_currentFile ); - } - public Token getToken( int pos ) { if( m_index == null ) @@ -168,10 +159,10 @@ public class GuiController public void openEntry( Entry obfEntry ) { Entry deobfEntry = m_deobfuscator.deobfuscateEntry( obfEntry ); - if( !m_currentFile.getName().equals( obfEntry.getClassName() ) ) + if( m_currentClass == null || !m_currentClass.equals( deobfEntry.getClassEntry() ) ) { - m_currentFile = new ClassFile( obfEntry.getClassName() ); - deobfuscate( m_currentFile, deobfEntry ); + m_currentClass = new ClassEntry( obfEntry.getClassEntry() ); + deobfuscate( m_currentClass, deobfEntry ); } else { @@ -181,8 +172,8 @@ public class GuiController private void refreshClasses( ) { - List obfClasses = Lists.newArrayList(); - Map deobfClasses = Maps.newHashMap(); + List obfClasses = Lists.newArrayList(); + List deobfClasses = Lists.newArrayList(); m_deobfuscator.getSeparatedClasses( obfClasses, deobfClasses ); m_gui.setObfClasses( obfClasses ); m_gui.setDeobfClasses( deobfClasses ); @@ -195,20 +186,14 @@ public class GuiController private void refreshCurrentClass( Entry entryToShow ) { - if( m_currentFile != null ) + if( m_currentClass != null ) { - deobfuscate( m_currentFile, entryToShow ); + deobfuscate( m_currentClass, entryToShow ); } } - - private void deobfuscate( final ClassFile classFile ) - { - deobfuscate( classFile, null ); - } - private void deobfuscate( final ClassFile classFile, final Entry entryToShow ) + private void deobfuscate( final ClassEntry classEntry, final Entry entryToShow ) { - m_currentFile = classFile; m_gui.setSource( "(deobfuscating...)" ); // run the deobfuscator in a separate thread so we don't block the GUI event queue @@ -218,7 +203,7 @@ public class GuiController public void run( ) { // decompile,deobfuscate the bytecode - m_index = m_deobfuscator.getSource( classFile ); + m_index = m_deobfuscator.getSource( classEntry.getClassName() ); m_gui.setSource( m_index.getSource() ); if( entryToShow != null ) { diff --git a/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java b/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java deleted file mode 100644 index d46e1ae6..00000000 --- a/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.gui; - -import java.awt.Component; - -import javassist.bytecode.Descriptor; - -import javax.swing.DefaultListCellRenderer; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.ListCellRenderer; - -import cuchaz.enigma.ClassFile; - -public class ObfuscatedClassListCellRenderer implements ListCellRenderer -{ - private DefaultListCellRenderer m_defaultRenderer; - - public ObfuscatedClassListCellRenderer( ) - { - m_defaultRenderer = new DefaultListCellRenderer(); - } - - @Override - public Component getListCellRendererComponent( JList list, ClassFile classFile, int index, boolean isSelected, boolean hasFocus ) - { - JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, classFile, index, isSelected, hasFocus ); - - label.setText( Descriptor.toJavaName( classFile.getName() ) ); - - return label; - } -} diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java index 0c25c4d3..27dcc41d 100644 --- a/src/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -65,6 +65,7 @@ public class ArgumentEntry implements Entry, Serializable return m_name; } + @Override public ClassEntry getClassEntry( ) { return m_methodEntry.getClassEntry(); diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 513862d9..738e8e3b 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -37,6 +37,12 @@ public class ClassEntry implements Entry, Serializable { m_name = other.m_name; } + + @Override + public ClassEntry getClassEntry( ) + { + return this; + } @Override public String getName( ) diff --git a/src/cuchaz/enigma/mapping/Entry.java b/src/cuchaz/enigma/mapping/Entry.java index 3ff80276..e1591f02 100644 --- a/src/cuchaz/enigma/mapping/Entry.java +++ b/src/cuchaz/enigma/mapping/Entry.java @@ -14,4 +14,5 @@ public interface Entry { String getName( ); String getClassName( ); + ClassEntry getClassEntry( ); } diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java index 6148c84a..435490bd 100644 --- a/src/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -49,6 +49,7 @@ public class FieldEntry implements Entry, Serializable m_name = other.m_name; } + @Override public ClassEntry getClassEntry( ) { return m_classEntry; diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java index ff232c59..b4b9c9bf 100644 --- a/src/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -56,6 +56,7 @@ public class MethodEntry implements Entry, Serializable m_signature = other.m_signature; } + @Override public ClassEntry getClassEntry( ) { return m_classEntry; -- cgit v1.2.3 From dc7c8847ab69e946a20a45c955b4a0273e262d48 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 10 Aug 2014 22:39:18 -0400 Subject: added backwards navigation --- src/cuchaz/enigma/analysis/SourceIndex.java | 14 ++++----- src/cuchaz/enigma/gui/Gui.java | 26 ++++++++++++++-- src/cuchaz/enigma/gui/GuiController.java | 46 ++++++++++++++++++++++------- 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index ad94cf00..531f3e04 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -79,16 +79,16 @@ public class SourceIndex return token; } - public void add( AstNode node, Entry entry ) + public void add( AstNode node, Entry deobfEntry ) { - m_tokens.put( getToken( node ), entry ); + m_tokens.put( getToken( node ), deobfEntry ); } - public void addDeclaration( AstNode node, Entry entry ) + public void addDeclaration( AstNode node, Entry deobfEntry ) { Token token = getToken( node ); - m_tokens.put( token, entry ); - m_declarations.put( entry, token ); + m_tokens.put( token, deobfEntry ); + m_declarations.put( deobfEntry, token ); } public Token getToken( int pos ) @@ -120,9 +120,9 @@ public class SourceIndex return m_tokens.keySet(); } - public Token getDeclarationToken( Entry entry ) + public Token getDeclarationToken( Entry deobfEntry ) { - return m_declarations.get( entry ); + return m_declarations.get( deobfEntry ); } private int toPos( int line, int col ) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 6666b4c4..62f23918 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -147,6 +147,7 @@ public class Gui private JMenuItem m_renameMenu; private JMenuItem m_inheritanceMenu; private JMenuItem m_openEntryMenu; + private JMenuItem m_openPreviousMenu; // state private EntryPair m_selectedEntryPair; @@ -282,6 +283,7 @@ public class Gui } } ); menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_R, 0 ) ); + menu.setEnabled( false ); popupMenu.add( menu ); m_renameMenu = menu; } @@ -295,8 +297,9 @@ public class Gui showInheritance(); } } ); - popupMenu.add( menu ); menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_I, 0 ) ); + menu.setEnabled( false ); + popupMenu.add( menu ); m_inheritanceMenu = menu; } { @@ -309,10 +312,26 @@ public class Gui openEntry(); } } ); - menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_O, 0 ) ); + menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_N, 0 ) ); + menu.setEnabled( false ); popupMenu.add( menu ); m_openEntryMenu = menu; } + { + JMenuItem menu = new JMenuItem( "Go to previous" ); + menu.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + m_controller.openPreviousEntry(); + } + } ); + menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_P, 0 ) ); + menu.setEnabled( false ); + popupMenu.add( menu ); + m_openPreviousMenu = menu; + } // init inheritance panel m_inheritanceTree = new JTree(); @@ -327,7 +346,7 @@ public class Gui ClassInheritanceTreeNode node = (ClassInheritanceTreeNode)m_inheritanceTree.getSelectionPath().getLastPathComponent(); if( node != null ) { - m_controller.openEntry( new ClassEntry( node.getDeobfClassName() ) ); + m_controller.openEntry( new ClassEntry( node.getObfClassName() ) ); } } } @@ -747,6 +766,7 @@ public class Gui m_inheritanceMenu.setEnabled( isClassEntry || isMethodEntry ); m_openEntryMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry ); + m_openPreviousMenu.setEnabled( m_controller.hasPreviousEntry() ); } private void startRename( ) diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 6a7a7da6..a4228c72 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -15,6 +15,7 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.List; +import java.util.Stack; import com.google.common.collect.Lists; @@ -36,6 +37,7 @@ public class GuiController private SourceIndex m_index; private ClassEntry m_currentClass; private boolean m_isDirty; + private Stack m_entryStack; public GuiController( Gui gui ) { @@ -44,6 +46,7 @@ public class GuiController m_index = null; m_currentClass = null; m_isDirty = false; + m_entryStack = new Stack(); } public boolean isDirty( ) @@ -153,21 +156,42 @@ public class GuiController m_deobfuscator.rename( obfEntry, newName ); m_isDirty = true; refreshClasses(); - refreshCurrentClass( m_deobfuscator.deobfuscateEntry( obfEntry ) ); + refreshCurrentClass( obfEntry ); } - public void openEntry( Entry obfEntry ) + public void openEntry( Entry entry ) { - Entry deobfEntry = m_deobfuscator.deobfuscateEntry( obfEntry ); - if( m_currentClass == null || !m_currentClass.equals( deobfEntry.getClassEntry() ) ) + // go to the entry + Entry obfEntry = m_deobfuscator.obfuscateEntry( entry ); + if( m_currentClass == null || !m_currentClass.equals( obfEntry.getClassEntry() ) ) { m_currentClass = new ClassEntry( obfEntry.getClassEntry() ); - deobfuscate( m_currentClass, deobfEntry ); + deobfuscate( m_currentClass, obfEntry ); } else { - m_gui.showToken( m_index.getDeclarationToken( deobfEntry ) ); + m_gui.showToken( m_index.getDeclarationToken( m_deobfuscator.deobfuscateEntry( obfEntry ) ) ); } + + if( m_entryStack.isEmpty() || !m_entryStack.peek().equals( obfEntry ) ) + { + // update the stack + m_entryStack.push( obfEntry ); + } + } + + public void openPreviousEntry( ) + { + if( hasPreviousEntry() ) + { + m_entryStack.pop(); + openEntry( m_entryStack.peek() ); + } + } + + public boolean hasPreviousEntry( ) + { + return m_entryStack.size() > 1; } private void refreshClasses( ) @@ -184,15 +208,15 @@ public class GuiController refreshCurrentClass( null ); } - private void refreshCurrentClass( Entry entryToShow ) + private void refreshCurrentClass( Entry obfEntryToShow ) { if( m_currentClass != null ) { - deobfuscate( m_currentClass, entryToShow ); + deobfuscate( m_currentClass, obfEntryToShow ); } } - private void deobfuscate( final ClassEntry classEntry, final Entry entryToShow ) + private void deobfuscate( final ClassEntry classEntry, final Entry obfEntryToShow ) { m_gui.setSource( "(deobfuscating...)" ); @@ -205,9 +229,9 @@ public class GuiController // decompile,deobfuscate the bytecode m_index = m_deobfuscator.getSource( classEntry.getClassName() ); m_gui.setSource( m_index.getSource() ); - if( entryToShow != null ) + if( obfEntryToShow != null ) { - m_gui.showToken( m_index.getDeclarationToken( entryToShow ) ); + m_gui.showToken( m_index.getDeclarationToken( m_deobfuscator.deobfuscateEntry( obfEntryToShow ) ) ); } // set the highlighted tokens -- cgit v1.2.3 From bba7c6a19c15bc82946176c79a4eba3612b25f17 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 11 Aug 2014 00:02:00 -0400 Subject: added method inheritance browsing also finally fixed method renamer to rename all method implementations in the inheritance hierarchy. --- src/cuchaz/enigma/Deobfuscator.java | 4 +- src/cuchaz/enigma/analysis/Ancestries.java | 235 +++++++++++++++++++++ .../enigma/analysis/ClassInheritanceTreeNode.java | 99 +++++++++ .../enigma/analysis/DeobfuscatedAncestries.java | 59 ++++++ .../enigma/analysis/MethodInheritanceTreeNode.java | 134 ++++++++++++ src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 10 + .../enigma/gui/ClassInheritanceTreeNode.java | 79 ------- src/cuchaz/enigma/gui/Gui.java | 61 ++++-- src/cuchaz/enigma/gui/GuiController.java | 39 ++-- src/cuchaz/enigma/mapping/Ancestries.java | 154 -------------- .../enigma/mapping/DeobfuscatedAncestries.java | 57 ----- src/cuchaz/enigma/mapping/Mappings.java | 2 + src/cuchaz/enigma/mapping/Renamer.java | 28 +++ src/cuchaz/enigma/mapping/Translator.java | 1 + 14 files changed, 634 insertions(+), 328 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/Ancestries.java create mode 100644 src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/DeobfuscatedAncestries.java create mode 100644 src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java delete mode 100644 src/cuchaz/enigma/gui/ClassInheritanceTreeNode.java delete mode 100644 src/cuchaz/enigma/mapping/Ancestries.java delete mode 100644 src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 8d4394cd..5321d2de 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -31,9 +31,9 @@ import com.strobel.decompiler.languages.java.ast.AstBuilder; import com.strobel.decompiler.languages.java.ast.CompilationUnit; import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; +import cuchaz.enigma.analysis.Ancestries; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; -import cuchaz.enigma.mapping.Ancestries; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; @@ -207,7 +207,7 @@ public class Deobfuscator } else if( obfEntry instanceof MethodEntry ) { - m_renamer.setMethodName( (MethodEntry)obfEntry, newName ); + m_renamer.setMethodTreeName( (MethodEntry)obfEntry, newName ); } else if( obfEntry instanceof ArgumentEntry ) { diff --git a/src/cuchaz/enigma/analysis/Ancestries.java b/src/cuchaz/enigma/analysis/Ancestries.java new file mode 100644 index 00000000..e6c8bbf1 --- /dev/null +++ b/src/cuchaz/enigma/analysis/Ancestries.java @@ -0,0 +1,235 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import javassist.ByteArrayClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.Descriptor; +import javassist.bytecode.MethodInfo; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class Ancestries implements Serializable +{ + private static final long serialVersionUID = 738687982126844179L; + + private Map m_superclasses; + private Multimap m_methodImplementations; + + public Ancestries( ) + { + m_superclasses = Maps.newHashMap(); + m_methodImplementations = HashMultimap.create(); + } + + @SuppressWarnings( "unchecked" ) + public void readFromJar( InputStream in ) + throws IOException + { + ClassPool classPool = new ClassPool(); + + ZipInputStream zin = new ZipInputStream( in ); + ZipEntry entry; + while( ( entry = zin.getNextEntry() ) != null ) + { + // filter out non-classes + if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) ) + { + continue; + } + + // read the class into a buffer + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buf = new byte[Constants.KiB]; + int totalNumBytesRead = 0; + while( zin.available() > 0 ) + { + int numBytesRead = zin.read( buf ); + if( numBytesRead < 0 ) + { + break; + } + bos.write( buf, 0, numBytesRead ); + + // sanity checking + totalNumBytesRead += numBytesRead; + if( totalNumBytesRead > Constants.MiB ) + { + throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" ); + } + } + + // determine the class name (ie chop off the ".class") + String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); + + // get a javassist handle for the class + classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); + try + { + CtClass c = classPool.get( className ); + addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); + addMethodImplementations( c.getName(), (List)c.getClassFile().getMethods() ); + } + catch( NotFoundException ex ) + { + throw new Error( "Unable to load class: " + className ); + } + } + } + + private void addMethodImplementations( String name, List methods ) + { + for( MethodInfo method : methods ) + { + m_methodImplementations.put( name, getMethodKey( method.getName(), method.getDescriptor() ) ); + } + } + + public void addSuperclass( String className, String superclassName ) + { + className = Descriptor.toJvmName( className ); + superclassName = Descriptor.toJvmName( superclassName ); + + if( className.equals( superclassName ) ) + { + throw new IllegalArgumentException( "Class cannot be its own superclass! " + className ); + } + + if( !isJre( className ) && !isJre( superclassName ) ) + { + m_superclasses.put( className, superclassName ); + } + } + + public String getSuperclassName( String className ) + { + return m_superclasses.get( className ); + } + + public List getAncestry( String className ) + { + List ancestors = new ArrayList(); + while( className != null ) + { + className = getSuperclassName( className ); + if( className != null ) + { + ancestors.add( className ); + } + } + return ancestors; + } + + public List getSubclasses( String className ) + { + // linear search is fast enough for now + List subclasses = Lists.newArrayList(); + for( Map.Entry entry : m_superclasses.entrySet() ) + { + String subclass = entry.getKey(); + String superclass = entry.getValue(); + if( className.equals( superclass ) ) + { + subclasses.add( subclass ); + } + } + return subclasses; + } + + public boolean isMethodImplemented( MethodEntry methodEntry ) + { + return isMethodImplemented( methodEntry.getClassName(), methodEntry.getName(), methodEntry.getSignature() ); + } + + public boolean isMethodImplemented( String className, String methodName, String methodSignature ) + { + Collection implementations = m_methodImplementations.get( className ); + if( implementations == null ) + { + return false; + } + return implementations.contains( getMethodKey( methodName, methodSignature ) ); + } + + public ClassInheritanceTreeNode getClassInheritance( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) + { + // get the root node + List ancestry = getAncestry( obfClassEntry.getName() ); + ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, ancestry.get( ancestry.size() - 1 ) ); + + // expand all children recursively + rootNode.load( this, true ); + + return rootNode; + } + + public MethodInheritanceTreeNode getMethodInheritance( Translator deobfuscatingTranslator, MethodEntry obfMethodEntry ) + { + // travel to the ancestor implementation + String baseImplementationClassName = obfMethodEntry.getClassName(); + for( String ancestorClassName : getAncestry( obfMethodEntry.getClassName() ) ) + { + if( isMethodImplemented( ancestorClassName, obfMethodEntry.getName(), obfMethodEntry.getSignature() ) ) + { + baseImplementationClassName = ancestorClassName; + } + } + + // make a root node at the base + MethodEntry methodEntry = new MethodEntry( + new ClassEntry( baseImplementationClassName ), + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( + deobfuscatingTranslator, + methodEntry, + isMethodImplemented( methodEntry ) + ); + + // expand the full tree + rootNode.load( this, true ); + + return rootNode; + } + + private boolean isJre( String className ) + { + return className.startsWith( "java/" ) + || className.startsWith( "javax/" ); + } + + private String getMethodKey( String name, String signature ) + { + return name + signature; + } +} diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java new file mode 100644 index 00000000..2ed141ff --- /dev/null +++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Translator; + +public class ClassInheritanceTreeNode extends DefaultMutableTreeNode +{ + private static final long serialVersionUID = 4432367405826178490L; + + private Translator m_deobfuscatingTranslator; + private String m_obfClassName; + + public ClassInheritanceTreeNode( Translator deobfuscatingTranslator, String obfClassName ) + { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_obfClassName = obfClassName; + } + + public String getObfClassName( ) + { + return m_obfClassName; + } + + public String getDeobfClassName( ) + { + return m_deobfuscatingTranslator.translateClass( m_obfClassName ); + } + + @Override + public String toString( ) + { + String deobfClassName = getDeobfClassName(); + if( deobfClassName != null ) + { + return deobfClassName; + } + return m_obfClassName; + } + + public void load( Ancestries ancestries, boolean recurse ) + { + // get all the child nodes + List nodes = Lists.newArrayList(); + for( String subclassName : ancestries.getSubclasses( m_obfClassName ) ) + { + nodes.add( new ClassInheritanceTreeNode( m_deobfuscatingTranslator, subclassName ) ); + } + + // add them to this node + for( ClassInheritanceTreeNode node : nodes ) + { + this.add( node ); + } + + if( recurse ) + { + for( ClassInheritanceTreeNode node : nodes ) + { + node.load( ancestries, true ); + } + } + } + + public static ClassInheritanceTreeNode findNode( ClassInheritanceTreeNode node, ClassEntry entry ) + { + // is this the node? + if( node.getObfClassName().equals( entry.getName() ) ) + { + return node; + } + + // recurse + for( int i=0; i m_classesByObf; + private Map m_classesByDeobf; + + public DeobfuscatedAncestries( Ancestries ancestries, Map classesByObf, Map classesByDeobf ) + { + m_ancestries = ancestries; + m_classesByObf = classesByObf; + m_classesByDeobf = classesByDeobf; + } + + @Override + public String getSuperclassName( String deobfClassName ) + { + // obfuscate the class name + ClassMapping classIndex = m_classesByDeobf.get( deobfClassName ); + if( classIndex == null ) + { + return null; + } + String obfClassName = classIndex.getObfName(); + + // get the superclass + String obfSuperclassName = m_ancestries.getSuperclassName( obfClassName ); + if( obfSuperclassName == null ) + { + return null; + } + + // deobfuscate the superclass name + classIndex = m_classesByObf.get( obfSuperclassName ); + if( classIndex == null ) + { + return null; + } + + return classIndex.getDeobfName(); + } +} diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java new file mode 100644 index 00000000..1fecf489 --- /dev/null +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class MethodInheritanceTreeNode extends DefaultMutableTreeNode +{ + private static final long serialVersionUID = 1096677030991810007L; + + private Translator m_deobfuscatingTranslator; + private MethodEntry m_entry; + private boolean m_isImplemented; + + public MethodInheritanceTreeNode( Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented ) + { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + m_isImplemented = isImplemented; + } + + public MethodEntry getMethodEntry( ) + { + return m_entry; + } + + public String getDeobfClassName( ) + { + return m_deobfuscatingTranslator.translateClass( m_entry.getClassName() ); + } + + public String getDeobfMethodName( ) + { + return m_deobfuscatingTranslator.translate( m_entry ); + } + + public boolean isImplemented( ) + { + return m_isImplemented; + } + + @Override + public String toString( ) + { + String className = getDeobfClassName(); + if( className == null ) + { + className = m_entry.getClassName(); + } + + if( !m_isImplemented ) + { + return className; + } + else + { + String methodName = getDeobfMethodName(); + if( methodName == null ) + { + methodName = m_entry.getName(); + } + return className + "." + methodName + "()"; + } + } + + public void load( Ancestries ancestries, boolean recurse ) + { + // get all the child nodes + List nodes = Lists.newArrayList(); + for( String subclassName : ancestries.getSubclasses( m_entry.getClassName() ) ) + { + MethodEntry methodEntry = new MethodEntry( + new ClassEntry( subclassName ), + m_entry.getName(), + m_entry.getSignature() + ); + nodes.add( new MethodInheritanceTreeNode( + m_deobfuscatingTranslator, + methodEntry, + ancestries.isMethodImplemented( subclassName, m_entry.getName(), m_entry.getSignature() ) + ) ); + } + + // add them to this node + for( MethodInheritanceTreeNode node : nodes ) + { + this.add( node ); + } + + if( recurse ) + { + for( MethodInheritanceTreeNode node : nodes ) + { + node.load( ancestries, true ); + } + } + } + + public static MethodInheritanceTreeNode findNode( MethodInheritanceTreeNode node, MethodEntry entry ) + { + // is this the node? + if( node.getMethodEntry().equals( entry ) ) + { + return node; + } + + // recurse + for( int i=0; i nodes = Lists.newArrayList(); - for( String subclassName : ancestries.getSubclasses( m_obfClassName ) ) - { - nodes.add( new ClassInheritanceTreeNode( m_deobfuscatingTranslator, subclassName ) ); - } - - // add them to this node - for( ClassInheritanceTreeNode node : nodes ) - { - this.add( node ); - } - - if( recurse ) - { - for( ClassInheritanceTreeNode node : nodes ) - { - node.load( ancestries, true ); - } - } - } -} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 62f23918..db676777 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -66,6 +66,8 @@ import jsyntaxpane.DefaultSyntaxKit; import com.google.common.collect.Lists; import cuchaz.enigma.Constants; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; @@ -343,10 +345,24 @@ public class Gui { if( event.getClickCount() == 2 ) { - ClassInheritanceTreeNode node = (ClassInheritanceTreeNode)m_inheritanceTree.getSelectionPath().getLastPathComponent(); - if( node != null ) + // get the selected node + Object node = m_inheritanceTree.getSelectionPath().getLastPathComponent(); + if( node == null ) { - m_controller.openEntry( new ClassEntry( node.getObfClassName() ) ); + return; + } + + if( node instanceof ClassInheritanceTreeNode ) + { + m_controller.openEntry( new ClassEntry( ((ClassInheritanceTreeNode)node).getObfClassName() ) ); + } + else if( node instanceof MethodInheritanceTreeNode ) + { + MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode)node; + if( methodNode.isImplemented() ) + { + m_controller.openEntry( methodNode.getMethodEntry() ); + } } } } @@ -836,31 +852,46 @@ public class Gui return; } - // get the current class if( m_selectedEntryPair.obf instanceof ClassEntry ) { + // get the class inheritance ClassInheritanceTreeNode classNode = m_controller.getClassInheritance( (ClassEntry)m_selectedEntryPair.obf ); - // build the path from the root to the class node - List nodes = Lists.newArrayList(); - TreeNode node = classNode; - do - { - nodes.add( node ); - node = node.getParent(); - } - while( node != null ); - Collections.reverse( nodes ); - TreePath path = new TreePath( nodes.toArray() ); + // show the tree at the root + TreePath path = getPathToRoot( classNode ); + m_inheritanceTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) ); + m_inheritanceTree.expandPath( path ); + m_inheritanceTree.setSelectionRow( m_inheritanceTree.getRowForPath( path ) ); + } + else if( m_selectedEntryPair.obf instanceof MethodEntry ) + { + // get the method inheritance + MethodInheritanceTreeNode classNode = m_controller.getMethodInheritance( (MethodEntry)m_selectedEntryPair.obf ); // show the tree at the root + TreePath path = getPathToRoot( classNode ); m_inheritanceTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) ); m_inheritanceTree.expandPath( path ); m_inheritanceTree.setSelectionRow( m_inheritanceTree.getRowForPath( path ) ); } + redraw(); } + private TreePath getPathToRoot( TreeNode node ) + { + List nodes = Lists.newArrayList(); + TreeNode n = node; + do + { + nodes.add( n ); + n = n.getParent(); + } + while( n != null ); + Collections.reverse( nodes ); + return new TreePath( nodes.toArray() ); + } + private void openEntry( ) { if( m_selectedEntryPair == null ) diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index a4228c72..1946b4a3 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -20,6 +20,8 @@ import java.util.Stack; import com.google.common.collect.Lists; import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ClassEntry; @@ -27,8 +29,8 @@ import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; +import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.TranslationDirection; -import cuchaz.enigma.mapping.Translator; public class GuiController { @@ -128,27 +130,22 @@ public class GuiController return m_deobfuscator.entryIsObfuscatedIdenfitier( m_deobfuscator.obfuscateEntry( deobfEntry ) ); } - public ClassInheritanceTreeNode getClassInheritance( ClassEntry classEntry ) + public ClassInheritanceTreeNode getClassInheritance( ClassEntry obfClassEntry ) { - Translator deobfuscatingTranslator = m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ); - - // create a node for this class - ClassInheritanceTreeNode thisNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, classEntry.getName() ); - - // expand all children recursively - thisNode.load( m_deobfuscator.getAncestries(), true ); - - // get the ancestors too - ClassInheritanceTreeNode node = thisNode; - for( String superclassName : m_deobfuscator.getAncestries().getAncestry( classEntry.getName() ) ) - { - // add the parent node - ClassInheritanceTreeNode parentNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, superclassName ); - parentNode.add( node ); - node = parentNode; - } - - return thisNode; + ClassInheritanceTreeNode rootNode = m_deobfuscator.getAncestries().getClassInheritance( + m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + obfClassEntry + ); + return ClassInheritanceTreeNode.findNode( rootNode, obfClassEntry ); + } + + public MethodInheritanceTreeNode getMethodInheritance( MethodEntry obfMethodEntry ) + { + MethodInheritanceTreeNode rootNode = m_deobfuscator.getAncestries().getMethodInheritance( + m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + obfMethodEntry + ); + return MethodInheritanceTreeNode.findNode( rootNode, obfMethodEntry ); } public void rename( Entry obfEntry, String newName ) diff --git a/src/cuchaz/enigma/mapping/Ancestries.java b/src/cuchaz/enigma/mapping/Ancestries.java deleted file mode 100644 index 894cf802..00000000 --- a/src/cuchaz/enigma/mapping/Ancestries.java +++ /dev/null @@ -1,154 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.mapping; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import javassist.ByteArrayClassPath; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.NotFoundException; -import javassist.bytecode.Descriptor; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - -import cuchaz.enigma.Constants; - -public class Ancestries implements Serializable -{ - private static final long serialVersionUID = 738687982126844179L; - - private Map m_superclasses; - - public Ancestries( ) - { - m_superclasses = Maps.newHashMap(); - } - - public void readFromJar( InputStream in ) - throws IOException - { - ClassPool classPool = new ClassPool(); - - ZipInputStream zin = new ZipInputStream( in ); - ZipEntry entry; - while( ( entry = zin.getNextEntry() ) != null ) - { - // filter out non-classes - if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) ) - { - continue; - } - - // read the class into a buffer - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - byte[] buf = new byte[Constants.KiB]; - int totalNumBytesRead = 0; - while( zin.available() > 0 ) - { - int numBytesRead = zin.read( buf ); - if( numBytesRead < 0 ) - { - break; - } - bos.write( buf, 0, numBytesRead ); - - // sanity checking - totalNumBytesRead += numBytesRead; - if( totalNumBytesRead > Constants.MiB ) - { - throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" ); - } - } - - // determine the class name (ie chop off the ".class") - String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); - - // get a javassist handle for the class - classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); - try - { - CtClass c = classPool.get( className ); - addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); - } - catch( NotFoundException ex ) - { - throw new Error( "Unable to load class: " + className ); - } - } - } - - public void addSuperclass( String className, String superclassName ) - { - className = Descriptor.toJvmName( className ); - superclassName = Descriptor.toJvmName( superclassName ); - - if( className.equals( superclassName ) ) - { - throw new IllegalArgumentException( "Class cannot be its own superclass! " + className ); - } - - if( !isJre( className ) && !isJre( superclassName ) ) - { - m_superclasses.put( className, superclassName ); - } - } - - public String getSuperclassName( String className ) - { - return m_superclasses.get( className ); - } - - public List getAncestry( String className ) - { - List ancestors = new ArrayList(); - while( className != null ) - { - className = getSuperclassName( className ); - if( className != null ) - { - ancestors.add( className ); - } - } - return ancestors; - } - - public List getSubclasses( String className ) - { - // linear search is fast enough for now - List subclasses = Lists.newArrayList(); - for( Map.Entry entry : m_superclasses.entrySet() ) - { - String subclass = entry.getKey(); - String superclass = entry.getValue(); - if( className.equals( superclass ) ) - { - subclasses.add( subclass ); - } - } - return subclasses; - } - - private boolean isJre( String className ) - { - return className.startsWith( "java/" ) - || className.startsWith( "javax/" ); - } -} diff --git a/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java b/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java deleted file mode 100644 index dcb0741a..00000000 --- a/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.mapping; - -import java.util.Map; - -public class DeobfuscatedAncestries extends Ancestries -{ - private static final long serialVersionUID = 8316248774892618324L; - - private Ancestries m_ancestries; - private Map m_classesByObf; - private Map m_classesByDeobf; - - protected DeobfuscatedAncestries( Ancestries ancestries, Map classesByObf, Map classesByDeobf ) - { - m_ancestries = ancestries; - m_classesByObf = classesByObf; - m_classesByDeobf = classesByDeobf; - } - - @Override - public String getSuperclassName( String deobfClassName ) - { - // obfuscate the class name - ClassMapping classIndex = m_classesByDeobf.get( deobfClassName ); - if( classIndex == null ) - { - return null; - } - String obfClassName = classIndex.getObfName(); - - // get the superclass - String obfSuperclassName = m_ancestries.getSuperclassName( obfClassName ); - if( obfSuperclassName == null ) - { - return null; - } - - // deobfuscate the superclass name - classIndex = m_classesByObf.get( obfSuperclassName ); - if( classIndex == null ) - { - return null; - } - - return classIndex.getDeobfName(); - } -} diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 4dff6935..c7cb6a67 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -20,6 +20,8 @@ import java.util.zip.GZIPInputStream; import com.google.common.collect.Maps; import cuchaz.enigma.Util; +import cuchaz.enigma.analysis.Ancestries; +import cuchaz.enigma.analysis.DeobfuscatedAncestries; public class Mappings implements Serializable { diff --git a/src/cuchaz/enigma/mapping/Renamer.java b/src/cuchaz/enigma/mapping/Renamer.java index b7aa35ca..5a75c016 100644 --- a/src/cuchaz/enigma/mapping/Renamer.java +++ b/src/cuchaz/enigma/mapping/Renamer.java @@ -15,6 +15,9 @@ import java.io.ObjectOutputStream; import java.io.OutputStream; import java.util.zip.GZIPOutputStream; +import cuchaz.enigma.analysis.Ancestries; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; + public class Renamer { private Ancestries m_ancestries; @@ -54,6 +57,30 @@ public class Renamer classMapping.setFieldName( obf.getName(), deobfName ); } + public void setMethodTreeName( MethodEntry obf, String deobfName ) + { + // get the method tree + setMethodTreeName( + m_ancestries.getMethodInheritance( m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ), obf ), + deobfName + ); + } + + private void setMethodTreeName( MethodInheritanceTreeNode node, String deobfName ) + { + if( node.isImplemented() ) + { + // apply the name here + setMethodName( node.getMethodEntry(), deobfName ); + } + + // recurse + for( int i=0; i m_obfClassNames; @@ -65,9 +65,9 @@ public class Deobfuscator InputStream jarIn = null; try { - m_ancestries = new Ancestries(); + m_jarIndex = new JarIndex(); jarIn = new FileInputStream( m_file ); - m_ancestries.readFromJar( jarIn ); + m_jarIndex.indexJar( jarIn ); } finally { @@ -107,9 +107,9 @@ public class Deobfuscator return m_file.getName(); } - public Ancestries getAncestries( ) + public JarIndex getJarIndex( ) { - return m_ancestries; + return m_jarIndex; } public Mappings getMappings( ) @@ -123,7 +123,7 @@ public class Deobfuscator val = new Mappings(); } m_mappings = val; - m_renamer = new Renamer( m_ancestries, m_mappings ); + m_renamer = new Renamer( m_jarIndex, m_mappings ); // update decompiler options m_settings.setTypeLoader( new TranslatingTypeLoader( @@ -135,7 +135,7 @@ public class Deobfuscator public Translator getTranslator( TranslationDirection direction ) { - return m_mappings.getTranslator( m_ancestries, direction ); + return m_mappings.getTranslator( m_jarIndex.getAncestries(), direction ); } public void getSeparatedClasses( List obfClasses, List deobfClasses ) diff --git a/src/cuchaz/enigma/analysis/Ancestries.java b/src/cuchaz/enigma/analysis/Ancestries.java index e6c8bbf1..83c239cd 100644 --- a/src/cuchaz/enigma/analysis/Ancestries.java +++ b/src/cuchaz/enigma/analysis/Ancestries.java @@ -10,110 +10,27 @@ ******************************************************************************/ package cuchaz.enigma.analysis; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import javassist.ByteArrayClassPath; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.NotFoundException; import javassist.bytecode.Descriptor; -import javassist.bytecode.MethodInfo; -import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; - -import cuchaz.enigma.Constants; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.MethodEntry; -import cuchaz.enigma.mapping.Translator; public class Ancestries implements Serializable { private static final long serialVersionUID = 738687982126844179L; private Map m_superclasses; - private Multimap m_methodImplementations; public Ancestries( ) { m_superclasses = Maps.newHashMap(); - m_methodImplementations = HashMultimap.create(); } - @SuppressWarnings( "unchecked" ) - public void readFromJar( InputStream in ) - throws IOException - { - ClassPool classPool = new ClassPool(); - - ZipInputStream zin = new ZipInputStream( in ); - ZipEntry entry; - while( ( entry = zin.getNextEntry() ) != null ) - { - // filter out non-classes - if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) ) - { - continue; - } - - // read the class into a buffer - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - byte[] buf = new byte[Constants.KiB]; - int totalNumBytesRead = 0; - while( zin.available() > 0 ) - { - int numBytesRead = zin.read( buf ); - if( numBytesRead < 0 ) - { - break; - } - bos.write( buf, 0, numBytesRead ); - - // sanity checking - totalNumBytesRead += numBytesRead; - if( totalNumBytesRead > Constants.MiB ) - { - throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" ); - } - } - - // determine the class name (ie chop off the ".class") - String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); - - // get a javassist handle for the class - classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); - try - { - CtClass c = classPool.get( className ); - addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); - addMethodImplementations( c.getName(), (List)c.getClassFile().getMethods() ); - } - catch( NotFoundException ex ) - { - throw new Error( "Unable to load class: " + className ); - } - } - } - - private void addMethodImplementations( String name, List methods ) - { - for( MethodInfo method : methods ) - { - m_methodImplementations.put( name, getMethodKey( method.getName(), method.getDescriptor() ) ); - } - } - public void addSuperclass( String className, String superclassName ) { className = Descriptor.toJvmName( className ); @@ -165,71 +82,9 @@ public class Ancestries implements Serializable return subclasses; } - public boolean isMethodImplemented( MethodEntry methodEntry ) - { - return isMethodImplemented( methodEntry.getClassName(), methodEntry.getName(), methodEntry.getSignature() ); - } - - public boolean isMethodImplemented( String className, String methodName, String methodSignature ) - { - Collection implementations = m_methodImplementations.get( className ); - if( implementations == null ) - { - return false; - } - return implementations.contains( getMethodKey( methodName, methodSignature ) ); - } - - public ClassInheritanceTreeNode getClassInheritance( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) - { - // get the root node - List ancestry = getAncestry( obfClassEntry.getName() ); - ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, ancestry.get( ancestry.size() - 1 ) ); - - // expand all children recursively - rootNode.load( this, true ); - - return rootNode; - } - - public MethodInheritanceTreeNode getMethodInheritance( Translator deobfuscatingTranslator, MethodEntry obfMethodEntry ) - { - // travel to the ancestor implementation - String baseImplementationClassName = obfMethodEntry.getClassName(); - for( String ancestorClassName : getAncestry( obfMethodEntry.getClassName() ) ) - { - if( isMethodImplemented( ancestorClassName, obfMethodEntry.getName(), obfMethodEntry.getSignature() ) ) - { - baseImplementationClassName = ancestorClassName; - } - } - - // make a root node at the base - MethodEntry methodEntry = new MethodEntry( - new ClassEntry( baseImplementationClassName ), - obfMethodEntry.getName(), - obfMethodEntry.getSignature() - ); - MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( - deobfuscatingTranslator, - methodEntry, - isMethodImplemented( methodEntry ) - ); - - // expand the full tree - rootNode.load( this, true ); - - return rootNode; - } - private boolean isJre( String className ) { return className.startsWith( "java/" ) || className.startsWith( "javax/" ); } - - private String getMethodKey( String name, String signature ) - { - return name + signature; - } } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java new file mode 100644 index 00000000..8a8384c6 --- /dev/null +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; + +import javassist.ByteArrayClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.Descriptor; +import javassist.bytecode.MethodInfo; +import cuchaz.enigma.Constants; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class JarIndex +{ + private Ancestries m_ancestries; + private Multimap m_methodImplementations; + + public JarIndex( ) + { + m_ancestries = new Ancestries(); + m_methodImplementations = HashMultimap.create(); + } + + @SuppressWarnings( "unchecked" ) + public void indexJar( InputStream in ) + throws IOException + { + ClassPool classPool = new ClassPool(); + + ZipInputStream zin = new ZipInputStream( in ); + ZipEntry entry; + while( ( entry = zin.getNextEntry() ) != null ) + { + // filter out non-classes + if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) ) + { + continue; + } + + // read the class into a buffer + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buf = new byte[Constants.KiB]; + int totalNumBytesRead = 0; + while( zin.available() > 0 ) + { + int numBytesRead = zin.read( buf ); + if( numBytesRead < 0 ) + { + break; + } + bos.write( buf, 0, numBytesRead ); + + // sanity checking + totalNumBytesRead += numBytesRead; + if( totalNumBytesRead > Constants.MiB ) + { + throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" ); + } + } + + // determine the class name (ie chop off the ".class") + String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); + + // get a javassist handle for the class + classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); + try + { + CtClass c = classPool.get( className ); + m_ancestries.addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); + addMethodImplementations( c.getName(), (List)c.getClassFile().getMethods() ); + } + catch( NotFoundException ex ) + { + throw new Error( "Unable to load class: " + className ); + } + } + } + + private void addMethodImplementations( String name, List methods ) + { + for( MethodInfo method : methods ) + { + m_methodImplementations.put( name, getMethodKey( method.getName(), method.getDescriptor() ) ); + } + } + + public Ancestries getAncestries( ) + { + return m_ancestries; + } + + public boolean isMethodImplemented( MethodEntry methodEntry ) + { + return isMethodImplemented( methodEntry.getClassName(), methodEntry.getName(), methodEntry.getSignature() ); + } + + public boolean isMethodImplemented( String className, String methodName, String methodSignature ) + { + Collection implementations = m_methodImplementations.get( className ); + if( implementations == null ) + { + return false; + } + return implementations.contains( getMethodKey( methodName, methodSignature ) ); + } + + + public ClassInheritanceTreeNode getClassInheritance( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) + { + // get the root node + List ancestry = m_ancestries.getAncestry( obfClassEntry.getName() ); + ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, ancestry.get( ancestry.size() - 1 ) ); + + // expand all children recursively + rootNode.load( m_ancestries, true ); + + return rootNode; + } + + public MethodInheritanceTreeNode getMethodInheritance( Translator deobfuscatingTranslator, MethodEntry obfMethodEntry ) + { + // travel to the ancestor implementation + String baseImplementationClassName = obfMethodEntry.getClassName(); + for( String ancestorClassName : m_ancestries.getAncestry( obfMethodEntry.getClassName() ) ) + { + if( isMethodImplemented( ancestorClassName, obfMethodEntry.getName(), obfMethodEntry.getSignature() ) ) + { + baseImplementationClassName = ancestorClassName; + } + } + + // make a root node at the base + MethodEntry methodEntry = new MethodEntry( + new ClassEntry( baseImplementationClassName ), + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( + deobfuscatingTranslator, + methodEntry, + isMethodImplemented( methodEntry ) + ); + + // expand the full tree + rootNode.load( this, true ); + + return rootNode; + } + + private String getMethodKey( String name, String signature ) + { + return name + signature; + } +} diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index 1fecf489..a28a9f46 100644 --- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -79,11 +79,11 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode } } - public void load( Ancestries ancestries, boolean recurse ) + public void load( JarIndex index, boolean recurse ) { // get all the child nodes List nodes = Lists.newArrayList(); - for( String subclassName : ancestries.getSubclasses( m_entry.getClassName() ) ) + for( String subclassName : index.getAncestries().getSubclasses( m_entry.getClassName() ) ) { MethodEntry methodEntry = new MethodEntry( new ClassEntry( subclassName ), @@ -93,7 +93,7 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode nodes.add( new MethodInheritanceTreeNode( m_deobfuscatingTranslator, methodEntry, - ancestries.isMethodImplemented( subclassName, m_entry.getName(), m_entry.getSignature() ) + index.isMethodImplemented( subclassName, m_entry.getName(), m_entry.getSignature() ) ) ); } @@ -107,7 +107,7 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { for( MethodInheritanceTreeNode node : nodes ) { - node.load( ancestries, true ); + node.load( index, true ); } } } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 1946b4a3..880f001f 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -132,7 +132,7 @@ public class GuiController public ClassInheritanceTreeNode getClassInheritance( ClassEntry obfClassEntry ) { - ClassInheritanceTreeNode rootNode = m_deobfuscator.getAncestries().getClassInheritance( + ClassInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getClassInheritance( m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), obfClassEntry ); @@ -141,7 +141,7 @@ public class GuiController public MethodInheritanceTreeNode getMethodInheritance( MethodEntry obfMethodEntry ) { - MethodInheritanceTreeNode rootNode = m_deobfuscator.getAncestries().getMethodInheritance( + MethodInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodInheritance( m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), obfMethodEntry ); diff --git a/src/cuchaz/enigma/mapping/Renamer.java b/src/cuchaz/enigma/mapping/Renamer.java index 5a75c016..d372575e 100644 --- a/src/cuchaz/enigma/mapping/Renamer.java +++ b/src/cuchaz/enigma/mapping/Renamer.java @@ -15,17 +15,17 @@ import java.io.ObjectOutputStream; import java.io.OutputStream; import java.util.zip.GZIPOutputStream; -import cuchaz.enigma.analysis.Ancestries; +import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; public class Renamer { - private Ancestries m_ancestries; + private JarIndex m_index; private Mappings m_mappings; - public Renamer( Ancestries ancestries, Mappings mappings ) + public Renamer( JarIndex index, Mappings mappings ) { - m_ancestries = ancestries; + m_index = index; m_mappings = mappings; } @@ -61,7 +61,7 @@ public class Renamer { // get the method tree setMethodTreeName( - m_ancestries.getMethodInheritance( m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ), obf ), + m_index.getMethodInheritance( m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ), obf ), deobfName ); } @@ -90,7 +90,7 @@ public class Renamer classMapping = createClassMapping( obf.getClassEntry() ); } - String deobfSignature = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); + String deobfSignature = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); // TODO: update ancestor/descendant methods in other classes in the inheritance hierarchy too @@ -129,7 +129,7 @@ public class Renamer private void updateDeobfMethodSignatures( ) { - Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ); + Translator translator = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ); for( ClassMapping classMapping : m_mappings.m_classesByObf.values() ) { classMapping.updateDeobfMethodSignatures( translator ); -- cgit v1.2.3 From db1de7d32d8c6ef55627165448fd3f4f90802486 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 11 Aug 2014 23:19:32 -0400 Subject: fix keyboard shortcuts --- src/cuchaz/enigma/gui/Gui.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index db676777..3e8a18cb 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -260,9 +260,13 @@ public class Gui showInheritance(); break; - case KeyEvent.VK_O: + case KeyEvent.VK_N: openEntry(); break; + + case KeyEvent.VK_P: + m_controller.openPreviousEntry(); + break; } } } ); -- cgit v1.2.3 From 52bb7ba51ceaf65f40e5e3e2de9d1ac3f7fc9c2e Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 12 Aug 2014 00:24:11 -0400 Subject: got simple method call graph working! --- src/cuchaz/enigma/Deobfuscator.java | 34 +------ src/cuchaz/enigma/analysis/JarIndex.java | 101 ++++++++++++++++++--- .../enigma/analysis/MethodCallsTreeNode.java | 96 ++++++++++++++++++++ src/cuchaz/enigma/gui/Gui.java | 93 +++++++++++++++++-- src/cuchaz/enigma/gui/GuiController.java | 11 +++ 5 files changed, 284 insertions(+), 51 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/MethodCallsTreeNode.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index c35a4835..33eef08a 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -15,12 +15,9 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; -import java.util.Enumeration; import java.util.List; -import java.util.jar.JarEntry; import java.util.jar.JarFile; -import com.google.common.collect.Lists; import com.strobel.assembler.metadata.MetadataSystem; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.decompiler.DecompilerContext; @@ -53,7 +50,6 @@ public class Deobfuscator private JarIndex m_jarIndex; private Mappings m_mappings; private Renamer m_renamer; - private List m_obfClassNames; public Deobfuscator( File file ) throws IOException @@ -65,7 +61,7 @@ public class Deobfuscator InputStream jarIn = null; try { - m_jarIndex = new JarIndex(); + m_jarIndex = new JarIndex( m_jar ); jarIn = new FileInputStream( m_file ); m_jarIndex.indexJar( jarIn ); } @@ -74,26 +70,6 @@ public class Deobfuscator Util.closeQuietly( jarIn ); } - // get the obf class names - m_obfClassNames = Lists.newArrayList(); - { - Enumeration entries = m_jar.entries(); - while( entries.hasMoreElements() ) - { - JarEntry entry = entries.nextElement(); - - // skip everything but class files - if( !entry.getName().endsWith( ".class" ) ) - { - continue; - } - - // get the class name from the file - String className = entry.getName().substring( 0, entry.getName().length() - 6 ); - m_obfClassNames.add( className ); - } - } - // config the decompiler m_settings = DecompilerSettings.javaDefaults(); m_settings.setShowSyntheticMembers( true ); @@ -140,7 +116,7 @@ public class Deobfuscator public void getSeparatedClasses( List obfClasses, List deobfClasses ) { - for( String obfClassName : m_obfClassNames ) + for( String obfClassName : m_jarIndex.getObfClassNames() ) { // separate the classes ClassMapping classMapping = m_mappings.getClassByObf( obfClassName ); @@ -302,15 +278,15 @@ public class Deobfuscator if( obfEntry instanceof ClassEntry ) { // obf classes must be in the list - return m_obfClassNames.contains( obfEntry.getName() ); + return m_jarIndex.getObfClassNames().contains( obfEntry.getName() ); } else if( obfEntry instanceof FieldEntry ) { - return m_obfClassNames.contains( ((FieldEntry)obfEntry).getClassName() ); + return m_jarIndex.getObfClassNames().contains( ((FieldEntry)obfEntry).getClassName() ); } else if( obfEntry instanceof MethodEntry ) { - return m_obfClassNames.contains( ((MethodEntry)obfEntry).getClassName() ); + return m_jarIndex.getObfClassNames().contains( ((MethodEntry)obfEntry).getClassName() ); } else if( obfEntry instanceof ArgumentEntry ) { diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 8a8384c6..06b01736 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -14,19 +14,28 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collection; +import java.util.Enumeration; import java.util.List; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; - import javassist.ByteArrayClassPath; +import javassist.CannotCompileException; import javassist.ClassPool; +import javassist.CtBehavior; import javassist.CtClass; import javassist.NotFoundException; import javassist.bytecode.Descriptor; -import javassist.bytecode.MethodInfo; +import javassist.expr.ExprEditor; +import javassist.expr.MethodCall; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + import cuchaz.enigma.Constants; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.MethodEntry; @@ -34,16 +43,35 @@ import cuchaz.enigma.mapping.Translator; public class JarIndex { + private Set m_obfClassNames; private Ancestries m_ancestries; - private Multimap m_methodImplementations; + private Multimap m_methodImplementations; + private Multimap m_methodCalls; - public JarIndex( ) + public JarIndex( JarFile jar ) { + m_obfClassNames = Sets.newHashSet(); m_ancestries = new Ancestries(); m_methodImplementations = HashMultimap.create(); + m_methodCalls = HashMultimap.create(); + + // read the class names + Enumeration enumeration = jar.entries(); + while( enumeration.hasMoreElements() ) + { + JarEntry entry = enumeration.nextElement(); + + // filter out non-classes + if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) ) + { + continue; + } + + String className = entry.getName().substring( 0, entry.getName().length() - 6 ); + m_obfClassNames.add( Descriptor.toJvmName( className ) ); + } } - @SuppressWarnings( "unchecked" ) public void indexJar( InputStream in ) throws IOException { @@ -89,7 +117,10 @@ public class JarIndex { CtClass c = classPool.get( className ); m_ancestries.addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); - addMethodImplementations( c.getName(), (List)c.getClassFile().getMethods() ); + for( CtBehavior behavior : c.getDeclaredBehaviors() ) + { + indexBehavior( behavior ); + } } catch( NotFoundException ex ) { @@ -98,14 +129,55 @@ public class JarIndex } } - private void addMethodImplementations( String name, List methods ) + private void indexBehavior( CtBehavior behavior ) { - for( MethodInfo method : methods ) + // get the method entry + String className = Descriptor.toJvmName( behavior.getDeclaringClass().getName() ); + final MethodEntry methodEntry = new MethodEntry( + new ClassEntry( className ), + behavior.getName(), + behavior.getSignature() + ); + + // index implementation + m_methodImplementations.put( className, methodEntry ); + + // index method calls + try + { + behavior.instrument( new ExprEditor( ) + { + @Override + public void edit( MethodCall call ) + { + // is this a jar class? + String className = Descriptor.toJvmName( call.getClassName() ); + if( !m_obfClassNames.contains( className ) ) + { + return; + } + + // make entry for the called method + MethodEntry calledMethodEntry = new MethodEntry( + new ClassEntry( className ), + call.getMethodName(), + call.getSignature() + ); + m_methodCalls.put( calledMethodEntry, methodEntry ); + } + } ); + } + catch( CannotCompileException ex ) { - m_methodImplementations.put( name, getMethodKey( method.getName(), method.getDescriptor() ) ); + throw new Error( ex ); } } + public Set getObfClassNames( ) + { + return m_obfClassNames; + } + public Ancestries getAncestries( ) { return m_ancestries; @@ -118,7 +190,7 @@ public class JarIndex public boolean isMethodImplemented( String className, String methodName, String methodSignature ) { - Collection implementations = m_methodImplementations.get( className ); + Collection implementations = m_methodImplementations.get( className ); if( implementations == null ) { return false; @@ -169,6 +241,11 @@ public class JarIndex return rootNode; } + public Collection getMethodCallers( MethodEntry methodEntry ) + { + return m_methodCalls.get( methodEntry ); + } + private String getMethodKey( String name, String signature ) { return name + signature; diff --git a/src/cuchaz/enigma/analysis/MethodCallsTreeNode.java b/src/cuchaz/enigma/analysis/MethodCallsTreeNode.java new file mode 100644 index 00000000..dedfb2e7 --- /dev/null +++ b/src/cuchaz/enigma/analysis/MethodCallsTreeNode.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class MethodCallsTreeNode extends DefaultMutableTreeNode +{ + private static final long serialVersionUID = -3658163700783307520L; + + private Translator m_deobfuscatingTranslator; + private MethodEntry m_entry; + + public MethodCallsTreeNode( Translator deobfuscatingTranslator, MethodEntry entry ) + { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + } + + public MethodEntry getMethodEntry( ) + { + return m_entry; + } + + public String getDeobfClassName( ) + { + return m_deobfuscatingTranslator.translateClass( m_entry.getClassName() ); + } + + public String getDeobfMethodName( ) + { + return m_deobfuscatingTranslator.translate( m_entry ); + } + + @Override + public String toString( ) + { + String className = getDeobfClassName(); + if( className == null ) + { + className = m_entry.getClassName(); + } + + String methodName = getDeobfMethodName(); + if( methodName == null ) + { + methodName = m_entry.getName(); + } + return className + "." + methodName + "()"; + } + + public void load( JarIndex index, boolean recurse ) + { + // get all the child nodes + List nodes = Lists.newArrayList(); + for( MethodEntry entry : index.getMethodCallers( m_entry ) ) + { + nodes.add( new MethodCallsTreeNode( m_deobfuscatingTranslator, entry ) ); + } + + // add them to this node + for( MethodCallsTreeNode node : nodes ) + { + this.add( node ); + } + + if( recurse ) + { + for( MethodCallsTreeNode node : nodes ) + { + // don't recurse into self + if( node.getMethodEntry().equals( m_entry ) ) + { + continue; + } + + node.load( index, true ); + } + } + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 3e8a18cb..072fb3a5 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -67,6 +67,7 @@ import com.google.common.collect.Lists; import cuchaz.enigma.Constants; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.MethodCallsTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ArgumentEntry; @@ -139,6 +140,8 @@ public class Gui private BoxHighlightPainter m_obfuscatedHighlightPainter; private BoxHighlightPainter m_deobfuscatedHighlightPainter; private JTree m_inheritanceTree; + private JTree m_callsTree; + private JTabbedPane m_tabs; // dynamic menu items private JMenuItem m_closeJarMenu; @@ -147,9 +150,10 @@ public class Gui private JMenuItem m_saveMappingsAsMenu; private JMenuItem m_closeMappingsMenu; private JMenuItem m_renameMenu; - private JMenuItem m_inheritanceMenu; + private JMenuItem m_showInheritanceMenu; private JMenuItem m_openEntryMenu; private JMenuItem m_openPreviousMenu; + private JMenuItem m_showCallsMenu; // state private EntryPair m_selectedEntryPair; @@ -267,6 +271,10 @@ public class Gui case KeyEvent.VK_P: m_controller.openPreviousEntry(); break; + + case KeyEvent.VK_C: + showCalls(); + break; } } } ); @@ -306,7 +314,22 @@ public class Gui menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_I, 0 ) ); menu.setEnabled( false ); popupMenu.add( menu ); - m_inheritanceMenu = menu; + m_showInheritanceMenu = menu; + } + { + JMenuItem menu = new JMenuItem( "Show Calls" ); + menu.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + showCalls(); + } + } ); + menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_C, 0 ) ); + menu.setEnabled( false ); + popupMenu.add( menu ); + m_showCallsMenu = menu; } { JMenuItem menu = new JMenuItem( "Go to Declaration" ); @@ -350,12 +373,13 @@ public class Gui if( event.getClickCount() == 2 ) { // get the selected node - Object node = m_inheritanceTree.getSelectionPath().getLastPathComponent(); - if( node == null ) + TreePath path = m_inheritanceTree.getSelectionPath(); + if( path == null ) { return; } + Object node = path.getLastPathComponent(); if( node instanceof ClassInheritanceTreeNode ) { m_controller.openEntry( new ClassEntry( ((ClassInheritanceTreeNode)node).getObfClassName() ) ); @@ -375,17 +399,47 @@ public class Gui inheritancePanel.setLayout( new BorderLayout() ); inheritancePanel.add( new JScrollPane( m_inheritanceTree ) ); + // init call panel + m_callsTree = new JTree(); + m_callsTree.setModel( null ); + m_callsTree.addMouseListener( new MouseAdapter( ) + { + @Override + public void mouseClicked( MouseEvent event ) + { + if( event.getClickCount() == 2 ) + { + // get the selected node + TreePath path = m_callsTree.getSelectionPath(); + if( path == null ) + { + return; + } + + Object node = path.getLastPathComponent(); + if( node instanceof MethodCallsTreeNode ) + { + m_controller.openEntry( ((MethodCallsTreeNode)node).getMethodEntry() ); + } + } + } + } ); + JPanel callPanel = new JPanel(); + callPanel.setLayout( new BorderLayout() ); + callPanel.add( new JScrollPane( m_callsTree ) ); + // layout controls JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); - splitLeft.setPreferredSize( new Dimension( 200, 0 ) ); + splitLeft.setPreferredSize( new Dimension( 250, 0 ) ); JPanel centerPanel = new JPanel(); centerPanel.setLayout( new BorderLayout() ); centerPanel.add( m_infoPanel, BorderLayout.NORTH ); centerPanel.add( sourceScroller, BorderLayout.CENTER ); - JTabbedPane tabbedPane = new JTabbedPane(); - tabbedPane.setPreferredSize( new Dimension( 200, 0 ) ); - tabbedPane.addTab( "Inheritance", inheritancePanel ); - JSplitPane splitRight = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, tabbedPane ); + m_tabs = new JTabbedPane(); + m_tabs.setPreferredSize( new Dimension( 250, 0 ) ); + m_tabs.addTab( "Inheritance", inheritancePanel ); + m_tabs.addTab( "Method Calls", callPanel ); + JSplitPane splitRight = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, m_tabs ); splitRight.setResizeWeight( 1 ); // let the left side take all the slack splitRight.resetToPreferredSizes(); JSplitPane splitCenter = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight ); @@ -784,7 +838,8 @@ public class Gui showEntryPair( m_selectedEntryPair ); - m_inheritanceMenu.setEnabled( isClassEntry || isMethodEntry ); + m_showInheritanceMenu.setEnabled( isClassEntry || isMethodEntry ); + m_showCallsMenu.setEnabled( isMethodEntry ); m_openEntryMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry ); m_openPreviousMenu.setEnabled( m_controller.hasPreviousEntry() ); } @@ -879,6 +934,24 @@ public class Gui m_inheritanceTree.setSelectionRow( m_inheritanceTree.getRowForPath( path ) ); } + m_tabs.setSelectedIndex( 0 ); + redraw(); + } + + private void showCalls( ) + { + if( m_selectedEntryPair == null ) + { + return; + } + + if( m_selectedEntryPair.obf instanceof MethodEntry ) + { + MethodCallsTreeNode node = m_controller.getMethodCalls( (MethodEntry)m_selectedEntryPair.obf ); + m_callsTree.setModel( new DefaultTreeModel( node ) ); + } + + m_tabs.setSelectedIndex( 1 ); redraw(); } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 880f001f..b54aeba3 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -21,6 +21,7 @@ import com.google.common.collect.Lists; import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.MethodCallsTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.Token; @@ -148,6 +149,16 @@ public class GuiController return MethodInheritanceTreeNode.findNode( rootNode, obfMethodEntry ); } + public MethodCallsTreeNode getMethodCalls( MethodEntry obfMethodEntry ) + { + MethodCallsTreeNode rootNode = new MethodCallsTreeNode( + m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + obfMethodEntry + ); + rootNode.load( m_deobfuscator.getJarIndex(), true ); + return rootNode; + } + public void rename( Entry obfEntry, String newName ) { m_deobfuscator.rename( obfEntry, newName ); -- cgit v1.2.3 From cc74d0e62cfdcf14c5918234f69d587d264807ed Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 13 Aug 2014 00:22:12 -0400 Subject: added support for field access searches added proper detection/handling for constructors --- src/cuchaz/enigma/Deobfuscator.java | 30 +++++- src/cuchaz/enigma/analysis/FieldCallsTreeNode.java | 82 +++++++++++++++ src/cuchaz/enigma/analysis/JarIndex.java | 107 +++++++++++++++---- .../enigma/analysis/MethodCallsTreeNode.java | 114 +++++++++++++++------ src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 5 +- src/cuchaz/enigma/gui/Gui.java | 60 ++++++++--- src/cuchaz/enigma/gui/GuiController.java | 38 ++++++- src/cuchaz/enigma/mapping/ConstructorEntry.java | 94 +++++++++++++++++ src/cuchaz/enigma/mapping/Translator.java | 8 ++ 9 files changed, 460 insertions(+), 78 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/FieldCallsTreeNode.java create mode 100644 src/cuchaz/enigma/mapping/ConstructorEntry.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 33eef08a..770172e3 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -34,6 +34,7 @@ import cuchaz.enigma.analysis.SourceIndexVisitor; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Mappings; @@ -185,6 +186,10 @@ public class Deobfuscator { m_renamer.setMethodTreeName( (MethodEntry)obfEntry, newName ); } + else if( obfEntry instanceof ConstructorEntry ) + { + m_renamer.setClassName( obfEntry.getClassEntry(), newName ); + } else if( obfEntry instanceof ArgumentEntry ) { m_renamer.setArgumentName( (ArgumentEntry)obfEntry, newName ); @@ -210,6 +215,10 @@ public class Deobfuscator { return translator.translateEntry( (MethodEntry)deobfEntry ); } + else if( deobfEntry instanceof ConstructorEntry ) + { + return translator.translateEntry( (ConstructorEntry)deobfEntry ); + } else if( deobfEntry instanceof ArgumentEntry ) { return translator.translateEntry( (ArgumentEntry)deobfEntry ); @@ -235,6 +244,10 @@ public class Deobfuscator { return translator.translateEntry( (MethodEntry)obfEntry ); } + else if( obfEntry instanceof ConstructorEntry ) + { + return translator.translateEntry( (ConstructorEntry)obfEntry ); + } else if( obfEntry instanceof ArgumentEntry ) { return translator.translateEntry( (ArgumentEntry)obfEntry ); @@ -263,6 +276,11 @@ public class Deobfuscator String deobfName = translator.translate( (MethodEntry)obfEntry ); return deobfName != null && !deobfName.equals( obfEntry.getName() ); } + else if( obfEntry instanceof ConstructorEntry ) + { + String deobfName = translator.translate( obfEntry.getClassEntry() ); + return deobfName != null && !deobfName.equals( obfEntry.getClassName() ); + } else if( obfEntry instanceof ArgumentEntry ) { return translator.translate( (ArgumentEntry)obfEntry ) != null; @@ -282,16 +300,20 @@ public class Deobfuscator } else if( obfEntry instanceof FieldEntry ) { - return m_jarIndex.getObfClassNames().contains( ((FieldEntry)obfEntry).getClassName() ); + return m_jarIndex.getObfClassNames().contains( obfEntry.getClassName() ); } else if( obfEntry instanceof MethodEntry ) { - return m_jarIndex.getObfClassNames().contains( ((MethodEntry)obfEntry).getClassName() ); + return m_jarIndex.getObfClassNames().contains( obfEntry.getClassName() ); + } + else if( obfEntry instanceof ConstructorEntry ) + { + return m_jarIndex.getObfClassNames().contains( obfEntry.getClassName() ); } else if( obfEntry instanceof ArgumentEntry ) { - // arguments only appear in method delcarations - // since we only show declrations for obf classes, these are always obfuscated + // arguments only appear in method declarations + // since we only show declarations for obf classes, these are always obfuscated return true; } diff --git a/src/cuchaz/enigma/analysis/FieldCallsTreeNode.java b/src/cuchaz/enigma/analysis/FieldCallsTreeNode.java new file mode 100644 index 00000000..0427b3be --- /dev/null +++ b/src/cuchaz/enigma/analysis/FieldCallsTreeNode.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import javax.swing.tree.DefaultMutableTreeNode; + +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class FieldCallsTreeNode extends DefaultMutableTreeNode +{ + private static final long serialVersionUID = -7934108091928699835L; + + private Translator m_deobfuscatingTranslator; + private FieldEntry m_entry; + + public FieldCallsTreeNode( Translator deobfuscatingTranslator, FieldEntry fieldEntry ) + { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = fieldEntry; + } + + public FieldEntry getFieldEntry( ) + { + return m_entry; + } + + @Override + public String toString( ) + { + String className = m_deobfuscatingTranslator.translateClass( m_entry.getClassName() ); + if( className == null ) + { + className = m_entry.getClassName(); + } + + String targetName = m_deobfuscatingTranslator.translate( m_entry ); + if( targetName == null ) + { + targetName = m_entry.getName(); + } + return className + "." + targetName; + } + + public void load( JarIndex index, boolean recurse ) + { + // get all the child nodes + for( Entry entry : index.getFieldCallers( m_entry ) ) + { + if( entry instanceof MethodEntry ) + { + add( new MethodCallsTreeNode( m_deobfuscatingTranslator, (MethodEntry)entry ) ); + } + else if( entry instanceof ConstructorEntry ) + { + add( new MethodCallsTreeNode( m_deobfuscatingTranslator, (ConstructorEntry)entry ) ); + } + } + + if( recurse && children != null ) + { + for( Object node : children ) + { + if( node instanceof MethodCallsTreeNode ) + { + ((MethodCallsTreeNode)node).load( index, true ); + } + } + } + } +} diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 06b01736..96bddc17 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -27,17 +27,26 @@ import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtBehavior; import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtMethod; import javassist.NotFoundException; import javassist.bytecode.Descriptor; +import javassist.expr.ConstructorCall; import javassist.expr.ExprEditor; +import javassist.expr.FieldAccess; import javassist.expr.MethodCall; +import javassist.expr.NewExpr; import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import cuchaz.enigma.Constants; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; @@ -46,7 +55,8 @@ public class JarIndex private Set m_obfClassNames; private Ancestries m_ancestries; private Multimap m_methodImplementations; - private Multimap m_methodCalls; + private Multimap m_methodCalls; + private Multimap m_fieldCalls; public JarIndex( JarFile jar ) { @@ -54,6 +64,7 @@ public class JarIndex m_ancestries = new Ancestries(); m_methodImplementations = HashMultimap.create(); m_methodCalls = HashMultimap.create(); + m_fieldCalls = HashMultimap.create(); // read the class names Enumeration enumeration = jar.entries(); @@ -133,14 +144,30 @@ public class JarIndex { // get the method entry String className = Descriptor.toJvmName( behavior.getDeclaringClass().getName() ); - final MethodEntry methodEntry = new MethodEntry( - new ClassEntry( className ), - behavior.getName(), - behavior.getSignature() - ); - - // index implementation - m_methodImplementations.put( className, methodEntry ); + final Entry thisEntry; + if( behavior instanceof CtMethod ) + { + MethodEntry methodEntry = new MethodEntry( + new ClassEntry( className ), + behavior.getName(), + behavior.getSignature() + ); + thisEntry = methodEntry; + + // index implementation + m_methodImplementations.put( className, methodEntry ); + } + else if( behavior instanceof CtConstructor ) + { + thisEntry = new ConstructorEntry( + new ClassEntry( className ), + behavior.getSignature() + ); + } + else + { + throw new IllegalArgumentException( "behavior must be a method or a constructor!" ); + } // index method calls try @@ -150,20 +177,53 @@ public class JarIndex @Override public void edit( MethodCall call ) { - // is this a jar class? String className = Descriptor.toJvmName( call.getClassName() ); - if( !m_obfClassNames.contains( className ) ) - { - return; - } - - // make entry for the called method MethodEntry calledMethodEntry = new MethodEntry( new ClassEntry( className ), call.getMethodName(), call.getSignature() ); - m_methodCalls.put( calledMethodEntry, methodEntry ); + m_methodCalls.put( calledMethodEntry, thisEntry ); + } + + @Override + public void edit( FieldAccess call ) + { + String className = Descriptor.toJvmName( call.getClassName() ); + FieldEntry calledFieldEntry = new FieldEntry( + new ClassEntry( className ), + call.getFieldName() + ); + m_fieldCalls.put( calledFieldEntry, thisEntry ); + } + + @Override + public void edit( ConstructorCall call ) + { + String className = Descriptor.toJvmName( call.getClassName() ); + ConstructorEntry calledConstructorEntry = new ConstructorEntry( + new ClassEntry( className ), + call.getSignature() + ); + m_methodCalls.put( calledConstructorEntry, thisEntry ); + } + + @Override + public void edit( NewExpr call ) + { + String className = Descriptor.toJvmName( call.getClassName() ); + ConstructorEntry calledConstructorEntry = new ConstructorEntry( + new ClassEntry( className ), + call.getSignature() + ); + + // TEMP + if( className.equals( "bgw" ) ) + { + System.out.println( calledConstructorEntry + " called by " + thisEntry ); + } + + m_methodCalls.put( calledConstructorEntry, thisEntry ); } } ); } @@ -202,7 +262,9 @@ public class JarIndex public ClassInheritanceTreeNode getClassInheritance( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) { // get the root node - List ancestry = m_ancestries.getAncestry( obfClassEntry.getName() ); + List ancestry = Lists.newArrayList(); + ancestry.add( obfClassEntry.getName() ); + ancestry.addAll( m_ancestries.getAncestry( obfClassEntry.getName() ) ); ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, ancestry.get( ancestry.size() - 1 ) ); // expand all children recursively @@ -241,9 +303,14 @@ public class JarIndex return rootNode; } - public Collection getMethodCallers( MethodEntry methodEntry ) + public Collection getFieldCallers( FieldEntry fieldEntry ) + { + return m_fieldCalls.get( fieldEntry ); + } + + public Collection getMethodCallers( Entry entry ) { - return m_methodCalls.get( methodEntry ); + return m_methodCalls.get( entry ); } private String getMethodKey( String name, String signature ) diff --git a/src/cuchaz/enigma/analysis/MethodCallsTreeNode.java b/src/cuchaz/enigma/analysis/MethodCallsTreeNode.java index dedfb2e7..b5cf4c33 100644 --- a/src/cuchaz/enigma/analysis/MethodCallsTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodCallsTreeNode.java @@ -10,12 +10,15 @@ ******************************************************************************/ package cuchaz.enigma.analysis; -import java.util.List; +import java.util.Set; import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; -import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; @@ -24,72 +27,117 @@ public class MethodCallsTreeNode extends DefaultMutableTreeNode private static final long serialVersionUID = -3658163700783307520L; private Translator m_deobfuscatingTranslator; - private MethodEntry m_entry; + private MethodEntry m_methodEntry; + private ConstructorEntry m_constructorEntry; public MethodCallsTreeNode( Translator deobfuscatingTranslator, MethodEntry entry ) { m_deobfuscatingTranslator = deobfuscatingTranslator; - m_entry = entry; + m_methodEntry = entry; + m_constructorEntry = null; + } + + public MethodCallsTreeNode( Translator deobfuscatingTranslator, ConstructorEntry entry ) + { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_methodEntry = null; + m_constructorEntry = entry; } public MethodEntry getMethodEntry( ) { - return m_entry; + return m_methodEntry; } - public String getDeobfClassName( ) + public ConstructorEntry getConstructorEntry( ) { - return m_deobfuscatingTranslator.translateClass( m_entry.getClassName() ); + return m_constructorEntry; } - public String getDeobfMethodName( ) + public Entry getEntry( ) { - return m_deobfuscatingTranslator.translate( m_entry ); + if( m_methodEntry != null ) + { + return m_methodEntry; + } + else if( m_constructorEntry != null ) + { + return m_constructorEntry; + } + throw new Error( "Illegal state!" ); } @Override public String toString( ) { - String className = getDeobfClassName(); - if( className == null ) + if( m_methodEntry != null ) { - className = m_entry.getClassName(); + String className = m_deobfuscatingTranslator.translateClass( m_methodEntry.getClassName() ); + if( className == null ) + { + className = m_methodEntry.getClassName(); + } + + String methodName = m_deobfuscatingTranslator.translate( m_methodEntry ); + if( methodName == null ) + { + methodName = m_methodEntry.getName(); + } + return className + "." + methodName + "()"; } - - String methodName = getDeobfMethodName(); - if( methodName == null ) + else if( m_constructorEntry != null ) { - methodName = m_entry.getName(); + String className = m_deobfuscatingTranslator.translateClass( m_constructorEntry.getClassName() ); + if( className == null ) + { + className = m_constructorEntry.getClassName(); + } + return className + "()"; } - return className + "." + methodName + "()"; + throw new Error( "Illegal state!" ); } public void load( JarIndex index, boolean recurse ) { // get all the child nodes - List nodes = Lists.newArrayList(); - for( MethodEntry entry : index.getMethodCallers( m_entry ) ) + for( Entry entry : index.getMethodCallers( getEntry() ) ) { - nodes.add( new MethodCallsTreeNode( m_deobfuscatingTranslator, entry ) ); - } - - // add them to this node - for( MethodCallsTreeNode node : nodes ) - { - this.add( node ); + if( entry instanceof MethodEntry ) + { + add( new MethodCallsTreeNode( m_deobfuscatingTranslator, (MethodEntry)entry ) ); + } + else if( entry instanceof ConstructorEntry ) + { + add( new MethodCallsTreeNode( m_deobfuscatingTranslator, (ConstructorEntry)entry ) ); + } } - if( recurse ) + if( recurse && children != null ) { - for( MethodCallsTreeNode node : nodes ) + for( Object child : children ) { - // don't recurse into self - if( node.getMethodEntry().equals( m_entry ) ) + if( child instanceof MethodCallsTreeNode ) { - continue; + MethodCallsTreeNode node = (MethodCallsTreeNode)child; + + // don't recurse into ancestor + Set ancestors = Sets.newHashSet(); + TreeNode n = (TreeNode)node; + while( n.getParent() != null ) + { + n = n.getParent(); + if( n instanceof MethodCallsTreeNode ) + { + ancestors.add( ((MethodCallsTreeNode)n).getEntry() ); + } + } + if( ancestors.contains( node.getEntry() ) ) + { + continue; + } + + node.load( index, true ); } - - node.load( index, true ); } } } diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java index 0ba5996c..6c14ee99 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -93,6 +93,7 @@ import com.strobel.decompiler.patterns.Pattern; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; @@ -158,7 +159,9 @@ public class SourceIndexVisitor implements IAstVisitor public Void visitConstructorDeclaration( ConstructorDeclaration node, SourceIndex index ) { MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); - index.add( node.getNameToken(), new ClassEntry( def.getDeclaringType().getInternalName() ) ); + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, def.getSignature() ); + index.addDeclaration( node.getNameToken(), constructorEntry ); return recurse( node, index ); } diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 072fb3a5..1d2d3ab1 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -67,11 +67,13 @@ import com.google.common.collect.Lists; import cuchaz.enigma.Constants; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.FieldCallsTreeNode; import cuchaz.enigma.analysis.MethodCallsTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.FieldEntry; @@ -419,7 +421,7 @@ public class Gui Object node = path.getLastPathComponent(); if( node instanceof MethodCallsTreeNode ) { - m_controller.openEntry( ((MethodCallsTreeNode)node).getMethodEntry() ); + m_controller.openEntry( ((MethodCallsTreeNode)node).getEntry() ); } } } @@ -438,7 +440,7 @@ public class Gui m_tabs = new JTabbedPane(); m_tabs.setPreferredSize( new Dimension( 250, 0 ) ); m_tabs.addTab( "Inheritance", inheritancePanel ); - m_tabs.addTab( "Method Calls", callPanel ); + m_tabs.addTab( "Call Graph", callPanel ); JSplitPane splitRight = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, m_tabs ); splitRight.setResizeWeight( 1 ); // let the left side take all the slack splitRight.resetToPreferredSizes(); @@ -770,6 +772,10 @@ public class Gui { showMethodEntryPair( (EntryPair)pair ); } + else if( pair.deobf instanceof ConstructorEntry ) + { + showConstructorEntryPair( (EntryPair)pair ); + } else if( pair.deobf instanceof ArgumentEntry ) { showArgumentEntryPair( (EntryPair)pair ); @@ -800,6 +806,12 @@ public class Gui addNameValue( m_infoPanel, "Signature", pair.deobf.getSignature() ); } + private void showConstructorEntryPair( EntryPair pair ) + { + addNameValue( m_infoPanel, "Constructor", pair.deobf.getClassEntry().getName() ); + addNameValue( m_infoPanel, "Signature", pair.deobf.getSignature() ); + } + private void showArgumentEntryPair( EntryPair pair ) { addNameValue( m_infoPanel, "Argument", pair.deobf.getName() ); @@ -815,7 +827,7 @@ public class Gui container.add( panel ); JLabel label = new JLabel( name + ":", JLabel.RIGHT ); - label.setPreferredSize( new Dimension( 80, label.getPreferredSize().height ) ); + label.setPreferredSize( new Dimension( 100, label.getPreferredSize().height ) ); panel.add( label ); panel.add( unboldLabel( new JLabel( value, JLabel.LEFT ) ) ); @@ -824,23 +836,27 @@ public class Gui private void onCaretMove( int pos ) { Token token = m_controller.getToken( pos ); - m_renameMenu.setEnabled( token != null ); - if( token == null ) - { - clearEntryPair(); - return; - } + boolean isToken = token != null; m_selectedEntryPair = m_controller.getEntryPair( token ); - boolean isClassEntry = m_selectedEntryPair.obf instanceof ClassEntry; - boolean isFieldEntry = m_selectedEntryPair.obf instanceof FieldEntry; - boolean isMethodEntry = m_selectedEntryPair.obf instanceof MethodEntry; + boolean isClassEntry = isToken && m_selectedEntryPair.obf instanceof ClassEntry; + boolean isFieldEntry = isToken && m_selectedEntryPair.obf instanceof FieldEntry; + boolean isMethodEntry = isToken && m_selectedEntryPair.obf instanceof MethodEntry; + boolean isConstructorEntry = isToken && m_selectedEntryPair.obf instanceof ConstructorEntry; - showEntryPair( m_selectedEntryPair ); + if( isToken ) + { + showEntryPair( m_selectedEntryPair ); + } + else + { + clearEntryPair(); + } - m_showInheritanceMenu.setEnabled( isClassEntry || isMethodEntry ); - m_showCallsMenu.setEnabled( isMethodEntry ); - m_openEntryMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry ); + m_renameMenu.setEnabled( isToken ); + m_showInheritanceMenu.setEnabled( isClassEntry || isMethodEntry || isConstructorEntry ); + m_showCallsMenu.setEnabled( isFieldEntry || isMethodEntry || isConstructorEntry ); + m_openEntryMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ); m_openPreviousMenu.setEnabled( m_controller.hasPreviousEntry() ); } @@ -945,11 +961,21 @@ public class Gui return; } - if( m_selectedEntryPair.obf instanceof MethodEntry ) + if( m_selectedEntryPair.obf instanceof FieldEntry ) + { + FieldCallsTreeNode node = m_controller.getFieldCalls( (FieldEntry)m_selectedEntryPair.obf ); + m_callsTree.setModel( new DefaultTreeModel( node ) ); + } + else if( m_selectedEntryPair.obf instanceof MethodEntry ) { MethodCallsTreeNode node = m_controller.getMethodCalls( (MethodEntry)m_selectedEntryPair.obf ); m_callsTree.setModel( new DefaultTreeModel( node ) ); } + else if( m_selectedEntryPair.obf instanceof ConstructorEntry ) + { + MethodCallsTreeNode node = m_controller.getMethodCalls( (ConstructorEntry)m_selectedEntryPair.obf ); + m_callsTree.setModel( new DefaultTreeModel( node ) ); + } m_tabs.setSelectedIndex( 1 ); redraw(); diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index b54aeba3..534b0cc5 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -21,13 +21,16 @@ import com.google.common.collect.Lists; import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.FieldCallsTreeNode; import cuchaz.enigma.analysis.MethodCallsTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; +import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; import cuchaz.enigma.mapping.MethodEntry; @@ -118,6 +121,10 @@ public class GuiController } Entry deobfEntry = m_index.getEntry( token ); + if( deobfEntry == null ) + { + return null; + } return new EntryPair( m_deobfuscator.obfuscateEntry( deobfEntry ), deobfEntry ); } @@ -149,16 +156,41 @@ public class GuiController return MethodInheritanceTreeNode.findNode( rootNode, obfMethodEntry ); } - public MethodCallsTreeNode getMethodCalls( MethodEntry obfMethodEntry ) + public FieldCallsTreeNode getFieldCalls( FieldEntry obfFieldEntry ) { - MethodCallsTreeNode rootNode = new MethodCallsTreeNode( + FieldCallsTreeNode rootNode = new FieldCallsTreeNode( m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), - obfMethodEntry + obfFieldEntry ); rootNode.load( m_deobfuscator.getJarIndex(), true ); return rootNode; } + public MethodCallsTreeNode getMethodCalls( Entry obfEntry ) + { + MethodCallsTreeNode rootNode; + if( obfEntry instanceof MethodEntry ) + { + rootNode = new MethodCallsTreeNode( + m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + (MethodEntry)obfEntry + ); + } + else if( obfEntry instanceof ConstructorEntry ) + { + rootNode = new MethodCallsTreeNode( + m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + (ConstructorEntry)obfEntry + ); + } + else + { + throw new IllegalArgumentException( "entry must be a MethodEntry or a ConstructorEntry!" ); + } + rootNode.load( m_deobfuscator.getJarIndex(), true ); + return rootNode; + } + public void rename( Entry obfEntry, String newName ) { m_deobfuscator.rename( obfEntry, newName ); diff --git a/src/cuchaz/enigma/mapping/ConstructorEntry.java b/src/cuchaz/enigma/mapping/ConstructorEntry.java new file mode 100644 index 00000000..e0fa7cf6 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ConstructorEntry.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +import cuchaz.enigma.Util; + +public class ConstructorEntry implements Entry, Serializable +{ + private static final long serialVersionUID = -868346075317366758L; + + private ClassEntry m_classEntry; + private String m_signature; + + public ConstructorEntry( ClassEntry classEntry, String signature ) + { + if( classEntry == null ) + { + throw new IllegalArgumentException( "Class cannot be null!" ); + } + if( signature == null ) + { + throw new IllegalArgumentException( "Method signature cannot be null!" ); + } + + m_classEntry = classEntry; + m_signature = signature; + } + + public ConstructorEntry( ConstructorEntry other ) + { + m_classEntry = new ClassEntry( other.m_classEntry ); + m_signature = other.m_signature; + } + + @Override + public ClassEntry getClassEntry( ) + { + return m_classEntry; + } + + @Override + public String getName( ) + { + return m_classEntry.getName(); + } + + public String getSignature( ) + { + return m_signature; + } + + @Override + public String getClassName( ) + { + return m_classEntry.getName(); + } + + @Override + public int hashCode( ) + { + return Util.combineHashesOrdered( m_classEntry, m_signature ); + } + + @Override + public boolean equals( Object other ) + { + if( other instanceof ConstructorEntry ) + { + return equals( (ConstructorEntry)other ); + } + return false; + } + + public boolean equals( ConstructorEntry other ) + { + return m_classEntry.equals( other.m_classEntry ) && m_signature.equals( other.m_signature ); + } + + @Override + public String toString( ) + { + return m_classEntry.getName() + m_signature; + } +} diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 02565c94..50433214 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -135,6 +135,14 @@ public class Translator ); } + public ConstructorEntry translateEntry( ConstructorEntry in ) + { + return new ConstructorEntry( + translateEntry( in.getClassEntry() ), + translateSignature( in.getSignature() ) + ); + } + public String translate( ArgumentEntry in ) { for( String className : getSelfAndAncestors( in.getClassName() ) ) -- cgit v1.2.3 From c4861bf517bedfd68cbc440a22f392c09a8ab73c Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 14 Aug 2014 00:03:57 -0400 Subject: updated ignore list --- .hgignore | 6 +++++- libs/procyon-decompiler-0.5.25.jar | Bin 1808653 -> 0 bytes 2 files changed, 5 insertions(+), 1 deletion(-) delete mode 100644 libs/procyon-decompiler-0.5.25.jar diff --git a/.hgignore b/.hgignore index 7e666d25..95812531 100644 --- a/.hgignore +++ b/.hgignore @@ -6,4 +6,8 @@ syntax: regexp syntax: regexp ^\.gradle$ syntax: regexp -^\.settings$ \ No newline at end of file +^\.settings$ +syntax: regexp +^libs$ +syntax: regexp +^build$ \ No newline at end of file diff --git a/libs/procyon-decompiler-0.5.25.jar b/libs/procyon-decompiler-0.5.25.jar deleted file mode 100644 index 04dda91d..00000000 Binary files a/libs/procyon-decompiler-0.5.25.jar and /dev/null differ -- cgit v1.2.3 From a7f865f1b7b790be4d4e53e0d24b5c18b3ceb1f8 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 14 Aug 2014 00:04:24 -0400 Subject: fixed javac compile errors --- src/cuchaz/enigma/gui/Gui.java | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 1d2d3ab1..3b67a329 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -158,7 +158,7 @@ public class Gui private JMenuItem m_showCallsMenu; // state - private EntryPair m_selectedEntryPair; + private EntryPair m_selectedEntryPair; private JFileChooser m_jarFileChooser; private JFileChooser m_mappingsFileChooser; @@ -749,7 +749,7 @@ public class Gui } @SuppressWarnings( "unchecked" ) - private void showEntryPair( EntryPair pair ) + private void showEntryPair( EntryPair pair ) { if( pair == null ) { @@ -762,23 +762,23 @@ public class Gui m_infoPanel.removeAll(); if( pair.deobf instanceof ClassEntry ) { - showClassEntryPair( (EntryPair)pair ); + showClassEntryPair( (EntryPair)pair ); } else if( pair.deobf instanceof FieldEntry ) { - showFieldEntryPair( (EntryPair)pair ); + showFieldEntryPair( (EntryPair)pair ); } else if( pair.deobf instanceof MethodEntry ) { - showMethodEntryPair( (EntryPair)pair ); + showMethodEntryPair( (EntryPair)pair ); } else if( pair.deobf instanceof ConstructorEntry ) { - showConstructorEntryPair( (EntryPair)pair ); + showConstructorEntryPair( (EntryPair)pair ); } else if( pair.deobf instanceof ArgumentEntry ) { - showArgumentEntryPair( (EntryPair)pair ); + showArgumentEntryPair( (EntryPair)pair ); } else { @@ -788,31 +788,31 @@ public class Gui redraw(); } - private void showClassEntryPair( EntryPair pair ) + private void showClassEntryPair( EntryPair pair ) { addNameValue( m_infoPanel, "Class", pair.deobf.getName() ); } - private void showFieldEntryPair( EntryPair pair ) + private void showFieldEntryPair( EntryPair pair ) { addNameValue( m_infoPanel, "Field", pair.deobf.getName() ); addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() ); } - private void showMethodEntryPair( EntryPair pair ) + private void showMethodEntryPair( EntryPair pair ) { addNameValue( m_infoPanel, "Method", pair.deobf.getName() ); addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() ); addNameValue( m_infoPanel, "Signature", pair.deobf.getSignature() ); } - private void showConstructorEntryPair( EntryPair pair ) + private void showConstructorEntryPair( EntryPair pair ) { addNameValue( m_infoPanel, "Constructor", pair.deobf.getClassEntry().getName() ); addNameValue( m_infoPanel, "Signature", pair.deobf.getSignature() ); } - private void showArgumentEntryPair( EntryPair pair ) + private void showArgumentEntryPair( EntryPair pair ) { addNameValue( m_infoPanel, "Argument", pair.deobf.getName() ); addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() ); -- cgit v1.2.3 From a7c1b0e8fa8866b40e693f3999acf23fa0eb41da Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 14 Aug 2014 00:05:02 -0400 Subject: updated license info, version, added readme --- license.APL2.txt | 55 ++++ license.GPL3.txt | 674 +++++++++++++++++++++++++++++++++++++++ license.txt | 674 --------------------------------------- readme.txt | 19 ++ src/cuchaz/enigma/Constants.java | 2 +- 5 files changed, 749 insertions(+), 675 deletions(-) create mode 100644 license.APL2.txt create mode 100644 license.GPL3.txt delete mode 100644 license.txt create mode 100644 readme.txt diff --git a/license.APL2.txt b/license.APL2.txt new file mode 100644 index 00000000..a453e432 --- /dev/null +++ b/license.APL2.txt @@ -0,0 +1,55 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + + +You must cause any modified files to carry prominent notices stating that You changed the files; and + + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/license.GPL3.txt b/license.GPL3.txt new file mode 100644 index 00000000..20d40b6b --- /dev/null +++ b/license.GPL3.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/license.txt b/license.txt deleted file mode 100644 index 20d40b6b..00000000 --- a/license.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 00000000..37197c9d --- /dev/null +++ b/readme.txt @@ -0,0 +1,19 @@ + +Enigma v0.1 beta +A tool for deobfuscation of Java bytecode + +Copyright Jeff Martin, 2014 + + +LICENSE + +Enigma is distributed under the GNU General Public license version 3 + +Enigma includes a modified version of Procyon which is distributed under the Apache license version 2. Procyon is copyrighted by Mike Strobel, 2013 + +Enigma includes unmodified versions of the following libraries which are also released under the Apache license version 2. + Guava + Javassist + JSyntaxPane + +Copies of the GNU General Public license verion 3 and the Apache license v2 have been included in this distribution. diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index a8c4f44c..72275628 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -14,7 +14,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.1"; + public static final String Version = "0.1 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024*1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte -- cgit v1.2.3 From 387456eec096e40682f30a5457d0f0aa3fff1b7d Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 14 Aug 2014 00:05:17 -0400 Subject: updated gradle build script to produce fat jar --- build.gradle | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index f95f1052..87a3c78b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,25 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath 'eu.appsatori:gradle-fatjar-plugin:0.2-rc1' + } +} + apply plugin: "java" apply plugin: "eclipse" apply plugin: "maven" +apply plugin: "fatjar" -sourceCompatibility = 1.6 -targetCompatibility = 1.6 +sourceCompatibility = 1.7 +targetCompatibility = 1.7 group = "com.cuchazinteractive" archivesBaseName = "enigma" -version = "0.1" +version = "0.1b" sourceSets { @@ -43,7 +54,6 @@ repositories dependencies { - compile files( "${System.properties['java.home']}/../lib/tools.jar" ) compile fileTree( dir: "libs", include: "*.jar" ) compile "de.sciss:jsyntaxpane:1.0.0" compile "com.google.guava:guava:17.0" @@ -52,10 +62,15 @@ dependencies testCompile "junit:junit:4.11" } -uploadArchives +fatJar { - repositories.mavenDeployer + manifest { - repository( url: "file://${project.projectDir}/../maven" ) + attributes( + "Title": archivesBaseName, + "Manifest-Version": "1.0", + "Version": version, + "Main-Class" : "cuchaz.enigma.Main" + ) } } -- cgit v1.2.3 From fa276baa63833d8dd64485cbc55126b0d983f478 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 14 Aug 2014 00:22:26 -0400 Subject: fixed far jar build task to properly include licenses --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 87a3c78b..a0b5bbc9 100644 --- a/build.gradle +++ b/build.gradle @@ -64,6 +64,9 @@ dependencies fatJar { + from ".", { + include "*.txt" + } manifest { attributes( -- cgit v1.2.3 -- cgit v1.2.3 From 06162e8d1627ac0d7a6cca79f3b5b4482fbcd37f Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 14 Aug 2014 00:33:28 -0400 Subject: remove old debug messages --- src/cuchaz/enigma/analysis/JarIndex.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 96bddc17..a0aa2955 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -216,13 +216,6 @@ public class JarIndex new ClassEntry( className ), call.getSignature() ); - - // TEMP - if( className.equals( "bgw" ) ) - { - System.out.println( calledConstructorEntry + " called by " + thisEntry ); - } - m_methodCalls.put( calledConstructorEntry, thisEntry ); } } ); -- cgit v1.2.3 From 580fbf899accb4181c1ca1e41abbcc2b7404c870 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 14 Aug 2014 00:46:16 -0400 Subject: fixed bug with method inheritance detection --- src/cuchaz/enigma/analysis/JarIndex.java | 22 ++++++++-------------- .../enigma/analysis/MethodInheritanceTreeNode.java | 2 +- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index a0aa2955..845be600 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -238,19 +238,13 @@ public class JarIndex public boolean isMethodImplemented( MethodEntry methodEntry ) { - return isMethodImplemented( methodEntry.getClassName(), methodEntry.getName(), methodEntry.getSignature() ); - } - - public boolean isMethodImplemented( String className, String methodName, String methodSignature ) - { - Collection implementations = m_methodImplementations.get( className ); + Collection implementations = m_methodImplementations.get( methodEntry.getClassName() ); if( implementations == null ) { return false; } - return implementations.contains( getMethodKey( methodName, methodSignature ) ); + return implementations.contains( methodEntry ); } - public ClassInheritanceTreeNode getClassInheritance( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) { @@ -272,7 +266,12 @@ public class JarIndex String baseImplementationClassName = obfMethodEntry.getClassName(); for( String ancestorClassName : m_ancestries.getAncestry( obfMethodEntry.getClassName() ) ) { - if( isMethodImplemented( ancestorClassName, obfMethodEntry.getName(), obfMethodEntry.getSignature() ) ) + MethodEntry ancestorMethodEntry = new MethodEntry( + new ClassEntry( ancestorClassName ), + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + if( isMethodImplemented( ancestorMethodEntry ) ) { baseImplementationClassName = ancestorClassName; } @@ -305,9 +304,4 @@ public class JarIndex { return m_methodCalls.get( entry ); } - - private String getMethodKey( String name, String signature ) - { - return name + signature; - } } diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index a28a9f46..73f9714c 100644 --- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -93,7 +93,7 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode nodes.add( new MethodInheritanceTreeNode( m_deobfuscatingTranslator, methodEntry, - index.isMethodImplemented( subclassName, m_entry.getName(), m_entry.getSignature() ) + index.isMethodImplemented( methodEntry ) ) ); } -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 From 37467e4a7b5e05e4da413a1e06e597fa806b72e4 Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 15 Aug 2014 01:43:48 -0400 Subject: trying to get inner/anonymous classes working... I have a working heuristic in place to detect anonymous classes, but I can't seem to get Procyon to decompile them correctly. I'm writing the InnerClasses attribute and translating all the inner class names, but there must be something else I'm missing... --- .hgignore | 4 +- src/cuchaz/enigma/Deobfuscator.java | 18 +-- src/cuchaz/enigma/TranslatingTypeLoader.java | 86 ++++++++--- src/cuchaz/enigma/analysis/JarClassIterator.java | 134 ++++++++++++++++ src/cuchaz/enigma/analysis/JarIndex.java | 187 ++++++++++++++--------- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 79 ++++++++++ 6 files changed, 399 insertions(+), 109 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/JarClassIterator.java create mode 100644 src/cuchaz/enigma/bytecode/InnerClassWriter.java diff --git a/.hgignore b/.hgignore index 95812531..ddf86ccd 100644 --- a/.hgignore +++ b/.hgignore @@ -10,4 +10,6 @@ syntax: regexp syntax: regexp ^libs$ syntax: regexp -^build$ \ No newline at end of file +^build$ +syntax: regexp +^data$ \ No newline at end of file diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 770172e3..127a0d98 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -11,9 +11,7 @@ package cuchaz.enigma; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.io.StringWriter; import java.util.List; import java.util.jar.JarFile; @@ -58,18 +56,9 @@ public class Deobfuscator m_file = file; m_jar = new JarFile( m_file ); - // build the ancestries - InputStream jarIn = null; - try - { - m_jarIndex = new JarIndex( m_jar ); - jarIn = new FileInputStream( m_file ); - m_jarIndex.indexJar( jarIn ); - } - finally - { - Util.closeQuietly( jarIn ); - } + // build the jar index + m_jarIndex = new JarIndex(); + m_jarIndex.indexJar( m_jar ); // config the decompiler m_settings = DecompilerSettings.javaDefaults(); @@ -105,6 +94,7 @@ public class Deobfuscator // update decompiler options m_settings.setTypeLoader( new TranslatingTypeLoader( m_jar, + m_jarIndex, getTranslator( TranslationDirection.Obfuscating ), getTranslator( TranslationDirection.Deobfuscating ) ) ); diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index f5112e0f..fdfcea0f 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -17,42 +17,77 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import javassist.ByteArrayClassPath; +import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; +import javassist.NotFoundException; import javassist.bytecode.Descriptor; import com.strobel.assembler.metadata.Buffer; import com.strobel.assembler.metadata.ITypeLoader; +import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.bytecode.ClassTranslator; +import cuchaz.enigma.bytecode.InnerClassWriter; import cuchaz.enigma.bytecode.MethodParameterWriter; import cuchaz.enigma.mapping.Translator; public class TranslatingTypeLoader implements ITypeLoader { private JarFile m_jar; + private JarIndex m_jarIndex; private Translator m_obfuscatingTranslator; private Translator m_deobfuscatingTranslator; - public TranslatingTypeLoader( JarFile jar, Translator obfuscatingTranslator, Translator deobfuscatingTranslator ) + public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator ) { m_jar = jar; + m_jarIndex = jarIndex; m_obfuscatingTranslator = obfuscatingTranslator; m_deobfuscatingTranslator = deobfuscatingTranslator; } @Override - public boolean tryLoadType( String name, Buffer out ) + public boolean tryLoadType( String deobfClassName, Buffer out ) { - // is this a deobufscated class name? - String obfName = m_obfuscatingTranslator.translateClass( name ); - if( obfName != null ) + // TEMP + if( !deobfClassName.startsWith( "java" ) && !deobfClassName.startsWith( "org" ) ) { - // point to the obfuscated class - name = obfName; + System.out.println( "Looking for: " + deobfClassName ); } - JarEntry entry = m_jar.getJarEntry( name + ".class" ); + // what class file should we actually load? + String obfClassName = m_obfuscatingTranslator.translateClass( deobfClassName ); + if( obfClassName == null ) + { + obfClassName = deobfClassName; + } + String classFileName = obfClassName; + + // is this a properly-referenced inner class? + boolean isInnerClass = deobfClassName.indexOf( '$' ) >= 0; + if( isInnerClass ) + { + // get just the bare inner class name + String[] parts = deobfClassName.split( "\\$" ); + String deobfClassFileName = parts[parts.length - 1]; + + // make sure the bare inner class name is obfuscated + classFileName = m_obfuscatingTranslator.translateClass( deobfClassFileName ); + if( classFileName == null ) + { + classFileName = deobfClassFileName; + } + } + + // TEMP + if( !deobfClassName.startsWith( "java" ) && !deobfClassName.startsWith( "org" ) ) + { + System.out.println( "\tLooking at class file: " + classFileName ); + } + + // get the jar entry + JarEntry entry = m_jar.getJarEntry( classFileName + ".class" ); if( entry == null ) { return false; @@ -73,32 +108,43 @@ public class TranslatingTypeLoader implements ITypeLoader } data.write( buf, 0, bytesRead ); } + data.close(); + in.close(); buf = data.toByteArray(); - // translate the class - String javaName = Descriptor.toJavaName( name ); + // load the javassist handle to the class + String javaClassFileName = Descriptor.toJavaName( classFileName ); ClassPool classPool = new ClassPool(); - classPool.insertClassPath( new ByteArrayClassPath( javaName, buf ) ); - try + classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); + CtClass c = classPool.get( javaClassFileName ); + + if( isInnerClass ) { - CtClass c = classPool.get( javaName ); - new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); - new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); - buf = c.toBytecode(); + // rename the class to what procyon expects + c.setName( deobfClassName ); } - catch( Exception ex ) + else { - throw new Error( ex ); + // maybe it's an outer class + new InnerClassWriter( m_deobfuscatingTranslator, m_jarIndex ).writeInnerClasses( c ); } - // pass it along to the decompiler + new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); + new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); + + assert( Descriptor.toJvmName( c.getName() ).equals( deobfClassName ) ); + assert( c.getClassFile().getName().equals( deobfClassName ) ); + + buf = c.toBytecode(); + + // pass the transformed class along to the decompiler out.reset( buf.length ); System.arraycopy( buf, 0, out.array(), out.position(), buf.length ); out.position( 0 ); return true; } - catch( IOException ex ) + catch( IOException | NotFoundException | CannotCompileException ex ) { throw new Error( ex ); } diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java new file mode 100644 index 00000000..cf6df805 --- /dev/null +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import javassist.ByteArrayClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.Descriptor; + +import com.beust.jcommander.internal.Lists; + +import cuchaz.enigma.Constants; + +public class JarClassIterator implements Iterator +{ + private JarFile m_jar; + private Iterator m_iter; + + public JarClassIterator( JarFile jar ) + { + this( jar, getClassEntries( jar ) ); + } + + public JarClassIterator( JarFile jar, List entries ) + { + m_jar = jar; + m_iter = entries.iterator(); + } + + @Override + public boolean hasNext( ) + { + return m_iter.hasNext(); + } + + @Override + public CtClass next( ) + { + JarEntry entry = m_iter.next(); + try + { + // read the class into a buffer + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buf = new byte[Constants.KiB]; + int totalNumBytesRead = 0; + InputStream in = m_jar.getInputStream( entry ); + while( in.available() > 0 ) + { + int numBytesRead = in.read( buf ); + if( numBytesRead < 0 ) + { + break; + } + bos.write( buf, 0, numBytesRead ); + + // sanity checking + totalNumBytesRead += numBytesRead; + if( totalNumBytesRead > Constants.MiB ) + { + throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" ); + } + } + + // determine the class name (ie chop off the ".class") + String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); + + // get a javassist handle for the class + ClassPool classPool = new ClassPool(); + classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); + return classPool.get( className ); + } + catch( IOException ex ) + { + throw new Error( "Unable to read class: " + entry.getName() ); + } + catch( NotFoundException ex ) + { + throw new Error( "Unable to load class: " + entry.getName() ); + } + } + + @Override + public void remove( ) + { + throw new UnsupportedOperationException(); + } + + public static List getClassEntries( JarFile jar ) + { + List classes = Lists.newArrayList(); + Enumeration entries = jar.entries(); + while( entries.hasMoreElements() ) + { + JarEntry entry = entries.nextElement(); + + // is this a class file? + if( !entry.isDirectory() && entry.getName().endsWith( ".class" ) ) + { + classes.add( entry ); + } + } + return classes; + } + + public static Iterable classes( final JarFile jar ) + { + return new Iterable( ) + { + @Override + public Iterator iterator( ) + { + return new JarClassIterator( jar ); + } + }; + } +} diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 845be600..9962bfaa 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -10,27 +10,21 @@ ******************************************************************************/ package cuchaz.enigma.analysis; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; import java.util.Collection; -import java.util.Enumeration; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import javassist.ByteArrayClassPath; import javassist.CannotCompileException; -import javassist.ClassPool; import javassist.CtBehavior; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtMethod; -import javassist.NotFoundException; +import javassist.bytecode.AccessFlag; import javassist.bytecode.Descriptor; +import javassist.bytecode.FieldInfo; import javassist.expr.ConstructorCall; import javassist.expr.ExprEditor; import javassist.expr.FieldAccess; @@ -39,10 +33,10 @@ import javassist.expr.NewExpr; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; -import cuchaz.enigma.Constants; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; @@ -57,89 +51,52 @@ public class JarIndex private Multimap m_methodImplementations; private Multimap m_methodCalls; private Multimap m_fieldCalls; + private Multimap m_innerClasses; + private Map m_outerClasses; - public JarIndex( JarFile jar ) + public JarIndex( ) { m_obfClassNames = Sets.newHashSet(); m_ancestries = new Ancestries(); m_methodImplementations = HashMultimap.create(); m_methodCalls = HashMultimap.create(); m_fieldCalls = HashMultimap.create(); - - // read the class names - Enumeration enumeration = jar.entries(); - while( enumeration.hasMoreElements() ) + m_innerClasses = HashMultimap.create(); + m_outerClasses = Maps.newHashMap(); + } + + public void indexJar( JarFile jar ) + { + // pass 1: read the class names + for( JarEntry entry : JarClassIterator.getClassEntries( jar ) ) { - JarEntry entry = enumeration.nextElement(); - - // filter out non-classes - if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) ) - { - continue; - } - String className = entry.getName().substring( 0, entry.getName().length() - 6 ); m_obfClassNames.add( Descriptor.toJvmName( className ) ); } - } - - public void indexJar( InputStream in ) - throws IOException - { - ClassPool classPool = new ClassPool(); - ZipInputStream zin = new ZipInputStream( in ); - ZipEntry entry; - while( ( entry = zin.getNextEntry() ) != null ) + // pass 2: index the types, methods + for( CtClass c : JarClassIterator.classes( jar ) ) { - // filter out non-classes - if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) ) + m_ancestries.addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); + for( CtBehavior behavior : c.getDeclaredBehaviors() ) { - continue; + indexBehavior( behavior ); } - - // read the class into a buffer - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - byte[] buf = new byte[Constants.KiB]; - int totalNumBytesRead = 0; - while( zin.available() > 0 ) - { - int numBytesRead = zin.read( buf ); - if( numBytesRead < 0 ) - { - break; - } - bos.write( buf, 0, numBytesRead ); - - // sanity checking - totalNumBytesRead += numBytesRead; - if( totalNumBytesRead > Constants.MiB ) - { - throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" ); - } - } - - // determine the class name (ie chop off the ".class") - String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); - - // get a javassist handle for the class - classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); - try - { - CtClass c = classPool.get( className ); - m_ancestries.addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); - for( CtBehavior behavior : c.getDeclaredBehaviors() ) - { - indexBehavior( behavior ); - } - } - catch( NotFoundException ex ) + } + + // pass 2: index inner classes + for( CtClass c : JarClassIterator.classes( jar ) ) + { + String outerClassName = isInnerClass( c ); + if( outerClassName != null ) { - throw new Error( "Unable to load class: " + className ); + String innerClassName = Descriptor.toJvmName( c.getName() ); + m_innerClasses.put( outerClassName, innerClassName ); + m_outerClasses.put( innerClassName, outerClassName ); } } } - + private void indexBehavior( CtBehavior behavior ) { // get the method entry @@ -226,6 +183,78 @@ public class JarIndex } } + @SuppressWarnings( "unchecked" ) + private String isInnerClass( CtClass c ) + { + String innerClassName = Descriptor.toJvmName( c.getName() ); + + // first, is this an anonymous class? + // for anonymous classes: + // the outer class is always a synthetic field + // there's at least one constructor with the type of the synthetic field as an argument + // this constructor is called exactly once by the class of the synthetic field + + for( FieldInfo field : (List)c.getClassFile().getFields() ) + { + boolean isSynthetic = (field.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; + if( !isSynthetic ) + { + continue; + } + + // skip non-class types + if( !field.getDescriptor().startsWith( "L" ) ) + { + continue; + } + + // get the outer class from the field type + String outerClassName = Descriptor.toJvmName( Descriptor.toClassName( field.getDescriptor() ) ); + + // look for a constructor where this type is the first parameter + CtConstructor targetConstructor = null; + for( CtConstructor constructor : c.getDeclaredConstructors() ) + { + String signature = Descriptor.getParamDescriptor( constructor.getMethodInfo().getDescriptor() ); + if( Descriptor.numOfParameters( signature ) < 1 ) + { + continue; + } + + // match the first parameter to the outer class + Descriptor.Iterator iter = new Descriptor.Iterator( signature ); + int pos = iter.next(); + if( iter.isParameter() && signature.charAt( pos ) == 'L' ) + { + String argumentDesc = signature.substring( pos, signature.indexOf(';', pos) + 1 ); + String argumentClassName = Descriptor.toJvmName( Descriptor.toClassName( argumentDesc ) ); + if( argumentClassName.equals( outerClassName ) ) + { + // is this constructor called exactly once? + ConstructorEntry constructorEntry = new ConstructorEntry( + new ClassEntry( innerClassName ), + constructor.getMethodInfo().getDescriptor() + ); + if( this.getMethodCallers( constructorEntry ).size() == 1 ) + { + targetConstructor = constructor; + break; + } + } + } + } + if( targetConstructor == null ) + { + continue; + } + + // yeah, this is an inner class + return outerClassName; + } + + return null; + } + public Set getObfClassNames( ) { return m_obfClassNames; @@ -304,4 +333,14 @@ public class JarIndex { return m_methodCalls.get( entry ); } + + public Collection getInnerClasses( String obfOuterClassName ) + { + return m_innerClasses.get( obfOuterClassName ); + } + + public String getOuterClass( String obfInnerClassName ) + { + return m_outerClasses.get( obfInnerClassName ); + } } diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java new file mode 100644 index 00000000..d4abe4ea --- /dev/null +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Collection; + +import javassist.CtClass; +import javassist.bytecode.AccessFlag; +import javassist.bytecode.Descriptor; +import javassist.bytecode.InnerClassesAttribute; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.mapping.Translator; + +public class InnerClassWriter +{ + private Translator m_deobfuscatingTranslator; + private JarIndex m_jarIndex; + + public InnerClassWriter( Translator deobfuscatingTranslator, JarIndex jarIndex ) + { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_jarIndex = jarIndex; + } + + public void writeInnerClasses( CtClass c ) + { + // is this an outer class with inner classes? + String obfOuterClassName = Descriptor.toJvmName( c.getName() ); + Collection obfInnerClassNames = m_jarIndex.getInnerClasses( obfOuterClassName ); + if( obfInnerClassNames != null && !obfInnerClassNames.isEmpty() ) + { + writeInnerClasses( c, obfInnerClassNames ); + } + } + + private void writeInnerClasses( CtClass c, Collection obfInnerClassNames ) + { + String obfOuterClassName = Descriptor.toJvmName( c.getName() ); + InnerClassesAttribute attr = new InnerClassesAttribute( c.getClassFile().getConstPool() ); + c.getClassFile().addAttribute( attr ); + for( String obfInnerClassName : obfInnerClassNames ) + { + // deobfuscate the class names + String deobfOuterClassName = m_deobfuscatingTranslator.translateClass( obfOuterClassName ); + if( deobfOuterClassName == null ) + { + deobfOuterClassName = obfOuterClassName; + } + String deobfInnerClassName = m_deobfuscatingTranslator.translateClass( obfInnerClassName ); + if( deobfInnerClassName == null ) + { + deobfInnerClassName = obfInnerClassName; + } + + // update the attribute + String deobfOuterInnerClassName = deobfOuterClassName + "$" + deobfInnerClassName; + attr.append( + deobfOuterInnerClassName, + deobfOuterClassName, + deobfInnerClassName, + c.getClassFile().getAccessFlags() & ~AccessFlag.SUPER + ); + + // make sure the outer class references only the new inner class names + c.replaceClassName( obfInnerClassName, deobfOuterInnerClassName ); + + // TEMP + System.out.println( "\tInner " + obfInnerClassName + " -> " + deobfOuterInnerClassName ); + } + } +} -- cgit v1.2.3 From 6c4440ac1133bfaa7871d1049d174528a289ef30 Mon Sep 17 00:00:00 2001 From: hg Date: Sun, 17 Aug 2014 10:56:17 -0400 Subject: added support for automatic reconstruction of inner and anonymous classes also added class to restore bridge method flags taken out by the obfuscator --- src/cuchaz/enigma/Deobfuscator.java | 51 +++---- src/cuchaz/enigma/TranslatingTypeLoader.java | 56 ++------ src/cuchaz/enigma/analysis/Ancestries.java | 20 +++ src/cuchaz/enigma/analysis/BridgeFixer.java | 91 ++++++++++++ src/cuchaz/enigma/analysis/JarIndex.java | 155 ++++++++++++++++++--- src/cuchaz/enigma/analysis/SourceIndex.java | 31 +++-- src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 45 +++++- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 64 ++++++--- src/cuchaz/enigma/gui/Gui.java | 16 ++- src/cuchaz/enigma/gui/GuiController.java | 5 +- src/cuchaz/enigma/mapping/ClassEntry.java | 28 ++++ src/cuchaz/enigma/mapping/ClassMapping.java | 97 +++++++++++-- .../enigma/mapping/MappingParseException.java | 31 +++++ src/cuchaz/enigma/mapping/MappingsReader.java | 76 +++++++--- src/cuchaz/enigma/mapping/MappingsWriter.java | 40 ++++-- src/cuchaz/enigma/mapping/Renamer.java | 64 ++++----- src/cuchaz/enigma/mapping/Translator.java | 104 +++++++++----- 17 files changed, 743 insertions(+), 231 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/BridgeFixer.java create mode 100644 src/cuchaz/enigma/mapping/MappingParseException.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 127a0d98..9a0ec132 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -62,7 +62,6 @@ public class Deobfuscator // config the decompiler m_settings = DecompilerSettings.javaDefaults(); - m_settings.setShowSyntheticMembers( true ); // init mappings setMappings( new Mappings() ); @@ -109,9 +108,15 @@ public class Deobfuscator { for( String obfClassName : m_jarIndex.getObfClassNames() ) { + // skip inner classes + if( m_jarIndex.getOuterClass( obfClassName ) != null ) + { + continue; + } + // separate the classes ClassMapping classMapping = m_mappings.getClassByObf( obfClassName ); - if( classMapping != null ) + if( classMapping != null && !classMapping.getObfName().equals( classMapping.getDeobfName() ) ) { deobfClasses.add( classMapping.getDeobfName() ); } @@ -151,6 +156,7 @@ public class Deobfuscator // render the AST into source StringWriter buf = new StringWriter(); root.acceptVisitor( new InsertParenthesesVisitor(), null ); + //root.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); root.acceptVisitor( new JavaOutputVisitor( new PlainTextOutput( buf ), m_settings ), null ); // build the source index @@ -281,33 +287,30 @@ public class Deobfuscator } } - public boolean entryIsObfuscatedIdenfitier( Entry obfEntry ) + public boolean isObfuscatedIdentifier( Entry obfEntry ) { if( obfEntry instanceof ClassEntry ) { - // obf classes must be in the list - return m_jarIndex.getObfClassNames().contains( obfEntry.getName() ); - } - else if( obfEntry instanceof FieldEntry ) - { - return m_jarIndex.getObfClassNames().contains( obfEntry.getClassName() ); - } - else if( obfEntry instanceof MethodEntry ) - { - return m_jarIndex.getObfClassNames().contains( obfEntry.getClassName() ); - } - else if( obfEntry instanceof ConstructorEntry ) - { - return m_jarIndex.getObfClassNames().contains( obfEntry.getClassName() ); + if( obfEntry.getName().indexOf( '$' ) >= 0 ) + { + String[] parts = obfEntry.getName().split( "\\$" ); + assert( parts.length == 2 ); // not supporting recursively-nested classes + String outerClassName = parts[0]; + String innerClassName = parts[1]; + + // both classes must be in the list + return m_jarIndex.getObfClassNames().contains( outerClassName ) + && m_jarIndex.getObfClassNames().contains( innerClassName ); + } + else + { + // class must be in the list + return m_jarIndex.getObfClassNames().contains( obfEntry.getName() ); + } } - else if( obfEntry instanceof ArgumentEntry ) + else { - // arguments only appear in method declarations - // since we only show declarations for obf classes, these are always obfuscated - return true; + return isObfuscatedIdentifier( obfEntry.getClassEntry() ); } - - // assume everything else is not obfuscated - return false; } } diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index fdfcea0f..c1d96ae3 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -26,6 +26,7 @@ import javassist.bytecode.Descriptor; import com.strobel.assembler.metadata.Buffer; import com.strobel.assembler.metadata.ITypeLoader; +import cuchaz.enigma.analysis.BridgeFixer; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.bytecode.ClassTranslator; import cuchaz.enigma.bytecode.InnerClassWriter; @@ -50,12 +51,6 @@ public class TranslatingTypeLoader implements ITypeLoader @Override public boolean tryLoadType( String deobfClassName, Buffer out ) { - // TEMP - if( !deobfClassName.startsWith( "java" ) && !deobfClassName.startsWith( "org" ) ) - { - System.out.println( "Looking for: " + deobfClassName ); - } - // what class file should we actually load? String obfClassName = m_obfuscatingTranslator.translateClass( deobfClassName ); if( obfClassName == null ) @@ -64,26 +59,12 @@ public class TranslatingTypeLoader implements ITypeLoader } String classFileName = obfClassName; - // is this a properly-referenced inner class? - boolean isInnerClass = deobfClassName.indexOf( '$' ) >= 0; - if( isInnerClass ) - { - // get just the bare inner class name - String[] parts = deobfClassName.split( "\\$" ); - String deobfClassFileName = parts[parts.length - 1]; - - // make sure the bare inner class name is obfuscated - classFileName = m_obfuscatingTranslator.translateClass( deobfClassFileName ); - if( classFileName == null ) - { - classFileName = deobfClassFileName; - } - } - - // TEMP - if( !deobfClassName.startsWith( "java" ) && !deobfClassName.startsWith( "org" ) ) + // is this an inner class? + if( obfClassName.indexOf( '$' ) >= 0 ) { - System.out.println( "\tLooking at class file: " + classFileName ); + // the file name is the bare inner class name + String[] parts = obfClassName.split( "\\$" ); + classFileName = parts[parts.length - 1]; } // get the jar entry @@ -118,26 +99,20 @@ public class TranslatingTypeLoader implements ITypeLoader classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); CtClass c = classPool.get( javaClassFileName ); - if( isInnerClass ) - { - // rename the class to what procyon expects - c.setName( deobfClassName ); - } - else - { - // maybe it's an outer class - new InnerClassWriter( m_deobfuscatingTranslator, m_jarIndex ).writeInnerClasses( c ); - } - + // do all kinds of deobfuscating transformations on the class + new InnerClassWriter( m_deobfuscatingTranslator, m_jarIndex ).write( c ); + new BridgeFixer().fixBridges( c ); new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); - assert( Descriptor.toJvmName( c.getName() ).equals( deobfClassName ) ); - assert( c.getClassFile().getName().equals( deobfClassName ) ); - - buf = c.toBytecode(); + // sanity checking + assert( Descriptor.toJvmName( c.getName() ).equals( deobfClassName ) ) + : String.format( "%s is not %s", Descriptor.toJvmName( c.getName() ), deobfClassName ); + assert( Descriptor.toJvmName( c.getClassFile().getName() ).equals( deobfClassName ) ) + : String.format( "%s is not %s", Descriptor.toJvmName( c.getClassFile().getName() ), deobfClassName ); // pass the transformed class along to the decompiler + buf = c.toBytecode(); out.reset( buf.length ); System.arraycopy( buf, 0, out.array(), out.position(), buf.length ); out.position( 0 ); @@ -149,5 +124,4 @@ public class TranslatingTypeLoader implements ITypeLoader throw new Error( ex ); } } - } diff --git a/src/cuchaz/enigma/analysis/Ancestries.java b/src/cuchaz/enigma/analysis/Ancestries.java index 83c239cd..b9d8cbf4 100644 --- a/src/cuchaz/enigma/analysis/Ancestries.java +++ b/src/cuchaz/enigma/analysis/Ancestries.java @@ -47,6 +47,26 @@ public class Ancestries implements Serializable } } + public void renameClasses( Map renames ) + { + Map newSuperclasses = Maps.newHashMap(); + for( Map.Entry entry : m_superclasses.entrySet() ) + { + String subclass = renames.get( entry.getKey() ); + if( subclass == null ) + { + subclass = entry.getKey(); + } + String superclass = renames.get( entry.getValue() ); + if( superclass == null ) + { + superclass = entry.getValue(); + } + newSuperclasses.put( subclass, superclass ); + } + m_superclasses = newSuperclasses; + } + public String getSuperclassName( String className ) { return m_superclasses.get( className ); diff --git a/src/cuchaz/enigma/analysis/BridgeFixer.java b/src/cuchaz/enigma/analysis/BridgeFixer.java new file mode 100644 index 00000000..db441d2b --- /dev/null +++ b/src/cuchaz/enigma/analysis/BridgeFixer.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javassist.CannotCompileException; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.NotFoundException; +import javassist.bytecode.AccessFlag; +import javassist.expr.ExprEditor; +import javassist.expr.MethodCall; + +import com.beust.jcommander.internal.Lists; + +public class BridgeFixer +{ + public void fixBridges( CtClass c ) + { + // bridge methods are scrubbed and marked as synthetic methods by the obfuscator + // need to figure out which synthetics are bridge methods and restore them + for( CtMethod method : c.getDeclaredMethods() ) + { + // skip non-synthetic methods + if( ( method.getModifiers() & AccessFlag.SYNTHETIC ) == 0 ) + { + continue; + } + + CtMethod bridgedMethod = getBridgedMethod( method ); + if( bridgedMethod != null ) + { + bridgedMethod.setName( method.getName() ); + method.setModifiers( method.getModifiers() | AccessFlag.BRIDGE ); + } + } + } + + private CtMethod getBridgedMethod( CtMethod method ) + { + // bridge methods just call another method, cast it to the return type, and return the result + // let's see if we can detect this scenario + + // get all the called methods + final List methodCalls = Lists.newArrayList(); + try + { + method.instrument( new ExprEditor( ) + { + @Override + public void edit( MethodCall call ) + { + methodCalls.add( call ); + } + } ); + } + catch( CannotCompileException ex ) + { + // this is stupid... we're not even compiling anything + throw new Error( ex ); + } + + // is there just one? + if( methodCalls.size() != 1 ) + { + return null; + } + MethodCall call = methodCalls.get( 0 ); + + try + { + // we have a bridge method! + return call.getMethod(); + } + catch( NotFoundException ex ) + { + // can't find the type? not a bridge method + ex.printStackTrace( System.err ); + return null; + } + } +} diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 9962bfaa..34e8986f 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -10,7 +10,9 @@ ******************************************************************************/ package cuchaz.enigma.analysis; +import java.util.AbstractMap; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -37,6 +39,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; +import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; @@ -53,6 +56,7 @@ public class JarIndex private Multimap m_fieldCalls; private Multimap m_innerClasses; private Map m_outerClasses; + private Set m_anonymousClasses; public JarIndex( ) { @@ -63,6 +67,7 @@ public class JarIndex m_fieldCalls = HashMultimap.create(); m_innerClasses = HashMultimap.create(); m_outerClasses = Maps.newHashMap(); + m_anonymousClasses = Sets.newHashSet(); } public void indexJar( JarFile jar ) @@ -84,7 +89,7 @@ public class JarIndex } } - // pass 2: index inner classes + // pass 2: index inner classes and anonymous classes for( CtClass c : JarClassIterator.classes( jar ) ) { String outerClassName = isInnerClass( c ); @@ -93,8 +98,21 @@ public class JarIndex String innerClassName = Descriptor.toJvmName( c.getName() ); m_innerClasses.put( outerClassName, innerClassName ); m_outerClasses.put( innerClassName, outerClassName ); + + if( isAnonymousClass( c, outerClassName ) ) + { + m_anonymousClasses.add( innerClassName ); + } } } + + // step 3: update other indicies with inner class info + Map renames = Maps.newHashMap(); + for( Map.Entry entry : m_outerClasses.entrySet() ) + { + renames.put( entry.getKey(), entry.getValue() + "$" + entry.getKey() ); + } + renameClasses( renames ); } private void indexBehavior( CtBehavior behavior ) @@ -186,14 +204,10 @@ public class JarIndex @SuppressWarnings( "unchecked" ) private String isInnerClass( CtClass c ) { - String innerClassName = Descriptor.toJvmName( c.getName() ); - - // first, is this an anonymous class? - // for anonymous classes: + // inner classes: // the outer class is always a synthetic field // there's at least one constructor with the type of the synthetic field as an argument - // this constructor is called exactly once by the class of the synthetic field - + for( FieldInfo field : (List)c.getClassFile().getFields() ) { boolean isSynthetic = (field.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; @@ -230,16 +244,8 @@ public class JarIndex String argumentClassName = Descriptor.toJvmName( Descriptor.toClassName( argumentDesc ) ); if( argumentClassName.equals( outerClassName ) ) { - // is this constructor called exactly once? - ConstructorEntry constructorEntry = new ConstructorEntry( - new ClassEntry( innerClassName ), - constructor.getMethodInfo().getDescriptor() - ); - if( this.getMethodCallers( constructorEntry ).size() == 1 ) - { - targetConstructor = constructor; - break; - } + targetConstructor = constructor; + break; } } } @@ -255,6 +261,30 @@ public class JarIndex return null; } + private boolean isAnonymousClass( CtClass c, String outerClassName ) + { + String innerClassName = Descriptor.toJvmName( c.getName() ); + + // anonymous classes: + // have only one constructor + // it's called exactly once by the outer class + // type of inner class not referenced anywhere in outer class + + // is there exactly one constructor? + if( c.getDeclaredConstructors().length != 1 ) + { + return false; + } + CtConstructor constructor = c.getDeclaredConstructors()[0]; + + // is this constructor called exactly once? + ConstructorEntry constructorEntry = new ConstructorEntry( + new ClassEntry( innerClassName ), + constructor.getMethodInfo().getDescriptor() + ); + return getMethodCallers( constructorEntry ).size() == 1; + } + public Set getObfClassNames( ) { return m_obfClassNames; @@ -343,4 +373,95 @@ public class JarIndex { return m_outerClasses.get( obfInnerClassName ); } + + public boolean isAnonymousClass( String obfInnerClassName ) + { + return m_anonymousClasses.contains( obfInnerClassName ); + } + + private void renameClasses( Map renames ) + { + m_ancestries.renameClasses( renames ); + renameMultimap( renames, m_methodImplementations ); + renameMultimap( renames, m_methodCalls ); + renameMultimap( renames, m_fieldCalls ); + } + + private void renameMultimap( Map renames, Multimap map ) + { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + Iterator> iter = map.entries().iterator(); + while( iter.hasNext() ) + { + Map.Entry entry = iter.next(); + iter.remove(); + entriesToAdd.add( new AbstractMap.SimpleEntry( + renameEntry( renames, entry.getKey() ), + renameEntry( renames, entry.getValue() ) + ) ); + } + for( Map.Entry entry : entriesToAdd ) + { + map.put( entry.getKey(), entry.getValue() ); + } + } + + @SuppressWarnings( "unchecked" ) + private T renameEntry( Map renames, T entry ) + { + if( entry instanceof String ) + { + String stringEntry = (String)entry; + if( renames.containsKey( stringEntry ) ) + { + return (T)renames.get( stringEntry ); + } + } + else if( entry instanceof ClassEntry ) + { + ClassEntry classEntry = (ClassEntry)entry; + return (T)new ClassEntry( renameEntry( renames, classEntry.getClassName() ) ); + } + else if( entry instanceof FieldEntry ) + { + FieldEntry fieldEntry = (FieldEntry)entry; + return (T)new FieldEntry( + renameEntry( renames, fieldEntry.getClassEntry() ), + fieldEntry.getName() + ); + } + else if( entry instanceof ConstructorEntry ) + { + ConstructorEntry constructorEntry = (ConstructorEntry)entry; + return (T)new ConstructorEntry( + renameEntry( renames, constructorEntry.getClassEntry() ), + constructorEntry.getSignature() + ); + } + else if( entry instanceof MethodEntry ) + { + MethodEntry methodEntry = (MethodEntry)entry; + return (T)new MethodEntry( + renameEntry( renames, methodEntry.getClassEntry() ), + methodEntry.getName(), + methodEntry.getSignature() + ); + } + else if( entry instanceof ArgumentEntry ) + { + ArgumentEntry argumentEntry = (ArgumentEntry)entry; + return (T)new ArgumentEntry( + renameEntry( renames, argumentEntry.getMethodEntry() ), + argumentEntry.getIndex(), + argumentEntry.getName() + ); + } + else + { + throw new Error( "Not an entry: " + entry ); + } + + return entry; + } } diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 531f3e04..645a71da 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -17,7 +17,7 @@ import java.util.TreeMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.strobel.decompiler.languages.Region; -import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.Identifier; import cuchaz.enigma.mapping.Entry; @@ -51,19 +51,27 @@ public class SourceIndex return m_source; } - public Token getToken( AstNode node ) + public Token getToken( Identifier node ) { // get a token for this node's region Region region = node.getRegion(); if( region.getBeginLine() == 0 || region.getEndLine() == 0 ) { - throw new IllegalArgumentException( "Invalid region: " + region ); + System.err.println( "WARNING: " + node.getNodeType() + " node has invalid region: " + region ); + return null; } Token token = new Token( toPos( region.getBeginLine(), region.getBeginColumn() ), toPos( region.getEndLine(), region.getEndColumn() ) ); + // for tokens representing inner classes, make sure we only get the simple name + int pos = node.getName().lastIndexOf( '$' ); + if( pos >= 0 ) + { + token.end -= pos + 1; + } + // HACKHACK: sometimes node regions are off by one // I think this is a bug in Procyon, but it's easy to work around if( !Character.isJavaIdentifierStart( m_source.charAt( token.start ) ) ) @@ -79,16 +87,23 @@ public class SourceIndex return token; } - public void add( AstNode node, Entry deobfEntry ) + public void add( Identifier node, Entry deobfEntry ) { - m_tokens.put( getToken( node ), deobfEntry ); + Token token = getToken( node ); + if( token != null ) + { + m_tokens.put( token, deobfEntry ); + } } - public void addDeclaration( AstNode node, Entry deobfEntry ) + public void addDeclaration( Identifier node, Entry deobfEntry ) { Token token = getToken( node ); - m_tokens.put( token, deobfEntry ); - m_declarations.put( deobfEntry, token ); + if( token != null ) + { + m_tokens.put( token, deobfEntry ); + m_declarations.put( deobfEntry, token ); + } } public Token getToken( int pos ) diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java index 05d0e6be..ac3e92db 100644 --- a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java +++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -10,6 +10,11 @@ ******************************************************************************/ package cuchaz.enigma.analysis; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; + import com.strobel.componentmodel.Key; import com.strobel.decompiler.languages.java.ast.Annotation; import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; @@ -87,10 +92,42 @@ import com.strobel.decompiler.patterns.Pattern; public class TreeDumpVisitor implements IAstVisitor { + private File m_file; + private Writer m_out; + + public TreeDumpVisitor( File file ) + { + m_file = file; + m_out = null; + } + + @Override + public Void visitCompilationUnit( CompilationUnit node, Void ignored ) + { + try + { + m_out = new FileWriter( m_file ); + recurse( node, ignored ); + m_out.close(); + return null; + } + catch( IOException ex ) + { + throw new Error( ex ); + } + } + private Void recurse( AstNode node, Void ignored ) { // show the tree - System.out.println( getIndent( node ) + node.getClass().getSimpleName() + dumpUserData( node ) + " " + node.getRegion() ); + try + { + m_out.write( getIndent( node ) + node.getClass().getSimpleName() + dumpUserData( node ) + " " + node.getRegion() + "\n" ); + } + catch( IOException ex ) + { + throw new Error( ex ); + } // recurse for( final AstNode child : node.getChildren() ) @@ -378,12 +415,6 @@ public class TreeDumpVisitor implements IAstVisitor return recurse( node, ignored ); } - @Override - public Void visitCompilationUnit( CompilationUnit node, Void ignored ) - { - return recurse( node, ignored ); - } - @Override public Void visitPackageDeclaration( PackageDeclaration node, Void ignored ) { diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index d4abe4ea..b0e33ac2 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -14,6 +14,7 @@ import java.util.Collection; import javassist.CtClass; import javassist.bytecode.AccessFlag; +import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.InnerClassesAttribute; import cuchaz.enigma.analysis.JarIndex; @@ -29,21 +30,33 @@ public class InnerClassWriter m_deobfuscatingTranslator = deobfuscatingTranslator; m_jarIndex = jarIndex; } - - public void writeInnerClasses( CtClass c ) + + public void write( CtClass c ) { - // is this an outer class with inner classes? - String obfOuterClassName = Descriptor.toJvmName( c.getName() ); + // get the outer class name + String obfClassName = Descriptor.toJvmName( c.getName() ); + String obfOuterClassName = m_jarIndex.getOuterClass( obfClassName ); + if( obfOuterClassName == null ) + { + // this is an outer class + obfOuterClassName = obfClassName; + } + else + { + // this is an inner class, rename it to outer$inner + c.setName( obfOuterClassName + "$" + obfClassName ); + } + + // write the inner classes if needed Collection obfInnerClassNames = m_jarIndex.getInnerClasses( obfOuterClassName ); - if( obfInnerClassNames != null && !obfInnerClassNames.isEmpty() ) + if( obfInnerClassNames != null ) { - writeInnerClasses( c, obfInnerClassNames ); + writeInnerClasses( c, obfOuterClassName, obfInnerClassNames ); } } - - private void writeInnerClasses( CtClass c, Collection obfInnerClassNames ) + + private void writeInnerClasses( CtClass c, String obfOuterClassName, Collection obfInnerClassNames ) { - String obfOuterClassName = Descriptor.toJvmName( c.getName() ); InnerClassesAttribute attr = new InnerClassesAttribute( c.getClassFile().getConstPool() ); c.getClassFile().addAttribute( attr ); for( String obfInnerClassName : obfInnerClassNames ) @@ -54,26 +67,37 @@ public class InnerClassWriter { deobfOuterClassName = obfOuterClassName; } - String deobfInnerClassName = m_deobfuscatingTranslator.translateClass( obfInnerClassName ); - if( deobfInnerClassName == null ) + String obfOuterInnerClassName = obfOuterClassName + "$" + obfInnerClassName; + String deobfOuterInnerClassName = m_deobfuscatingTranslator.translateClass( obfOuterInnerClassName ); + if( deobfOuterInnerClassName == null ) + { + deobfOuterInnerClassName = obfOuterInnerClassName; + } + String deobfInnerClassName = deobfOuterInnerClassName.substring( deobfOuterInnerClassName.lastIndexOf( '$' ) + 1 ); + + // here's what the JVM spec says about the InnerClasses attribute + // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); + + // update the attribute with this inner class + ConstPool constPool = c.getClassFile().getConstPool(); + int innerClassIndex = constPool.addClassInfo( deobfOuterInnerClassName ); + int outerClassIndex = 0; + int innerClassSimpleNameIndex = 0; + if( !m_jarIndex.isAnonymousClass( obfInnerClassName ) ) { - deobfInnerClassName = obfInnerClassName; + outerClassIndex = constPool.addClassInfo( deobfOuterClassName ); + innerClassSimpleNameIndex = constPool.addUtf8Info( deobfInnerClassName ); } - // update the attribute - String deobfOuterInnerClassName = deobfOuterClassName + "$" + deobfInnerClassName; attr.append( - deobfOuterInnerClassName, - deobfOuterClassName, - deobfInnerClassName, + innerClassIndex, + outerClassIndex, + innerClassSimpleNameIndex, c.getClassFile().getAccessFlags() & ~AccessFlag.SUPER ); // make sure the outer class references only the new inner class names c.replaceClassName( obfInnerClassName, deobfOuterInnerClassName ); - - // TEMP - System.out.println( "\tInner " + obfInnerClassName + " -> " + deobfOuterInnerClassName ); } } } diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 3b67a329..9ed6dd86 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -78,6 +78,7 @@ import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.IllegalNameException; +import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.MethodEntry; public class Gui @@ -508,6 +509,10 @@ public class Gui { throw new Error( ex ); } + catch( MappingParseException ex ) + { + JOptionPane.showMessageDialog( m_frame, ex.getMessage() ); + } } } } ); @@ -862,9 +867,17 @@ public class Gui private void startRename( ) { + // get the class name + String className = m_selectedEntryPair.deobf.getName(); + int pos = className.lastIndexOf( '$' ); + if( pos >= 0 ) + { + className = className.substring( pos + 1 ); + } + // init the text box final JTextField text = new JTextField(); - text.setText( m_selectedEntryPair.deobf.getName() ); + text.setText( className ); text.setPreferredSize( new Dimension( 360, text.getPreferredSize().height ) ); text.addKeyListener( new KeyAdapter( ) { @@ -905,6 +918,7 @@ public class Gui } catch( IllegalNameException ex ) { + ex.printStackTrace( System.err ); text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) ); } return; diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 534b0cc5..ffeb99aa 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -31,6 +31,7 @@ import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; import cuchaz.enigma.mapping.MethodEntry; @@ -75,7 +76,7 @@ public class GuiController } public void openMappings( File file ) - throws IOException + throws IOException, MappingParseException { FileReader in = new FileReader( file ); m_deobfuscator.setMappings( new MappingsReader().read( in ) ); @@ -135,7 +136,7 @@ public class GuiController public boolean entryIsObfuscatedIdenfitier( Entry deobfEntry ) { - return m_deobfuscator.entryIsObfuscatedIdenfitier( m_deobfuscator.obfuscateEntry( deobfEntry ) ); + return m_deobfuscator.isObfuscatedIdentifier( m_deobfuscator.obfuscateEntry( deobfEntry ) ); } public ClassInheritanceTreeNode getClassInheritance( ClassEntry obfClassEntry ) diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 738e8e3b..dad6da90 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -82,4 +82,32 @@ public class ClassEntry implements Entry, Serializable { return m_name; } + + public boolean isInnerClass( ) + { + return m_name.lastIndexOf( '$' ) >= 0; + } + + public String getOuterClassName( ) + { + if( isInnerClass() ) + { + return m_name.substring( 0, m_name.lastIndexOf( '$' ) ); + } + return m_name; + } + + public String getInnerClassName( ) + { + if( !isInnerClass() ) + { + throw new Error( "This is not an inner class!" ); + } + return m_name.substring( m_name.lastIndexOf( '$' ) + 1 ); + } + + public ClassEntry getOuterClassEntry( ) + { + return new ClassEntry( getOuterClassName() ); + } } diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index c6826f31..c7f930c6 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -21,6 +21,8 @@ public class ClassMapping implements Serializable, Comparable private String m_obfName; private String m_deobfName; + private Map m_innerClassesByObf; + private Map m_innerClassesByDeobf; private Map m_fieldsByObf; private Map m_fieldsByDeobf; private Map m_methodsByObf; @@ -31,6 +33,8 @@ public class ClassMapping implements Serializable, Comparable { m_obfName = obfName; m_deobfName = NameValidator.validateClassName( deobfName ); + m_innerClassesByObf = Maps.newHashMap(); + m_innerClassesByDeobf = Maps.newHashMap(); m_fieldsByObf = Maps.newHashMap(); m_fieldsByDeobf = Maps.newHashMap(); m_methodsByObf = Maps.newHashMap(); @@ -51,6 +55,72 @@ public class ClassMapping implements Serializable, Comparable m_deobfName = NameValidator.validateClassName( val ); } + //// INNER CLASSES //////// + + public Iterable innerClasses( ) + { + assert( m_innerClassesByObf.size() == m_innerClassesByDeobf.size() ); + return m_innerClassesByObf.values(); + } + + protected void addInnerClassMapping( ClassMapping classMapping ) + { + m_innerClassesByObf.put( classMapping.getObfName(), classMapping ); + m_innerClassesByDeobf.put( classMapping.getDeobfName(), classMapping ); + } + + public ClassMapping getOrCreateInnerClass( String obfName ) + { + ClassMapping classMapping = m_innerClassesByObf.get( obfName ); + if( classMapping == null ) + { + classMapping = new ClassMapping( obfName, obfName ); + m_innerClassesByObf.put( obfName, classMapping ); + m_innerClassesByDeobf.put( obfName, classMapping ); + } + return classMapping; + } + + public ClassMapping getInnerClassByObf( String obfName ) + { + return m_innerClassesByObf.get( obfName ); + } + + public ClassMapping getInnerClassByDeobf( String deobfName ) + { + return m_innerClassesByDeobf.get( deobfName ); + } + + public String getObfInnerClassName( String deobfName ) + { + ClassMapping classMapping = m_innerClassesByDeobf.get( deobfName ); + if( classMapping != null ) + { + return classMapping.getObfName(); + } + return null; + } + + public String getDeobfInnerClassName( String obfName ) + { + ClassMapping classMapping = m_innerClassesByObf.get( obfName ); + if( classMapping != null ) + { + return classMapping.getDeobfName(); + } + return null; + } + + public void setInnerClassName( String obfName, String deobfName ) + { + ClassMapping classMapping = getOrCreateInnerClass( obfName ); + m_innerClassesByDeobf.remove( classMapping.getDeobfName() ); + classMapping.setDeobfName( deobfName ); + m_innerClassesByDeobf.put( deobfName, classMapping ); + } + + //// FIELDS //////// + public Iterable fields( ) { assert( m_fieldsByObf.size() == m_fieldsByDeobf.size() ); @@ -62,18 +132,7 @@ public class ClassMapping implements Serializable, Comparable m_fieldsByObf.put( fieldMapping.getObfName(), fieldMapping ); m_fieldsByDeobf.put( fieldMapping.getDeobfName(), fieldMapping ); } - - public Iterable methods( ) - { - assert( m_methodsByObf.size() == m_methodsByDeobf.size() ); - return m_methodsByObf.values(); - } - protected void addMethodMapping( MethodMapping methodMapping ) - { - m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping ); - m_methodsByDeobf.put( getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ), methodMapping ); - } public String getObfFieldName( String deobfName ) { @@ -110,6 +169,20 @@ public class ClassMapping implements Serializable, Comparable m_fieldsByDeobf.put( deobfName, fieldMapping ); } + //// METHODS //////// + + public Iterable methods( ) + { + assert( m_methodsByObf.size() == m_methodsByDeobf.size() ); + return m_methodsByObf.values(); + } + + protected void addMethodMapping( MethodMapping methodMapping ) + { + m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping ); + m_methodsByDeobf.put( getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ), methodMapping ); + } + public MethodMapping getMethodByObf( String obfName, String signature ) { return m_methodsByObf.get( getMethodKey( obfName, signature ) ); @@ -155,6 +228,8 @@ public class ClassMapping implements Serializable, Comparable } } + //// ARGUMENTS //////// + public void setArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName ) { MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ); diff --git a/src/cuchaz/enigma/mapping/MappingParseException.java b/src/cuchaz/enigma/mapping/MappingParseException.java new file mode 100644 index 00000000..4fcc1f18 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingParseException.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public class MappingParseException extends Exception +{ + private static final long serialVersionUID = -5487280332892507236L; + + private int m_line; + private String m_message; + + public MappingParseException( int line, String message ) + { + m_line = line; + m_message = message; + } + + @Override + public String getMessage( ) + { + return "Line " + m_line + ": " + m_message; + } +} diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index b0394090..4cebb3a4 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -13,25 +13,27 @@ package cuchaz.enigma.mapping; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; +import java.util.Deque; import java.util.NoSuchElementException; import java.util.Scanner; +import com.google.common.collect.Queues; + import cuchaz.enigma.Util; public class MappingsReader { public Mappings read( Reader in ) - throws IOException + throws IOException, MappingParseException { return read( new BufferedReader( in ) ); } public Mappings read( BufferedReader in ) - throws IOException + throws IOException, MappingParseException { Mappings mappings = new Mappings(); - ClassMapping classMapping = null; - MethodMapping methodMapping = null; + Deque mappingStack = Queues.newArrayDeque(); int lineNumber = 0; String line = null; @@ -47,12 +49,28 @@ public class MappingsReader } // skip blank lines - line = line.trim(); - if( line.length() <= 0 ) + if( line.trim().length() <= 0 ) { continue; } + // get the indent of this line + int indent = 0; + for( int i=0; i> List sorted( Iterable classes ) @@ -86,4 +92,14 @@ public class MappingsWriter Collections.sort( out ); return out; } + + private String getIndent( int depth ) + { + StringBuilder buf = new StringBuilder(); + for( int i=0; i m_classes; + public Map m_classes; private Ancestries m_ancestries; protected Translator( TranslationDirection direction, Map classes, Ancestries ancestries ) @@ -30,22 +30,42 @@ public class Translator m_ancestries = ancestries; } - public String translate( ClassEntry in ) + public String translateClass( String className ) { - return translateClass( in.getName() ); + return translate( new ClassEntry( className ) ); } - public String translateClass( String in ) + public String translate( ClassEntry in ) { - ClassMapping classIndex = m_classes.get( in ); - if( classIndex != null ) + ClassMapping classMapping = m_classes.get( in.getOuterClassName() ); + if( classMapping != null ) { - return m_direction.choose( - classIndex.getDeobfName(), - classIndex.getObfName() - ); + if( in.isInnerClass() ) + { + // look for the inner class + String translatedInnerClassName = m_direction.choose( + classMapping.getDeobfInnerClassName( in.getInnerClassName() ), + classMapping.getObfInnerClassName( in.getInnerClassName() ) + ); + if( translatedInnerClassName != null ) + { + // return outer$inner + String translatedOuterClassName = m_direction.choose( + classMapping.getDeobfName(), + classMapping.getObfName() + ); + return translatedOuterClassName + "$" + translatedInnerClassName; + } + } + else + { + // just return outer + return m_direction.choose( + classMapping.getDeobfName(), + classMapping.getObfName() + ); + } } - return null; } @@ -64,21 +84,20 @@ public class Translator for( String className : getSelfAndAncestors( in.getClassName() ) ) { // look for the class - ClassMapping classIndex = m_classes.get( className ); - if( classIndex != null ) + ClassMapping classMapping = findClassMapping( new ClassEntry( className ) ); + if( classMapping != null ) { // look for the field - String deobfName = m_direction.choose( - classIndex.getDeobfFieldName( in.getName() ), - classIndex.getObfFieldName( in.getName() ) + String translatedName = m_direction.choose( + classMapping.getDeobfFieldName( in.getName() ), + classMapping.getObfFieldName( in.getName() ) ); - if( deobfName != null ) + if( translatedName != null ) { - return deobfName; + return translatedName; } } } - return null; } @@ -99,20 +118,20 @@ public class Translator { for( String className : getSelfAndAncestors( in.getClassName() ) ) { - // look for the class - ClassMapping classIndex = m_classes.get( className ); - if( classIndex != null ) + // look for class + ClassMapping classMapping = findClassMapping( new ClassEntry( className ) ); + if( classMapping != null ) { // look for the method - MethodMapping methodIndex = m_direction.choose( - classIndex.getMethodByObf( in.getName(), in.getSignature() ), - classIndex.getMethodByDeobf( in.getName(), in.getSignature() ) + MethodMapping methodMapping = m_direction.choose( + classMapping.getMethodByObf( in.getName(), in.getSignature() ), + classMapping.getMethodByDeobf( in.getName(), in.getSignature() ) ); - if( methodIndex != null ) + if( methodMapping != null ) { return m_direction.choose( - methodIndex.getDeobfName(), - methodIndex.getObfName() + methodMapping.getDeobfName(), + methodMapping.getObfName() ); } } @@ -148,19 +167,19 @@ public class Translator for( String className : getSelfAndAncestors( in.getClassName() ) ) { // look for the class - ClassMapping classIndex = m_classes.get( className ); - if( classIndex != null ) + ClassMapping classMapping = findClassMapping( new ClassEntry( className ) ); + if( classMapping != null ) { // look for the method - MethodMapping methodIndex = m_direction.choose( - classIndex.getMethodByObf( in.getMethodName(), in.getMethodSignature() ), - classIndex.getMethodByDeobf( in.getMethodName(), in.getMethodSignature() ) + MethodMapping methodMapping = m_direction.choose( + classMapping.getMethodByObf( in.getMethodName(), in.getMethodSignature() ), + classMapping.getMethodByDeobf( in.getMethodName(), in.getMethodSignature() ) ); - if( methodIndex != null ) + if( methodMapping != null ) { return m_direction.choose( - methodIndex.getDeobfArgumentName( in.getIndex() ), - methodIndex.getObfArgumentName( in.getIndex() ) + methodMapping.getDeobfArgumentName( in.getIndex() ), + methodMapping.getObfArgumentName( in.getIndex() ) ); } } @@ -207,4 +226,17 @@ public class Translator ancestry.addAll( m_ancestries.getAncestry( className ) ); return ancestry; } + + private ClassMapping findClassMapping( ClassEntry classEntry ) + { + ClassMapping classMapping = m_classes.get( classEntry.getOuterClassName() ); + if( classMapping != null && classEntry.isInnerClass() ) + { + classMapping = m_direction.choose( + classMapping.getInnerClassByObf( classEntry.getInnerClassName() ), + classMapping.getInnerClassByDeobf( classEntry.getInnerClassName() ) + ); + } + return classMapping; + } } -- cgit v1.2.3 From 34c1e8e64ec4575527a19fb4cb0640c57da784db Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 18 Aug 2014 00:55:30 -0400 Subject: crap-ton of bug fixes for inner classes --- src/cuchaz/enigma/Deobfuscator.java | 2 + src/cuchaz/enigma/Main.java | 4 + src/cuchaz/enigma/TranslatingTypeLoader.java | 103 +++++++++--- src/cuchaz/enigma/Util.java | 20 +++ src/cuchaz/enigma/analysis/BridgeFixer.java | 2 + src/cuchaz/enigma/analysis/JarIndex.java | 195 ++++++++++++++++++----- src/cuchaz/enigma/bytecode/BytecodeTools.java | 57 +++++++ src/cuchaz/enigma/bytecode/ClassTranslator.java | 53 ++++-- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 42 +++-- src/cuchaz/enigma/mapping/Translator.java | 16 +- 10 files changed, 392 insertions(+), 102 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 9a0ec132..323aa2ed 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -62,6 +62,8 @@ public class Deobfuscator // config the decompiler m_settings = DecompilerSettings.javaDefaults(); + // DEBUG + //m_settings.setShowSyntheticMembers( true ); // init mappings setMappings( new Mappings() ); diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index 6a300ed6..20d73c29 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -13,6 +13,7 @@ package cuchaz.enigma; import java.io.File; import cuchaz.enigma.gui.Gui; +import cuchaz.enigma.mapping.ClassEntry; public class Main { @@ -30,6 +31,9 @@ public class Main { gui.getController().openMappings( getFile( args[1] ) ); } + + // DEBUG + //gui.getController().openEntry( new ClassEntry( "bah$bag" ) ); // bah,bag } private static File getFile( String path ) diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index c1d96ae3..ae27f374 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -13,6 +13,7 @@ package cuchaz.enigma; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -23,6 +24,7 @@ import javassist.CtClass; import javassist.NotFoundException; import javassist.bytecode.Descriptor; +import com.beust.jcommander.internal.Maps; import com.strobel.assembler.metadata.Buffer; import com.strobel.assembler.metadata.ITypeLoader; @@ -31,6 +33,7 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.bytecode.ClassTranslator; import cuchaz.enigma.bytecode.InnerClassWriter; import cuchaz.enigma.bytecode.MethodParameterWriter; +import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Translator; public class TranslatingTypeLoader implements ITypeLoader @@ -39,6 +42,7 @@ public class TranslatingTypeLoader implements ITypeLoader private JarIndex m_jarIndex; private Translator m_obfuscatingTranslator; private Translator m_deobfuscatingTranslator; + private Map m_cache; public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator ) { @@ -46,32 +50,71 @@ public class TranslatingTypeLoader implements ITypeLoader m_jarIndex = jarIndex; m_obfuscatingTranslator = obfuscatingTranslator; m_deobfuscatingTranslator = deobfuscatingTranslator; + m_cache = Maps.newHashMap(); } @Override public boolean tryLoadType( String deobfClassName, Buffer out ) + { + // check the cache + byte[] data; + if( m_cache.containsKey( deobfClassName ) ) + { + data = m_cache.get( deobfClassName ); + } + else + { + data = loadType( deobfClassName ); + m_cache.put( deobfClassName, data ); + } + + if( data == null ) + { + return false; + } + + // send the class to the decompiler + out.reset( data.length ); + System.arraycopy( data, 0, out.array(), out.position(), data.length ); + out.position( 0 ); + return true; + } + + private byte[] loadType( String deobfClassName ) { // what class file should we actually load? - String obfClassName = m_obfuscatingTranslator.translateClass( deobfClassName ); - if( obfClassName == null ) + ClassEntry deobfClassEntry = new ClassEntry( deobfClassName ); + ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry( deobfClassEntry ); + + // is this an inner class referenced directly? + if( m_jarIndex.getOuterClass( obfClassEntry.getName() ) != null ) { - obfClassName = deobfClassName; + // this class doesn't really exist. Reference it by outer$inner instead + System.err.println( String.format( "WARNING: class %s referenced by bare inner name", deobfClassName ) ); + return null; } - String classFileName = obfClassName; - // is this an inner class? - if( obfClassName.indexOf( '$' ) >= 0 ) + /* DEBUG + if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) { - // the file name is the bare inner class name - String[] parts = obfClassName.split( "\\$" ); - classFileName = parts[parts.length - 1]; + System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); } + */ // get the jar entry + String classFileName; + if( obfClassEntry.isInnerClass() ) + { + classFileName = obfClassEntry.getInnerClassName(); + } + else + { + classFileName = obfClassEntry.getOuterClassName(); + } JarEntry entry = m_jar.getJarEntry( classFileName + ".class" ); if( entry == null ) { - return false; + return null; } try @@ -93,35 +136,49 @@ public class TranslatingTypeLoader implements ITypeLoader in.close(); buf = data.toByteArray(); - // load the javassist handle to the class + // load the javassist handle to the raw class String javaClassFileName = Descriptor.toJavaName( classFileName ); ClassPool classPool = new ClassPool(); classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); CtClass c = classPool.get( javaClassFileName ); + // reconstruct inner classes + new InnerClassWriter( m_jarIndex ).write( c ); + + // re-get the javassist handle since we changed class names + String javaClassReconstructedName = Descriptor.toJavaName( obfClassEntry.getName() ); + classPool = new ClassPool(); + classPool.insertClassPath( new ByteArrayClassPath( javaClassReconstructedName, c.toBytecode() ) ); + c = classPool.get( javaClassReconstructedName ); + + // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong) + assertClassName( c, obfClassEntry ); + // do all kinds of deobfuscating transformations on the class - new InnerClassWriter( m_deobfuscatingTranslator, m_jarIndex ).write( c ); new BridgeFixer().fixBridges( c ); new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); // sanity checking - assert( Descriptor.toJvmName( c.getName() ).equals( deobfClassName ) ) - : String.format( "%s is not %s", Descriptor.toJvmName( c.getName() ), deobfClassName ); - assert( Descriptor.toJvmName( c.getClassFile().getName() ).equals( deobfClassName ) ) - : String.format( "%s is not %s", Descriptor.toJvmName( c.getClassFile().getName() ), deobfClassName ); - - // pass the transformed class along to the decompiler - buf = c.toBytecode(); - out.reset( buf.length ); - System.arraycopy( buf, 0, out.array(), out.position(), buf.length ); - out.position( 0 ); + assertClassName( c, deobfClassEntry ); - return true; + // we have a transformed class! + return c.toBytecode(); } catch( IOException | NotFoundException | CannotCompileException ex ) { throw new Error( ex ); } } + + private void assertClassName( CtClass c, ClassEntry obfClassEntry ) + { + String name1 = Descriptor.toJvmName( c.getName() ); + assert( name1.equals( obfClassEntry.getName() ) ) + : String.format( "Looking for %s, instead found %s", obfClassEntry.getName(), name1 ); + + String name2 = Descriptor.toJvmName( c.getClassFile().getName() ); + assert( name2.equals( obfClassEntry.getName() ) ) + : String.format( "Looking for %s, instead found %s", obfClassEntry.getName(), name2 ); + } } diff --git a/src/cuchaz/enigma/Util.java b/src/cuchaz/enigma/Util.java index 84927fd9..3686ef02 100644 --- a/src/cuchaz/enigma/Util.java +++ b/src/cuchaz/enigma/Util.java @@ -12,6 +12,8 @@ package cuchaz.enigma; import java.awt.Desktop; import java.io.Closeable; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -19,6 +21,10 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.jar.JarFile; +import javassist.CannotCompileException; +import javassist.CtClass; +import javassist.bytecode.Descriptor; + import com.google.common.io.CharStreams; @@ -106,4 +112,18 @@ public class Util } } } + + public static void writeClass( CtClass c ) + { + String name = Descriptor.toJavaName( c.getName() ); + File file = new File( name + ".class" ); + try( FileOutputStream out = new FileOutputStream( file ) ) + { + out.write( c.toBytecode() ); + } + catch( IOException | CannotCompileException ex ) + { + throw new Error( ex ); + } + } } diff --git a/src/cuchaz/enigma/analysis/BridgeFixer.java b/src/cuchaz/enigma/analysis/BridgeFixer.java index db441d2b..ee90f513 100644 --- a/src/cuchaz/enigma/analysis/BridgeFixer.java +++ b/src/cuchaz/enigma/analysis/BridgeFixer.java @@ -41,6 +41,8 @@ public class BridgeFixer { bridgedMethod.setName( method.getName() ); method.setModifiers( method.getModifiers() | AccessFlag.BRIDGE ); + + // TODO: rename all references to this method? } } } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 34e8986f..7d68c357 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -10,6 +10,7 @@ ******************************************************************************/ package cuchaz.enigma.analysis; +import java.lang.reflect.Modifier; import java.util.AbstractMap; import java.util.Collection; import java.util.Iterator; @@ -92,8 +93,8 @@ public class JarIndex // pass 2: index inner classes and anonymous classes for( CtClass c : JarClassIterator.classes( jar ) ) { - String outerClassName = isInnerClass( c ); - if( outerClassName != null ) + String outerClassName = findOuterClass( c ); + if( outerClassName != null )// /* TEMP */ && false ) { String innerClassName = Descriptor.toJvmName( c.getName() ); m_innerClasses.put( outerClassName, innerClassName ); @@ -102,6 +103,14 @@ public class JarIndex if( isAnonymousClass( c, outerClassName ) ) { m_anonymousClasses.add( innerClassName ); + + // DEBUG + System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); + } + else + { + // DEBUG + System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); } } } @@ -175,6 +184,10 @@ public class JarIndex @Override public void edit( ConstructorCall call ) { + boolean isSuper = call.getMethodName().equals( "super" ); + // TODO: make method reference class, update method calls tree to use Invocation instances + // this might end up being a big refactor... =( + String className = Descriptor.toJvmName( call.getClassName() ); ConstructorEntry calledConstructorEntry = new ConstructorEntry( new ClassEntry( className ), @@ -201,75 +214,168 @@ public class JarIndex } } - @SuppressWarnings( "unchecked" ) - private String isInnerClass( CtClass c ) + private String findOuterClass( CtClass c ) { // inner classes: - // the outer class is always a synthetic field - // there's at least one constructor with the type of the synthetic field as an argument - - for( FieldInfo field : (List)c.getClassFile().getFields() ) + // have constructors that can (illegally) set synthetic fields + // the outer class is the only class that calls constructors + + // use the synthetic fields to find the synthetic constructors + for( CtConstructor constructor : c.getDeclaredConstructors() ) { - boolean isSynthetic = (field.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; - if( !isSynthetic ) + if( !isIllegalConstructor( constructor ) ) { continue; } - // skip non-class types - if( !field.getDescriptor().startsWith( "L" ) ) + // who calls this constructor? + Set callerClasses = Sets.newHashSet(); + ConstructorEntry constructorEntry = new ConstructorEntry( + new ClassEntry( Descriptor.toJvmName( c.getName() ) ), + constructor.getMethodInfo().getDescriptor() + ); + for( Entry callerEntry : getMethodCallers( constructorEntry ) ) { - continue; + callerClasses.add( callerEntry.getClassEntry() ); } - // get the outer class from the field type - String outerClassName = Descriptor.toJvmName( Descriptor.toClassName( field.getDescriptor() ) ); - - // look for a constructor where this type is the first parameter - CtConstructor targetConstructor = null; - for( CtConstructor constructor : c.getDeclaredConstructors() ) + // is this called by exactly one class? + if( callerClasses.size() == 1 ) + { + return callerClasses.iterator().next().getName(); + } + else if( callerClasses.size() > 1 ) + { + // TEMP + System.out.println( "WARNING: Illegal class called by more than one class!" + callerClasses ); + } + } + + return null; + } + + @SuppressWarnings( "unchecked" ) + private boolean isIllegalConstructor( CtConstructor constructor ) + { + // illegal constructors only set synthetic member fields, then call super() + String className = constructor.getDeclaringClass().getName(); + + // collect all the field accesses, constructor calls, and method calls + final List illegalFieldWrites = Lists.newArrayList(); + final List constructorCalls = Lists.newArrayList(); + final List methodCalls = Lists.newArrayList(); + try + { + constructor.instrument( new ExprEditor( ) { - String signature = Descriptor.getParamDescriptor( constructor.getMethodInfo().getDescriptor() ); - if( Descriptor.numOfParameters( signature ) < 1 ) + @Override + public void edit( FieldAccess fieldAccess ) { - continue; + if( fieldAccess.isWriter() && constructorCalls.isEmpty() ) + { + illegalFieldWrites.add( fieldAccess ); + } } - // match the first parameter to the outer class - Descriptor.Iterator iter = new Descriptor.Iterator( signature ); - int pos = iter.next(); - if( iter.isParameter() && signature.charAt( pos ) == 'L' ) + @Override + public void edit( ConstructorCall constructorCall ) { - String argumentDesc = signature.substring( pos, signature.indexOf(';', pos) + 1 ); - String argumentClassName = Descriptor.toJvmName( Descriptor.toClassName( argumentDesc ) ); - if( argumentClassName.equals( outerClassName ) ) - { - targetConstructor = constructor; - break; - } + constructorCalls.add( constructorCall ); + } + + @Override + public void edit( MethodCall methodCall ) + { + methodCalls.add( methodCall ); } + } ); + } + catch( CannotCompileException ex ) + { + // we're not compiling anything... this is stupid + throw new Error( ex ); + } + + // method calls are not allowed + if( !methodCalls.isEmpty() ) + { + return false; + } + + // is there only one constructor call? + if( constructorCalls.size() != 1 ) + { + return false; + } + + // is the call to super? + ConstructorCall constructorCall = constructorCalls.get( 0 ); + if( !constructorCall.getMethodName().equals( "super" ) ) + { + return false; + } + + // are there any illegal field writes? + if( illegalFieldWrites.isEmpty() ) + { + return false; + } + + // are all the writes to synthetic fields? + for( FieldAccess fieldWrite : illegalFieldWrites ) + { + // all illegal writes have to be to the local class + if( !fieldWrite.getClassName().equals( className ) ) + { + System.err.println( String.format( "WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName() ) ); + return false; } - if( targetConstructor == null ) + + // find the field + FieldInfo fieldInfo = null; + for( FieldInfo info : (List)constructor.getDeclaringClass().getClassFile().getFields() ) { - continue; + if( info.getName().equals( fieldWrite.getFieldName() ) ) + { + fieldInfo = info; + break; + } + } + if( fieldInfo == null ) + { + // field is in a superclass or something, can't be a local synthetic member + return false; } - // yeah, this is an inner class - return outerClassName; + // is this field synthetic? + boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; + if( !isSynthetic ) + { + System.err.println( String.format( "WARNING: illegal write to non synthetic field %s.%s", className, fieldInfo.getName() ) ); + return false; + } } - return null; + // we passed all the tests! + return true; } - + private boolean isAnonymousClass( CtClass c, String outerClassName ) { String innerClassName = Descriptor.toJvmName( c.getName() ); // anonymous classes: + // can't be abstract // have only one constructor // it's called exactly once by the outer class // type of inner class not referenced anywhere in outer class + // is absract? + if( Modifier.isAbstract( c.getModifiers() ) ) + { + return false; + } + // is there exactly one constructor? if( c.getDeclaredConstructors().length != 1 ) { @@ -282,7 +388,16 @@ public class JarIndex new ClassEntry( innerClassName ), constructor.getMethodInfo().getDescriptor() ); - return getMethodCallers( constructorEntry ).size() == 1; + if( getMethodCallers( constructorEntry ).size() != 1 ) + { + return false; + } + + // TODO: check outer class doesn't reference type + // except this is hard because we can't just load the outer class now + // we'd have to pre-index those references in the JarIndex + + return true; } public Set getObfClassNames( ) diff --git a/src/cuchaz/enigma/bytecode/BytecodeTools.java b/src/cuchaz/enigma/bytecode/BytecodeTools.java index 664350ea..0de9bd6b 100644 --- a/src/cuchaz/enigma/bytecode/BytecodeTools.java +++ b/src/cuchaz/enigma/bytecode/BytecodeTools.java @@ -15,6 +15,7 @@ import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.List; import java.util.Map; import java.util.Set; @@ -25,6 +26,7 @@ import javassist.bytecode.CodeAttribute; import javassist.bytecode.ConstPool; import javassist.bytecode.ExceptionTable; +import com.beust.jcommander.internal.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -266,4 +268,59 @@ public class BytecodeTools ); } } + + public static List getParameterTypes( String signature ) + { + List types = Lists.newArrayList(); + for( int i=0; i 0 ) + { + type = "[" + type; + } + types.add( type ); + } + return types; + } } diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 3b5beeb2..9ce06a58 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -10,7 +10,6 @@ ******************************************************************************/ package cuchaz.enigma.bytecode; -import java.util.HashSet; import java.util.Set; import javassist.ClassMap; @@ -20,6 +19,10 @@ import javassist.CtField; import javassist.CtMethod; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; +import javassist.bytecode.InnerClassesAttribute; + +import com.beust.jcommander.internal.Sets; + import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; @@ -133,25 +136,47 @@ public class ClassTranslator // translate all the class names referenced in the code // the above code only changed method/field/reference names and types, but not the class names themselves - Set classNames = getAllClassNames( c ); + Set classEntries = getAllClassEntries( c ); ClassMap map = new ClassMap(); - for( String className : classNames ) + for( ClassEntry obfClassEntry : classEntries ) { - String translatedName = m_translator.translateClass( className ); - if( translatedName != null ) - { - map.put( className, translatedName ); - } + map.put( obfClassEntry.getName(), m_translator.translateEntry( obfClassEntry ).getName() ); } - if( !map.isEmpty() ) + c.replaceClassName( map ); + + // translate the names in the InnerClasses attribute + InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); + if( attr != null ) { - c.replaceClassName( map ); + for( int i=0; i ATTR: %s,%s,%s", + obfClassEntry, deobfClassEntry, + attr.outerClass( i ), + attr.innerClass( i ), + attr.innerName( i ) + ) ); + */ + } } } - private Set getAllClassNames( CtClass c ) + private Set getAllClassEntries( CtClass c ) { - final Set names = new HashSet(); + final Set entries = Sets.newHashSet(); ClassMap map = new ClassMap( ) { @Override @@ -159,13 +184,13 @@ public class ClassTranslator { if( obj instanceof String ) { - names.add( (String)obj ); + entries.add( new ClassEntry( (String)obj ) ); } return null; } private static final long serialVersionUID = -202160293602070641L; }; c.replaceClassName( map ); - return names; + return entries; } } diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index b0e33ac2..c412b1aa 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -18,16 +18,14 @@ import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.InnerClassesAttribute; import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.mapping.Translator; +import cuchaz.enigma.mapping.ClassEntry; public class InnerClassWriter { - private Translator m_deobfuscatingTranslator; private JarIndex m_jarIndex; - public InnerClassWriter( Translator deobfuscatingTranslator, JarIndex jarIndex ) + public InnerClassWriter( JarIndex jarIndex ) { - m_deobfuscatingTranslator = deobfuscatingTranslator; m_jarIndex = jarIndex; } @@ -44,7 +42,8 @@ public class InnerClassWriter else { // this is an inner class, rename it to outer$inner - c.setName( obfOuterClassName + "$" + obfClassName ); + ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfClassName ); + c.setName( obfClassEntry.getName() ); } // write the inner classes if needed @@ -62,31 +61,20 @@ public class InnerClassWriter for( String obfInnerClassName : obfInnerClassNames ) { // deobfuscate the class names - String deobfOuterClassName = m_deobfuscatingTranslator.translateClass( obfOuterClassName ); - if( deobfOuterClassName == null ) - { - deobfOuterClassName = obfOuterClassName; - } - String obfOuterInnerClassName = obfOuterClassName + "$" + obfInnerClassName; - String deobfOuterInnerClassName = m_deobfuscatingTranslator.translateClass( obfOuterInnerClassName ); - if( deobfOuterInnerClassName == null ) - { - deobfOuterInnerClassName = obfOuterInnerClassName; - } - String deobfInnerClassName = deobfOuterInnerClassName.substring( deobfOuterInnerClassName.lastIndexOf( '$' ) + 1 ); - + ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfInnerClassName ); + // here's what the JVM spec says about the InnerClasses attribute // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); // update the attribute with this inner class ConstPool constPool = c.getClassFile().getConstPool(); - int innerClassIndex = constPool.addClassInfo( deobfOuterInnerClassName ); + int innerClassIndex = constPool.addClassInfo( obfClassEntry.getName() ); int outerClassIndex = 0; int innerClassSimpleNameIndex = 0; if( !m_jarIndex.isAnonymousClass( obfInnerClassName ) ) { - outerClassIndex = constPool.addClassInfo( deobfOuterClassName ); - innerClassSimpleNameIndex = constPool.addUtf8Info( deobfInnerClassName ); + outerClassIndex = constPool.addClassInfo( obfClassEntry.getOuterClassName() ); + innerClassSimpleNameIndex = constPool.addUtf8Info( obfClassEntry.getInnerClassName() ); } attr.append( @@ -96,8 +84,18 @@ public class InnerClassWriter c.getClassFile().getAccessFlags() & ~AccessFlag.SUPER ); + /* DEBUG + System.out.println( String.format( "\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", + obfClassEntry, + attr.outerClass( attr.tableLength() - 1 ), + attr.innerClass( attr.tableLength() - 1 ), + attr.innerName( attr.tableLength() - 1 ), + obfInnerClassName, obfClassEntry.getName() + ) ); + */ + // make sure the outer class references only the new inner class names - c.replaceClassName( obfInnerClassName, deobfOuterInnerClassName ); + c.replaceClassName( obfInnerClassName, obfClassEntry.getName() ); } } } diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 76f45cd6..fc41f945 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -72,11 +72,21 @@ public class Translator public ClassEntry translateEntry( ClassEntry in ) { String name = translate( in ); - if( name == null ) + if( name != null ) + { + return new ClassEntry( name ); + } + + if( in.isInnerClass() ) { - return in; + // just translate the outer class name + String outerClassName = translate( in.getOuterClassEntry() ); + if( outerClassName != null ) + { + return new ClassEntry( outerClassName + "$" + in.getInnerClassName() ); + } } - return new ClassEntry( name ); + return in; } public String translate( FieldEntry in ) -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 From fb94b7b01070c176f096d2a6e43c495ebea80bbe Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 18 Aug 2014 00:57:14 -0400 Subject: packaged v0.2 beta! --- readme.txt | 2 +- src/cuchaz/enigma/Constants.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.txt b/readme.txt index 37197c9d..944c4890 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ -Enigma v0.1 beta +Enigma v0.2 beta A tool for deobfuscation of Java bytecode Copyright Jeff Martin, 2014 diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 72275628..0b431c71 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -14,7 +14,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.1 beta"; + public static final String Version = "0.2 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024*1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte -- cgit v1.2.3 -- cgit v1.2.3 From 89612c3ce7a96c9bb4564ae414c9d40f8b2901a3 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 18 Aug 2014 21:28:49 -0400 Subject: fixed about dialog --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index a0b5bbc9..05f12504 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ sourceSets } resources { - srcDir "assets" + srcDir "conf" } } test @@ -42,7 +42,7 @@ sourceSets } resources { - srcDir "assets" + srcDir "conf" } } } -- cgit v1.2.3 From 58fe3a4ec77507345bc2a96857b04534bcb845a7 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 18 Aug 2014 22:09:24 -0400 Subject: fixed type caching after rename allowed enums constants to be renamable --- src/cuchaz/enigma/Deobfuscator.java | 13 ++++++++++--- src/cuchaz/enigma/TranslatingTypeLoader.java | 5 +++++ src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 18 ++++++++++++------ src/cuchaz/enigma/gui/GuiController.java | 9 ++++++++- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 323aa2ed..293e1c2f 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -29,6 +29,7 @@ import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; +import cuchaz.enigma.analysis.TreeDumpVisitor; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; @@ -49,6 +50,7 @@ public class Deobfuscator private JarIndex m_jarIndex; private Mappings m_mappings; private Renamer m_renamer; + private TranslatingTypeLoader m_typeLoader; public Deobfuscator( File file ) throws IOException @@ -93,12 +95,13 @@ public class Deobfuscator m_renamer = new Renamer( m_jarIndex, m_mappings ); // update decompiler options - m_settings.setTypeLoader( new TranslatingTypeLoader( + m_typeLoader = new TranslatingTypeLoader( m_jar, m_jarIndex, getTranslator( TranslationDirection.Obfuscating ), getTranslator( TranslationDirection.Deobfuscating ) - ) ); + ); + m_settings.setTypeLoader( m_typeLoader ); } public Translator getTranslator( TranslationDirection direction ) @@ -158,7 +161,8 @@ public class Deobfuscator // render the AST into source StringWriter buf = new StringWriter(); root.acceptVisitor( new InsertParenthesesVisitor(), null ); - //root.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); + // DEBUG + root.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); root.acceptVisitor( new JavaOutputVisitor( new PlainTextOutput( buf ), m_settings ), null ); // build the source index @@ -196,6 +200,9 @@ public class Deobfuscator { throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); } + + // clear the type loader cache + m_typeLoader.clearCache(); } public Entry obfuscateEntry( Entry deobfEntry ) diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index ae27f374..cc863646 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -53,6 +53,11 @@ public class TranslatingTypeLoader implements ITypeLoader m_cache = Maps.newHashMap(); } + public void clearCache( ) + { + m_cache.clear(); + } + @Override public boolean tryLoadType( String deobfClassName, Buffer out ) { diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java index 6c14ee99..f31eb1a8 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -201,6 +201,18 @@ public class SourceIndexVisitor implements IAstVisitor return recurse( node, index ); } + @Override + public Void visitEnumValueDeclaration( EnumValueDeclaration node, SourceIndex index ) + { + // treat enum declarations as field declarations + FieldDefinition def = node.getUserData( Keys.FIELD_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + FieldEntry fieldEntry = new FieldEntry( classEntry, def.getName() ); + index.addDeclaration( node.getNameToken(), fieldEntry ); + + return recurse( node, index ); + } + private Void recurse( AstNode node, SourceIndex index ) { for( final AstNode child : node.getChildren() ) @@ -560,12 +572,6 @@ public class SourceIndexVisitor implements IAstVisitor return recurse( node, index ); } - @Override - public Void visitEnumValueDeclaration( EnumValueDeclaration node, SourceIndex index ) - { - return recurse( node, index ); - } - @Override public Void visitAssertStatement( AssertStatement node, SourceIndex index ) { diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index ffeb99aa..f305e341 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -272,7 +272,14 @@ public class GuiController m_gui.setSource( m_index.getSource() ); if( obfEntryToShow != null ) { - m_gui.showToken( m_index.getDeclarationToken( m_deobfuscator.deobfuscateEntry( obfEntryToShow ) ) ); + Entry deobfEntryToShow = m_deobfuscator.deobfuscateEntry( obfEntryToShow ); + Token token = m_index.getDeclarationToken( deobfEntryToShow ); + if( token == null ) + { + // TEMP + System.out.println( "WARNING: can't find token for " + obfEntryToShow + " -> " + deobfEntryToShow ); + } + m_gui.showToken( token ); } // set the highlighted tokens -- cgit v1.2.3 From c4e35f2d516ade27e8e1a863b4bc356f182f43c2 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 19 Aug 2014 00:25:32 -0400 Subject: started new reference navigation system --- .../enigma/analysis/BehaviorReferenceTreeNode.java | 105 +++++++++++++++ src/cuchaz/enigma/analysis/EntryReference.java | 28 ++++ src/cuchaz/enigma/analysis/FieldCallsTreeNode.java | 82 ------------ .../enigma/analysis/FieldReferenceTreeNode.java | 92 +++++++++++++ src/cuchaz/enigma/analysis/JarIndex.java | 141 +++++++++++++------- .../enigma/analysis/MethodCallsTreeNode.java | 144 --------------------- src/cuchaz/enigma/analysis/ReferenceTreeNode.java | 19 +++ src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 20 ++- src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 11 +- src/cuchaz/enigma/gui/Gui.java | 46 ++++--- src/cuchaz/enigma/gui/GuiController.java | 65 +++++----- src/cuchaz/enigma/mapping/BehaviorEntry.java | 6 + src/cuchaz/enigma/mapping/ConstructorEntry.java | 3 +- src/cuchaz/enigma/mapping/MethodEntry.java | 3 +- src/cuchaz/enigma/mapping/Translator.java | 13 ++ 15 files changed, 441 insertions(+), 337 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/EntryReference.java delete mode 100644 src/cuchaz/enigma/analysis/FieldCallsTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java delete mode 100644 src/cuchaz/enigma/analysis/MethodCallsTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/ReferenceTreeNode.java create mode 100644 src/cuchaz/enigma/mapping/BehaviorEntry.java diff --git a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java new file mode 100644 index 00000000..158aad71 --- /dev/null +++ b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.Set; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; + +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.Translator; + +public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode +{ + private static final long serialVersionUID = -3658163700783307520L; + + private Translator m_deobfuscatingTranslator; + private BehaviorEntry m_entry; + private EntryReference m_reference; + + public BehaviorReferenceTreeNode( Translator deobfuscatingTranslator, BehaviorEntry entry ) + { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + m_reference = null; + } + + public BehaviorReferenceTreeNode( Translator deobfuscatingTranslator, EntryReference reference ) + { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = reference.entry; + m_reference = reference; + } + + @Override + public BehaviorEntry getEntry( ) + { + return m_entry; + } + + @Override + public EntryReference getReference( ) + { + return m_reference; + } + + @Override + public String toString( ) + { + if( m_reference != null ) + { + return m_deobfuscatingTranslator.translateEntry( m_reference.caller ).toString(); + } + return m_deobfuscatingTranslator.translateEntry( m_entry ).toString(); + } + + public void load( JarIndex index, boolean recurse ) + { + // get all the child nodes + for( EntryReference reference : index.getBehaviorReferences( m_entry ) ) + { + add( new BehaviorReferenceTreeNode( m_deobfuscatingTranslator, reference ) ); + } + + if( recurse && children != null ) + { + for( Object child : children ) + { + if( child instanceof BehaviorReferenceTreeNode ) + { + BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode)child; + + // don't recurse into ancestor + Set ancestors = Sets.newHashSet(); + TreeNode n = (TreeNode)node; + while( n.getParent() != null ) + { + n = n.getParent(); + if( n instanceof BehaviorReferenceTreeNode ) + { + ancestors.add( ((BehaviorReferenceTreeNode)n).getEntry() ); + } + } + if( ancestors.contains( node.getEntry() ) ) + { + continue; + } + + node.load( index, true ); + } + } + } + } +} diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java new file mode 100644 index 00000000..f462210a --- /dev/null +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.Entry; + +public class EntryReference +{ + public T entry; + public BehaviorEntry caller; + public int pos; + + public EntryReference( T entry, BehaviorEntry caller, int pos ) + { + this.entry = entry; + this.caller = caller; + this.pos = pos; + } +} diff --git a/src/cuchaz/enigma/analysis/FieldCallsTreeNode.java b/src/cuchaz/enigma/analysis/FieldCallsTreeNode.java deleted file mode 100644 index 0427b3be..00000000 --- a/src/cuchaz/enigma/analysis/FieldCallsTreeNode.java +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import javax.swing.tree.DefaultMutableTreeNode; - -import cuchaz.enigma.mapping.ConstructorEntry; -import cuchaz.enigma.mapping.Entry; -import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.MethodEntry; -import cuchaz.enigma.mapping.Translator; - -public class FieldCallsTreeNode extends DefaultMutableTreeNode -{ - private static final long serialVersionUID = -7934108091928699835L; - - private Translator m_deobfuscatingTranslator; - private FieldEntry m_entry; - - public FieldCallsTreeNode( Translator deobfuscatingTranslator, FieldEntry fieldEntry ) - { - m_deobfuscatingTranslator = deobfuscatingTranslator; - m_entry = fieldEntry; - } - - public FieldEntry getFieldEntry( ) - { - return m_entry; - } - - @Override - public String toString( ) - { - String className = m_deobfuscatingTranslator.translateClass( m_entry.getClassName() ); - if( className == null ) - { - className = m_entry.getClassName(); - } - - String targetName = m_deobfuscatingTranslator.translate( m_entry ); - if( targetName == null ) - { - targetName = m_entry.getName(); - } - return className + "." + targetName; - } - - public void load( JarIndex index, boolean recurse ) - { - // get all the child nodes - for( Entry entry : index.getFieldCallers( m_entry ) ) - { - if( entry instanceof MethodEntry ) - { - add( new MethodCallsTreeNode( m_deobfuscatingTranslator, (MethodEntry)entry ) ); - } - else if( entry instanceof ConstructorEntry ) - { - add( new MethodCallsTreeNode( m_deobfuscatingTranslator, (ConstructorEntry)entry ) ); - } - } - - if( recurse && children != null ) - { - for( Object node : children ) - { - if( node instanceof MethodCallsTreeNode ) - { - ((MethodCallsTreeNode)node).load( index, true ); - } - } - } - } -} diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java new file mode 100644 index 00000000..dd552d68 --- /dev/null +++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import javax.swing.tree.DefaultMutableTreeNode; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.Translator; + +public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode +{ + private static final long serialVersionUID = -7934108091928699835L; + + private Translator m_deobfuscatingTranslator; + private FieldEntry m_entry; + private EntryReference m_reference; + + public FieldReferenceTreeNode( Translator deobfuscatingTranslator, FieldEntry entry ) + { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + m_reference = null; + } + + private FieldReferenceTreeNode( Translator deobfuscatingTranslator, EntryReference reference ) + { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = reference.entry; + m_reference = reference; + } + + @Override + public FieldEntry getEntry( ) + { + return m_entry; + } + + @Override + public EntryReference getReference( ) + { + return m_reference; + } + + @Override + public String toString( ) + { + if( m_reference != null ) + { + return m_deobfuscatingTranslator.translateEntry( m_reference.caller ).toString(); + } + return m_deobfuscatingTranslator.translateEntry( m_entry ).toString(); + } + + public void load( JarIndex index, boolean recurse ) + { + // get all the child nodes + if( m_reference == null ) + { + for( EntryReference reference : index.getFieldReferences( m_entry ) ) + { + add( new FieldReferenceTreeNode( m_deobfuscatingTranslator, reference ) ); + } + } + else + { + for( EntryReference reference : index.getBehaviorReferences( m_reference.caller ) ) + { + add( new BehaviorReferenceTreeNode( m_deobfuscatingTranslator, reference ) ); + } + } + + if( recurse && children != null ) + { + for( Object node : children ) + { + if( node instanceof BehaviorReferenceTreeNode ) + { + ((BehaviorReferenceTreeNode)node).load( index, true ); + } + } + } + } +} diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 7d68c357..f1c29c5b 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -35,12 +35,15 @@ import javassist.expr.MethodCall; import javassist.expr.NewExpr; import com.google.common.collect.HashMultimap; +import com.google.common.collect.HashMultiset; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; +import com.google.common.collect.Multiset; import com.google.common.collect.Sets; import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; @@ -53,8 +56,8 @@ public class JarIndex private Set m_obfClassNames; private Ancestries m_ancestries; private Multimap m_methodImplementations; - private Multimap m_methodCalls; - private Multimap m_fieldCalls; + private Multimap> m_behaviorReferences; + private Multimap> m_fieldReferences; private Multimap m_innerClasses; private Map m_outerClasses; private Set m_anonymousClasses; @@ -64,8 +67,8 @@ public class JarIndex m_obfClassNames = Sets.newHashSet(); m_ancestries = new Ancestries(); m_methodImplementations = HashMultimap.create(); - m_methodCalls = HashMultimap.create(); - m_fieldCalls = HashMultimap.create(); + m_behaviorReferences = HashMultimap.create(); + m_fieldReferences = HashMultimap.create(); m_innerClasses = HashMultimap.create(); m_outerClasses = Maps.newHashMap(); m_anonymousClasses = Sets.newHashSet(); @@ -128,7 +131,7 @@ public class JarIndex { // get the method entry String className = Descriptor.toJvmName( behavior.getDeclaringClass().getName() ); - final Entry thisEntry; + final BehaviorEntry thisEntry; if( behavior instanceof CtMethod ) { MethodEntry methodEntry = new MethodEntry( @@ -156,6 +159,7 @@ public class JarIndex // index method calls try { + final Multiset callNumbers = HashMultiset.create(); behavior.instrument( new ExprEditor( ) { @Override @@ -167,7 +171,13 @@ public class JarIndex call.getMethodName(), call.getSignature() ); - m_methodCalls.put( calledMethodEntry, thisEntry ); + callNumbers.add( calledMethodEntry ); + EntryReference reference = new EntryReference( + calledMethodEntry, + thisEntry, + callNumbers.count( calledMethodEntry ) - 1 + ); + m_behaviorReferences.put( calledMethodEntry, reference ); } @Override @@ -178,22 +188,33 @@ public class JarIndex new ClassEntry( className ), call.getFieldName() ); - m_fieldCalls.put( calledFieldEntry, thisEntry ); + callNumbers.add( calledFieldEntry ); + EntryReference reference = new EntryReference( + calledFieldEntry, + thisEntry, + callNumbers.count( calledFieldEntry ) - 1 + ); + m_fieldReferences.put( calledFieldEntry, reference ); } @Override public void edit( ConstructorCall call ) { + // TODO: save isSuper in the reference somehow boolean isSuper = call.getMethodName().equals( "super" ); - // TODO: make method reference class, update method calls tree to use Invocation instances - // this might end up being a big refactor... =( String className = Descriptor.toJvmName( call.getClassName() ); ConstructorEntry calledConstructorEntry = new ConstructorEntry( new ClassEntry( className ), call.getSignature() ); - m_methodCalls.put( calledConstructorEntry, thisEntry ); + callNumbers.add( calledConstructorEntry ); + EntryReference reference = new EntryReference( + calledConstructorEntry, + thisEntry, + callNumbers.count( calledConstructorEntry ) - 1 + ); + m_behaviorReferences.put( calledConstructorEntry, reference ); } @Override @@ -204,7 +225,13 @@ public class JarIndex new ClassEntry( className ), call.getSignature() ); - m_methodCalls.put( calledConstructorEntry, thisEntry ); + callNumbers.add( calledConstructorEntry ); + EntryReference reference = new EntryReference( + calledConstructorEntry, + thisEntry, + callNumbers.count( calledConstructorEntry ) - 1 + ); + m_behaviorReferences.put( calledConstructorEntry, reference ); } } ); } @@ -234,9 +261,9 @@ public class JarIndex new ClassEntry( Descriptor.toJvmName( c.getName() ) ), constructor.getMethodInfo().getDescriptor() ); - for( Entry callerEntry : getMethodCallers( constructorEntry ) ) + for( EntryReference reference : getBehaviorReferences( constructorEntry ) ) { - callerClasses.add( callerEntry.getClassEntry() ); + callerClasses.add( reference.caller.getClassEntry() ); } // is this called by exactly one class? @@ -388,7 +415,7 @@ public class JarIndex new ClassEntry( innerClassName ), constructor.getMethodInfo().getDescriptor() ); - if( getMethodCallers( constructorEntry ).size() != 1 ) + if( getBehaviorReferences( constructorEntry ).size() != 1 ) { return false; } @@ -469,14 +496,26 @@ public class JarIndex return rootNode; } - public Collection getFieldCallers( FieldEntry fieldEntry ) + @SuppressWarnings( "unchecked" ) + public Collection> getFieldReferences( FieldEntry fieldEntry ) { - return m_fieldCalls.get( fieldEntry ); + List> references = Lists.newArrayList(); + for( EntryReference reference : m_fieldReferences.get( fieldEntry ) ) + { + references.add( (EntryReference)reference ); + } + return references; } - public Collection getMethodCallers( Entry entry ) + @SuppressWarnings( "unchecked" ) + public Collection> getBehaviorReferences( BehaviorEntry behaviorEntry ) { - return m_methodCalls.get( entry ); + List> references = Lists.newArrayList(); + for( EntryReference reference : m_behaviorReferences.get( behaviorEntry ) ) + { + references.add( (EntryReference)reference ); + } + return references; } public Collection getInnerClasses( String obfOuterClassName ) @@ -498,85 +537,91 @@ public class JarIndex { m_ancestries.renameClasses( renames ); renameMultimap( renames, m_methodImplementations ); - renameMultimap( renames, m_methodCalls ); - renameMultimap( renames, m_fieldCalls ); + renameMultimap( renames, m_behaviorReferences ); + renameMultimap( renames, m_fieldReferences ); } - private void renameMultimap( Map renames, Multimap map ) + private void renameMultimap( Map renames, Multimap map ) { // for each key/value pair... - Set> entriesToAdd = Sets.newHashSet(); - Iterator> iter = map.entries().iterator(); + Set> entriesToAdd = Sets.newHashSet(); + Iterator> iter = map.entries().iterator(); while( iter.hasNext() ) { - Map.Entry entry = iter.next(); + Map.Entry entry = iter.next(); iter.remove(); - entriesToAdd.add( new AbstractMap.SimpleEntry( - renameEntry( renames, entry.getKey() ), - renameEntry( renames, entry.getValue() ) + entriesToAdd.add( new AbstractMap.SimpleEntry( + renameThing( renames, entry.getKey() ), + renameThing( renames, entry.getValue() ) ) ); } - for( Map.Entry entry : entriesToAdd ) + for( Map.Entry entry : entriesToAdd ) { map.put( entry.getKey(), entry.getValue() ); } } @SuppressWarnings( "unchecked" ) - private T renameEntry( Map renames, T entry ) + private T renameThing( Map renames, T thing ) { - if( entry instanceof String ) + if( thing instanceof String ) { - String stringEntry = (String)entry; + String stringEntry = (String)thing; if( renames.containsKey( stringEntry ) ) { return (T)renames.get( stringEntry ); } } - else if( entry instanceof ClassEntry ) + else if( thing instanceof ClassEntry ) { - ClassEntry classEntry = (ClassEntry)entry; - return (T)new ClassEntry( renameEntry( renames, classEntry.getClassName() ) ); + ClassEntry classEntry = (ClassEntry)thing; + return (T)new ClassEntry( renameThing( renames, classEntry.getClassName() ) ); } - else if( entry instanceof FieldEntry ) + else if( thing instanceof FieldEntry ) { - FieldEntry fieldEntry = (FieldEntry)entry; + FieldEntry fieldEntry = (FieldEntry)thing; return (T)new FieldEntry( - renameEntry( renames, fieldEntry.getClassEntry() ), + renameThing( renames, fieldEntry.getClassEntry() ), fieldEntry.getName() ); } - else if( entry instanceof ConstructorEntry ) + else if( thing instanceof ConstructorEntry ) { - ConstructorEntry constructorEntry = (ConstructorEntry)entry; + ConstructorEntry constructorEntry = (ConstructorEntry)thing; return (T)new ConstructorEntry( - renameEntry( renames, constructorEntry.getClassEntry() ), + renameThing( renames, constructorEntry.getClassEntry() ), constructorEntry.getSignature() ); } - else if( entry instanceof MethodEntry ) + else if( thing instanceof MethodEntry ) { - MethodEntry methodEntry = (MethodEntry)entry; + MethodEntry methodEntry = (MethodEntry)thing; return (T)new MethodEntry( - renameEntry( renames, methodEntry.getClassEntry() ), + renameThing( renames, methodEntry.getClassEntry() ), methodEntry.getName(), methodEntry.getSignature() ); } - else if( entry instanceof ArgumentEntry ) + else if( thing instanceof ArgumentEntry ) { - ArgumentEntry argumentEntry = (ArgumentEntry)entry; + ArgumentEntry argumentEntry = (ArgumentEntry)thing; return (T)new ArgumentEntry( - renameEntry( renames, argumentEntry.getMethodEntry() ), + renameThing( renames, argumentEntry.getMethodEntry() ), argumentEntry.getIndex(), argumentEntry.getName() ); } + else if( thing instanceof EntryReference ) + { + EntryReference reference = (EntryReference)thing; + reference.entry = renameThing( renames, reference.entry ); + return thing; + } else { - throw new Error( "Not an entry: " + entry ); + throw new Error( "Not an entry: " + thing ); } - return entry; + return thing; } } diff --git a/src/cuchaz/enigma/analysis/MethodCallsTreeNode.java b/src/cuchaz/enigma/analysis/MethodCallsTreeNode.java deleted file mode 100644 index b5cf4c33..00000000 --- a/src/cuchaz/enigma/analysis/MethodCallsTreeNode.java +++ /dev/null @@ -1,144 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import java.util.Set; - -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreeNode; - -import com.google.common.collect.Sets; - -import cuchaz.enigma.mapping.ConstructorEntry; -import cuchaz.enigma.mapping.Entry; -import cuchaz.enigma.mapping.MethodEntry; -import cuchaz.enigma.mapping.Translator; - -public class MethodCallsTreeNode extends DefaultMutableTreeNode -{ - private static final long serialVersionUID = -3658163700783307520L; - - private Translator m_deobfuscatingTranslator; - private MethodEntry m_methodEntry; - private ConstructorEntry m_constructorEntry; - - public MethodCallsTreeNode( Translator deobfuscatingTranslator, MethodEntry entry ) - { - m_deobfuscatingTranslator = deobfuscatingTranslator; - m_methodEntry = entry; - m_constructorEntry = null; - } - - public MethodCallsTreeNode( Translator deobfuscatingTranslator, ConstructorEntry entry ) - { - m_deobfuscatingTranslator = deobfuscatingTranslator; - m_methodEntry = null; - m_constructorEntry = entry; - } - - public MethodEntry getMethodEntry( ) - { - return m_methodEntry; - } - - public ConstructorEntry getConstructorEntry( ) - { - return m_constructorEntry; - } - - public Entry getEntry( ) - { - if( m_methodEntry != null ) - { - return m_methodEntry; - } - else if( m_constructorEntry != null ) - { - return m_constructorEntry; - } - throw new Error( "Illegal state!" ); - } - - @Override - public String toString( ) - { - if( m_methodEntry != null ) - { - String className = m_deobfuscatingTranslator.translateClass( m_methodEntry.getClassName() ); - if( className == null ) - { - className = m_methodEntry.getClassName(); - } - - String methodName = m_deobfuscatingTranslator.translate( m_methodEntry ); - if( methodName == null ) - { - methodName = m_methodEntry.getName(); - } - return className + "." + methodName + "()"; - } - else if( m_constructorEntry != null ) - { - String className = m_deobfuscatingTranslator.translateClass( m_constructorEntry.getClassName() ); - if( className == null ) - { - className = m_constructorEntry.getClassName(); - } - return className + "()"; - } - throw new Error( "Illegal state!" ); - } - - public void load( JarIndex index, boolean recurse ) - { - // get all the child nodes - for( Entry entry : index.getMethodCallers( getEntry() ) ) - { - if( entry instanceof MethodEntry ) - { - add( new MethodCallsTreeNode( m_deobfuscatingTranslator, (MethodEntry)entry ) ); - } - else if( entry instanceof ConstructorEntry ) - { - add( new MethodCallsTreeNode( m_deobfuscatingTranslator, (ConstructorEntry)entry ) ); - } - } - - if( recurse && children != null ) - { - for( Object child : children ) - { - if( child instanceof MethodCallsTreeNode ) - { - MethodCallsTreeNode node = (MethodCallsTreeNode)child; - - // don't recurse into ancestor - Set ancestors = Sets.newHashSet(); - TreeNode n = (TreeNode)node; - while( n.getParent() != null ) - { - n = n.getParent(); - if( n instanceof MethodCallsTreeNode ) - { - ancestors.add( ((MethodCallsTreeNode)n).getEntry() ); - } - } - if( ancestors.contains( node.getEntry() ) ) - { - continue; - } - - node.load( index, true ); - } - } - } - } -} diff --git a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java new file mode 100644 index 00000000..08ae39d9 --- /dev/null +++ b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import cuchaz.enigma.mapping.Entry; + +public interface ReferenceTreeNode +{ + T getEntry(); + EntryReference getReference(); +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java index f31eb1a8..841d1767 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -213,6 +213,20 @@ public class SourceIndexVisitor implements IAstVisitor return recurse( node, index ); } + @Override + public Void visitIdentifierExpression( IdentifierExpression node, SourceIndex index ) + { + MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); + if( ref != null ) + { + ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); + FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); + index.add( node.getIdentifierToken(), fieldEntry ); + } + + return recurse( node, index ); + } + private Void recurse( AstNode node, SourceIndex index ) { for( final AstNode child : node.getChildren() ) @@ -476,12 +490,6 @@ public class SourceIndexVisitor implements IAstVisitor return recurse( node, index ); } - @Override - public Void visitIdentifierExpression( IdentifierExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - @Override public Void visitUnaryOperatorExpression( UnaryOperatorExpression node, SourceIndex index ) { diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java index ac3e92db..12febefd 100644 --- a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java +++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -122,7 +122,7 @@ public class TreeDumpVisitor implements IAstVisitor // show the tree try { - m_out.write( getIndent( node ) + node.getClass().getSimpleName() + dumpUserData( node ) + " " + node.getRegion() + "\n" ); + m_out.write( getIndent( node ) + node.getClass().getSimpleName() + " " + getText( node ) + " " + dumpUserData( node ) + " " + node.getRegion() + "\n" ); } catch( IOException ex ) { @@ -137,6 +137,15 @@ public class TreeDumpVisitor implements IAstVisitor return null; } + private String getText( AstNode node ) + { + if( node instanceof Identifier ) + { + return "\"" + node.getText() + "\""; + } + return ""; + } + private String dumpUserData( AstNode node ) { StringBuilder buf = new StringBuilder(); diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 9ed6dd86..43a0cdad 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -66,10 +66,11 @@ import jsyntaxpane.DefaultSyntaxKit; import com.google.common.collect.Lists; import cuchaz.enigma.Constants; +import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; -import cuchaz.enigma.analysis.FieldCallsTreeNode; -import cuchaz.enigma.analysis.MethodCallsTreeNode; +import cuchaz.enigma.analysis.FieldReferenceTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; +import cuchaz.enigma.analysis.ReferenceTreeNode; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; @@ -191,7 +192,7 @@ public class Gui String selected = m_obfClasses.getSelectedValue(); if( selected != null ) { - m_controller.openEntry( new ClassEntry( selected ) ); + m_controller.openDeclaration( new ClassEntry( selected ) ); } } } @@ -217,7 +218,7 @@ public class Gui String selected = m_deobfClasses.getSelectedValue(); if( selected != null ) { - m_controller.openEntry( new ClassEntry( selected ) ); + m_controller.openDeclaration( new ClassEntry( selected ) ); } } } @@ -268,11 +269,11 @@ public class Gui break; case KeyEvent.VK_N: - openEntry(); + openDeclaration(); break; case KeyEvent.VK_P: - m_controller.openPreviousEntry(); + m_controller.openPreviousLocation(); break; case KeyEvent.VK_C: @@ -341,7 +342,7 @@ public class Gui @Override public void actionPerformed( ActionEvent event ) { - openEntry(); + openDeclaration(); } } ); menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_N, 0 ) ); @@ -356,7 +357,7 @@ public class Gui @Override public void actionPerformed( ActionEvent event ) { - m_controller.openPreviousEntry(); + m_controller.openPreviousLocation(); } } ); menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_P, 0 ) ); @@ -385,14 +386,14 @@ public class Gui Object node = path.getLastPathComponent(); if( node instanceof ClassInheritanceTreeNode ) { - m_controller.openEntry( new ClassEntry( ((ClassInheritanceTreeNode)node).getObfClassName() ) ); + m_controller.openDeclaration( new ClassEntry( ((ClassInheritanceTreeNode)node).getObfClassName() ) ); } else if( node instanceof MethodInheritanceTreeNode ) { MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode)node; if( methodNode.isImplemented() ) { - m_controller.openEntry( methodNode.getMethodEntry() ); + m_controller.openDeclaration( methodNode.getMethodEntry() ); } } } @@ -407,6 +408,7 @@ public class Gui m_callsTree.setModel( null ); m_callsTree.addMouseListener( new MouseAdapter( ) { + @SuppressWarnings( "unchecked" ) @Override public void mouseClicked( MouseEvent event ) { @@ -420,9 +422,17 @@ public class Gui } Object node = path.getLastPathComponent(); - if( node instanceof MethodCallsTreeNode ) + if( node instanceof ReferenceTreeNode ) { - m_controller.openEntry( ((MethodCallsTreeNode)node).getEntry() ); + ReferenceTreeNode referenceNode = ((ReferenceTreeNode)node); + if( referenceNode.getReference() != null ) + { + m_controller.openReference( referenceNode.getReference() ); + } + else + { + m_controller.openDeclaration( referenceNode.getEntry() ); + } } } } @@ -862,7 +872,7 @@ public class Gui m_showInheritanceMenu.setEnabled( isClassEntry || isMethodEntry || isConstructorEntry ); m_showCallsMenu.setEnabled( isFieldEntry || isMethodEntry || isConstructorEntry ); m_openEntryMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ); - m_openPreviousMenu.setEnabled( m_controller.hasPreviousEntry() ); + m_openPreviousMenu.setEnabled( m_controller.hasPreviousLocation() ); } private void startRename( ) @@ -977,17 +987,17 @@ public class Gui if( m_selectedEntryPair.obf instanceof FieldEntry ) { - FieldCallsTreeNode node = m_controller.getFieldCalls( (FieldEntry)m_selectedEntryPair.obf ); + FieldReferenceTreeNode node = m_controller.getFieldReferences( (FieldEntry)m_selectedEntryPair.obf ); m_callsTree.setModel( new DefaultTreeModel( node ) ); } else if( m_selectedEntryPair.obf instanceof MethodEntry ) { - MethodCallsTreeNode node = m_controller.getMethodCalls( (MethodEntry)m_selectedEntryPair.obf ); + BehaviorReferenceTreeNode node = m_controller.getMethodReferences( (MethodEntry)m_selectedEntryPair.obf ); m_callsTree.setModel( new DefaultTreeModel( node ) ); } else if( m_selectedEntryPair.obf instanceof ConstructorEntry ) { - MethodCallsTreeNode node = m_controller.getMethodCalls( (ConstructorEntry)m_selectedEntryPair.obf ); + BehaviorReferenceTreeNode node = m_controller.getMethodReferences( (ConstructorEntry)m_selectedEntryPair.obf ); m_callsTree.setModel( new DefaultTreeModel( node ) ); } @@ -1009,13 +1019,13 @@ public class Gui return new TreePath( nodes.toArray() ); } - private void openEntry( ) + private void openDeclaration( ) { if( m_selectedEntryPair == null ) { return; } - m_controller.openEntry( m_selectedEntryPair.obf ); + m_controller.openDeclaration( m_selectedEntryPair.obf ); } private void close( ) diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index f305e341..f80bec7e 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -20,14 +20,15 @@ import java.util.Stack; import com.google.common.collect.Lists; import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; -import cuchaz.enigma.analysis.FieldCallsTreeNode; -import cuchaz.enigma.analysis.MethodCallsTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.FieldReferenceTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.FieldEntry; @@ -44,7 +45,7 @@ public class GuiController private SourceIndex m_index; private ClassEntry m_currentClass; private boolean m_isDirty; - private Stack m_entryStack; + private Stack m_locationStack; // TODO: make a location class, can be either Entry or EntryReference public GuiController( Gui gui ) { @@ -53,7 +54,7 @@ public class GuiController m_index = null; m_currentClass = null; m_isDirty = false; - m_entryStack = new Stack(); + m_locationStack = new Stack(); } public boolean isDirty( ) @@ -157,9 +158,9 @@ public class GuiController return MethodInheritanceTreeNode.findNode( rootNode, obfMethodEntry ); } - public FieldCallsTreeNode getFieldCalls( FieldEntry obfFieldEntry ) + public FieldReferenceTreeNode getFieldReferences( FieldEntry obfFieldEntry ) { - FieldCallsTreeNode rootNode = new FieldCallsTreeNode( + FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode( m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), obfFieldEntry ); @@ -167,27 +168,12 @@ public class GuiController return rootNode; } - public MethodCallsTreeNode getMethodCalls( Entry obfEntry ) + public BehaviorReferenceTreeNode getMethodReferences( BehaviorEntry obfEntry ) { - MethodCallsTreeNode rootNode; - if( obfEntry instanceof MethodEntry ) - { - rootNode = new MethodCallsTreeNode( - m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), - (MethodEntry)obfEntry - ); - } - else if( obfEntry instanceof ConstructorEntry ) - { - rootNode = new MethodCallsTreeNode( - m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), - (ConstructorEntry)obfEntry - ); - } - else - { - throw new IllegalArgumentException( "entry must be a MethodEntry or a ConstructorEntry!" ); - } + BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode( + m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + obfEntry + ); rootNode.load( m_deobfuscator.getJarIndex(), true ); return rootNode; } @@ -200,7 +186,7 @@ public class GuiController refreshCurrentClass( obfEntry ); } - public void openEntry( Entry entry ) + public void openDeclaration( Entry entry ) { // go to the entry Entry obfEntry = m_deobfuscator.obfuscateEntry( entry ); @@ -214,25 +200,32 @@ public class GuiController m_gui.showToken( m_index.getDeclarationToken( m_deobfuscator.deobfuscateEntry( obfEntry ) ) ); } - if( m_entryStack.isEmpty() || !m_entryStack.peek().equals( obfEntry ) ) + if( m_locationStack.isEmpty() || !m_locationStack.peek().equals( obfEntry ) ) { // update the stack - m_entryStack.push( obfEntry ); + m_locationStack.push( obfEntry ); } } - public void openPreviousEntry( ) + public void openReference( EntryReference reference ) + { + // TODO: find out how to load the n-th reference in a caller + // TEMP: just go to the caller for now + openDeclaration( reference.caller ); + } + + public void openPreviousLocation( ) { - if( hasPreviousEntry() ) + if( hasPreviousLocation() ) { - m_entryStack.pop(); - openEntry( m_entryStack.peek() ); + m_locationStack.pop(); + openDeclaration( m_locationStack.peek() ); } } - public boolean hasPreviousEntry( ) + public boolean hasPreviousLocation( ) { - return m_entryStack.size() > 1; + return m_locationStack.size() > 1; } private void refreshClasses( ) diff --git a/src/cuchaz/enigma/mapping/BehaviorEntry.java b/src/cuchaz/enigma/mapping/BehaviorEntry.java new file mode 100644 index 00000000..99fdd28d --- /dev/null +++ b/src/cuchaz/enigma/mapping/BehaviorEntry.java @@ -0,0 +1,6 @@ +package cuchaz.enigma.mapping; + +public interface BehaviorEntry extends Entry +{ + public String getSignature(); +} diff --git a/src/cuchaz/enigma/mapping/ConstructorEntry.java b/src/cuchaz/enigma/mapping/ConstructorEntry.java index e0fa7cf6..0f7dab68 100644 --- a/src/cuchaz/enigma/mapping/ConstructorEntry.java +++ b/src/cuchaz/enigma/mapping/ConstructorEntry.java @@ -14,7 +14,7 @@ import java.io.Serializable; import cuchaz.enigma.Util; -public class ConstructorEntry implements Entry, Serializable +public class ConstructorEntry implements BehaviorEntry, Serializable { private static final long serialVersionUID = -868346075317366758L; @@ -54,6 +54,7 @@ public class ConstructorEntry implements Entry, Serializable return m_classEntry.getName(); } + @Override public String getSignature( ) { return m_signature; diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java index b4b9c9bf..a311e636 100644 --- a/src/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -14,7 +14,7 @@ import java.io.Serializable; import cuchaz.enigma.Util; -public class MethodEntry implements Entry, Serializable +public class MethodEntry implements BehaviorEntry, Serializable { private static final long serialVersionUID = 4770915224467247458L; @@ -68,6 +68,7 @@ public class MethodEntry implements Entry, Serializable return m_name; } + @Override public String getSignature( ) { return m_signature; diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index fc41f945..a1230db3 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -172,6 +172,19 @@ public class Translator ); } + public BehaviorEntry translateEntry( BehaviorEntry in ) + { + if( in instanceof MethodEntry ) + { + return translateEntry( (MethodEntry)in ); + } + else if( in instanceof ConstructorEntry ) + { + return translateEntry( (ConstructorEntry)in ); + } + throw new Error( "Wrong entry type!" ); + } + public String translate( ArgumentEntry in ) { for( String className : getSelfAndAncestors( in.getClassName() ) ) -- cgit v1.2.3 From a85529d1ce6ec533809575ec84572de855464b36 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 20 Aug 2014 01:21:52 -0400 Subject: finished reference navigation system. Still need to debug and polish it, but the basic idea seems to work. =) --- src/cuchaz/enigma/Deobfuscator.java | 105 ++-- src/cuchaz/enigma/Main.java | 1 - .../enigma/analysis/BehaviorReferenceTreeNode.java | 12 +- src/cuchaz/enigma/analysis/EntryReference.java | 89 ++- .../enigma/analysis/FieldReferenceTreeNode.java | 14 +- src/cuchaz/enigma/analysis/JarIndex.java | 43 +- src/cuchaz/enigma/analysis/ReferenceTreeNode.java | 6 +- src/cuchaz/enigma/analysis/SourceIndex.java | 51 +- .../analysis/SourceIndexBehaviorVisitor.java | 142 +++++ .../enigma/analysis/SourceIndexClassVisitor.java | 114 ++++ src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 600 --------------------- src/cuchaz/enigma/analysis/Token.java | 6 + src/cuchaz/enigma/gui/Gui.java | 158 +++--- src/cuchaz/enigma/gui/GuiController.java | 106 ++-- src/cuchaz/enigma/mapping/Translator.java | 28 + 15 files changed, 633 insertions(+), 842 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java delete mode 100644 src/cuchaz/enigma/analysis/SourceIndexVisitor.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 293e1c2f..34e6033b 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -26,6 +26,7 @@ import com.strobel.decompiler.languages.java.ast.AstBuilder; import com.strobel.decompiler.languages.java.ast.CompilationUnit; import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; +import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; @@ -169,98 +170,92 @@ public class Deobfuscator SourceIndex index = new SourceIndex( buf.toString() ); root.acceptVisitor( new SourceIndexVisitor(), index ); - return index; - } - - // NOTE: these methods are a bit messy... oh well - - public void rename( Entry obfEntry, String newName ) - { - if( obfEntry instanceof ClassEntry ) + /* DEBUG + for( Token token : index.referenceTokens() ) { - m_renamer.setClassName( (ClassEntry)obfEntry, newName ); - } - else if( obfEntry instanceof FieldEntry ) - { - m_renamer.setFieldName( (FieldEntry)obfEntry, newName ); - } - else if( obfEntry instanceof MethodEntry ) - { - m_renamer.setMethodTreeName( (MethodEntry)obfEntry, newName ); - } - else if( obfEntry instanceof ConstructorEntry ) - { - m_renamer.setClassName( obfEntry.getClassEntry(), newName ); - } - else if( obfEntry instanceof ArgumentEntry ) - { - m_renamer.setArgumentName( (ArgumentEntry)obfEntry, newName ); - } - else - { - throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); + EntryReference reference = index.getDeobfReference( token ); + System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); } + */ - // clear the type loader cache - m_typeLoader.clearCache(); + return index; } public Entry obfuscateEntry( Entry deobfEntry ) { - Translator translator = getTranslator( TranslationDirection.Obfuscating ); - if( deobfEntry instanceof ClassEntry ) - { - return translator.translateEntry( (ClassEntry)deobfEntry ); - } - else if( deobfEntry instanceof FieldEntry ) - { - return translator.translateEntry( (FieldEntry)deobfEntry ); - } - else if( deobfEntry instanceof MethodEntry ) + if( deobfEntry == null ) { - return translator.translateEntry( (MethodEntry)deobfEntry ); + return null; } - else if( deobfEntry instanceof ConstructorEntry ) + return getTranslator( TranslationDirection.Obfuscating ).translateEntry( deobfEntry ); + } + + public Entry deobfuscateEntry( Entry obfEntry ) + { + if( obfEntry == null ) { - return translator.translateEntry( (ConstructorEntry)deobfEntry ); + return null; } - else if( deobfEntry instanceof ArgumentEntry ) + return getTranslator( TranslationDirection.Deobfuscating ).translateEntry( obfEntry ); + } + + public EntryReference obfuscateReference( EntryReference deobfReference ) + { + if( deobfReference == null ) { - return translator.translateEntry( (ArgumentEntry)deobfEntry ); + return null; } - else + return new EntryReference( + obfuscateEntry( deobfReference.entry ), + obfuscateEntry( deobfReference.context ), + deobfReference.pos + ); + } + + public EntryReference deobfuscateReference( EntryReference obfReference ) + { + if( obfReference == null ) { - throw new Error( "Unknown entry type: " + deobfEntry.getClass().getName() ); + return null; } + return new EntryReference( + deobfuscateEntry( obfReference.entry ), + deobfuscateEntry( obfReference.context ), + obfReference.pos + ); } - public Entry deobfuscateEntry( Entry obfEntry ) + // NOTE: these methods are a bit messy... oh well + + public void rename( Entry obfEntry, String newName ) { - Translator translator = getTranslator( TranslationDirection.Deobfuscating ); if( obfEntry instanceof ClassEntry ) { - return translator.translateEntry( (ClassEntry)obfEntry ); + m_renamer.setClassName( (ClassEntry)obfEntry, newName ); } else if( obfEntry instanceof FieldEntry ) { - return translator.translateEntry( (FieldEntry)obfEntry ); + m_renamer.setFieldName( (FieldEntry)obfEntry, newName ); } else if( obfEntry instanceof MethodEntry ) { - return translator.translateEntry( (MethodEntry)obfEntry ); + m_renamer.setMethodTreeName( (MethodEntry)obfEntry, newName ); } else if( obfEntry instanceof ConstructorEntry ) { - return translator.translateEntry( (ConstructorEntry)obfEntry ); + m_renamer.setClassName( obfEntry.getClassEntry(), newName ); } else if( obfEntry instanceof ArgumentEntry ) { - return translator.translateEntry( (ArgumentEntry)obfEntry ); + m_renamer.setArgumentName( (ArgumentEntry)obfEntry, newName ); } else { throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); } + + // clear the type loader cache + m_typeLoader.clearCache(); } public boolean hasMapping( Entry obfEntry ) diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index 20d73c29..7d38bd62 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -13,7 +13,6 @@ package cuchaz.enigma; import java.io.File; import cuchaz.enigma.gui.Gui; -import cuchaz.enigma.mapping.ClassEntry; public class Main { diff --git a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java index 158aad71..0f7e7f71 100644 --- a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java @@ -21,13 +21,13 @@ import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.Translator; -public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode +public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { private static final long serialVersionUID = -3658163700783307520L; private Translator m_deobfuscatingTranslator; private BehaviorEntry m_entry; - private EntryReference m_reference; + private EntryReference m_reference; public BehaviorReferenceTreeNode( Translator deobfuscatingTranslator, BehaviorEntry entry ) { @@ -36,7 +36,7 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements m_reference = null; } - public BehaviorReferenceTreeNode( Translator deobfuscatingTranslator, EntryReference reference ) + public BehaviorReferenceTreeNode( Translator deobfuscatingTranslator, EntryReference reference ) { m_deobfuscatingTranslator = deobfuscatingTranslator; m_entry = reference.entry; @@ -50,7 +50,7 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements } @Override - public EntryReference getReference( ) + public EntryReference getReference( ) { return m_reference; } @@ -60,7 +60,7 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements { if( m_reference != null ) { - return m_deobfuscatingTranslator.translateEntry( m_reference.caller ).toString(); + return m_deobfuscatingTranslator.translateEntry( m_reference.context ).toString(); } return m_deobfuscatingTranslator.translateEntry( m_entry ).toString(); } @@ -68,7 +68,7 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements public void load( JarIndex index, boolean recurse ) { // get all the child nodes - for( EntryReference reference : index.getBehaviorReferences( m_entry ) ) + for( EntryReference reference : index.getBehaviorReferences( m_entry ) ) { add( new BehaviorReferenceTreeNode( m_deobfuscatingTranslator, reference ) ); } diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java index f462210a..869e08c1 100644 --- a/src/cuchaz/enigma/analysis/EntryReference.java +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -10,19 +10,96 @@ ******************************************************************************/ package cuchaz.enigma.analysis; -import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.Util; +import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; -public class EntryReference +public class EntryReference { - public T entry; - public BehaviorEntry caller; + public E entry; + public C context; public int pos; - public EntryReference( T entry, BehaviorEntry caller, int pos ) + public EntryReference( E entry ) { + this( entry, null, -1 ); + } + + public EntryReference( E entry, C context, int pos ) + { + if( entry == null ) + { + throw new IllegalArgumentException( "Entry cannot be null!" ); + } + this.entry = entry; - this.caller = caller; + this.context = context; this.pos = pos; } + + public ClassEntry getClassEntry( ) + { + if( context != null ) + { + return context.getClassEntry(); + } + return entry.getClassEntry(); + } + + @Override + public int hashCode( ) + { + if( context != null ) + { + return Util.combineHashesOrdered( entry.hashCode(), context.hashCode(), Integer.valueOf( pos ).hashCode() ); + } + return entry.hashCode(); + } + + @Override + public boolean equals( Object other ) + { + if( other instanceof EntryReference ) + { + return equals( (EntryReference)other ); + } + return false; + } + + public boolean equals( EntryReference other ) + { + // check entry first + boolean isEntrySame = entry.equals( other.entry ); + if( !isEntrySame ) + { + return false; + } + + // check caller + if( context == null && other.context == null ) + { + return true; + } + else if( context != null && other.context != null ) + { + return context.equals( other.context ) && pos == other.pos; + } + return false; + } + + @Override + public String toString( ) + { + StringBuilder buf = new StringBuilder(); + buf.append( entry ); + if( context != null ) + { + buf.append( " called from " ); + buf.append( context ); + buf.append( " (" ); + buf.append( pos ); + buf.append( ")" ); + } + return buf.toString(); + } } diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java index dd552d68..645e6821 100644 --- a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -16,13 +16,13 @@ import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Translator; -public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode +public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { private static final long serialVersionUID = -7934108091928699835L; private Translator m_deobfuscatingTranslator; private FieldEntry m_entry; - private EntryReference m_reference; + private EntryReference m_reference; public FieldReferenceTreeNode( Translator deobfuscatingTranslator, FieldEntry entry ) { @@ -31,7 +31,7 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re m_reference = null; } - private FieldReferenceTreeNode( Translator deobfuscatingTranslator, EntryReference reference ) + private FieldReferenceTreeNode( Translator deobfuscatingTranslator, EntryReference reference ) { m_deobfuscatingTranslator = deobfuscatingTranslator; m_entry = reference.entry; @@ -45,7 +45,7 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re } @Override - public EntryReference getReference( ) + public EntryReference getReference( ) { return m_reference; } @@ -55,7 +55,7 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re { if( m_reference != null ) { - return m_deobfuscatingTranslator.translateEntry( m_reference.caller ).toString(); + return m_deobfuscatingTranslator.translateEntry( m_reference.context ).toString(); } return m_deobfuscatingTranslator.translateEntry( m_entry ).toString(); } @@ -65,14 +65,14 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re // get all the child nodes if( m_reference == null ) { - for( EntryReference reference : index.getFieldReferences( m_entry ) ) + for( EntryReference reference : index.getFieldReferences( m_entry ) ) { add( new FieldReferenceTreeNode( m_deobfuscatingTranslator, reference ) ); } } else { - for( EntryReference reference : index.getBehaviorReferences( m_reference.caller ) ) + for( EntryReference reference : index.getBehaviorReferences( m_reference.context ) ) { add( new BehaviorReferenceTreeNode( m_deobfuscatingTranslator, reference ) ); } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index f1c29c5b..f408d930 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -56,8 +56,8 @@ public class JarIndex private Set m_obfClassNames; private Ancestries m_ancestries; private Multimap m_methodImplementations; - private Multimap> m_behaviorReferences; - private Multimap> m_fieldReferences; + private Multimap> m_behaviorReferences; + private Multimap> m_fieldReferences; private Multimap m_innerClasses; private Map m_outerClasses; private Set m_anonymousClasses; @@ -108,12 +108,12 @@ public class JarIndex m_anonymousClasses.add( innerClassName ); // DEBUG - System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); + //System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); } else { // DEBUG - System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); + //System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); } } } @@ -172,7 +172,7 @@ public class JarIndex call.getSignature() ); callNumbers.add( calledMethodEntry ); - EntryReference reference = new EntryReference( + EntryReference reference = new EntryReference( calledMethodEntry, thisEntry, callNumbers.count( calledMethodEntry ) - 1 @@ -189,7 +189,7 @@ public class JarIndex call.getFieldName() ); callNumbers.add( calledFieldEntry ); - EntryReference reference = new EntryReference( + EntryReference reference = new EntryReference( calledFieldEntry, thisEntry, callNumbers.count( calledFieldEntry ) - 1 @@ -209,7 +209,7 @@ public class JarIndex call.getSignature() ); callNumbers.add( calledConstructorEntry ); - EntryReference reference = new EntryReference( + EntryReference reference = new EntryReference( calledConstructorEntry, thisEntry, callNumbers.count( calledConstructorEntry ) - 1 @@ -226,7 +226,7 @@ public class JarIndex call.getSignature() ); callNumbers.add( calledConstructorEntry ); - EntryReference reference = new EntryReference( + EntryReference reference = new EntryReference( calledConstructorEntry, thisEntry, callNumbers.count( calledConstructorEntry ) - 1 @@ -261,9 +261,9 @@ public class JarIndex new ClassEntry( Descriptor.toJvmName( c.getName() ) ), constructor.getMethodInfo().getDescriptor() ); - for( EntryReference reference : getBehaviorReferences( constructorEntry ) ) + for( EntryReference reference : getBehaviorReferences( constructorEntry ) ) { - callerClasses.add( reference.caller.getClassEntry() ); + callerClasses.add( reference.context.getClassEntry() ); } // is this called by exactly one class? @@ -496,26 +496,14 @@ public class JarIndex return rootNode; } - @SuppressWarnings( "unchecked" ) - public Collection> getFieldReferences( FieldEntry fieldEntry ) + public Collection> getFieldReferences( FieldEntry fieldEntry ) { - List> references = Lists.newArrayList(); - for( EntryReference reference : m_fieldReferences.get( fieldEntry ) ) - { - references.add( (EntryReference)reference ); - } - return references; + return m_fieldReferences.get( fieldEntry ); } - @SuppressWarnings( "unchecked" ) - public Collection> getBehaviorReferences( BehaviorEntry behaviorEntry ) + public Collection> getBehaviorReferences( BehaviorEntry behaviorEntry ) { - List> references = Lists.newArrayList(); - for( EntryReference reference : m_behaviorReferences.get( behaviorEntry ) ) - { - references.add( (EntryReference)reference ); - } - return references; + return m_behaviorReferences.get( behaviorEntry ); } public Collection getInnerClasses( String obfOuterClassName ) @@ -613,8 +601,9 @@ public class JarIndex } else if( thing instanceof EntryReference ) { - EntryReference reference = (EntryReference)thing; + EntryReference reference = (EntryReference)thing; reference.entry = renameThing( renames, reference.entry ); + reference.context = renameThing( renames, reference.context ); return thing; } else diff --git a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java index 08ae39d9..e0a0a747 100644 --- a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java @@ -12,8 +12,8 @@ package cuchaz.enigma.analysis; import cuchaz.enigma.mapping.Entry; -public interface ReferenceTreeNode +public interface ReferenceTreeNode { - T getEntry(); - EntryReference getReference(); + E getEntry(); + EntryReference getReference(); } diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 645a71da..bf890e30 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -10,6 +10,7 @@ ******************************************************************************/ package cuchaz.enigma.analysis; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -24,15 +25,17 @@ import cuchaz.enigma.mapping.Entry; public class SourceIndex { private String m_source; - private TreeMap m_tokens; - private Map m_declarations; + private TreeMap> m_tokenToReference; + private HashMap,Token> m_referenceToToken; + private Map m_declarationToToken; private List m_lineOffsets; public SourceIndex( String source ) { m_source = source; - m_tokens = Maps.newTreeMap(); - m_declarations = Maps.newHashMap(); + m_tokenToReference = Maps.newTreeMap(); + m_referenceToToken = Maps.newHashMap(); + m_declarationToToken = Maps.newHashMap(); m_lineOffsets = Lists.newArrayList(); // count the lines @@ -87,12 +90,13 @@ public class SourceIndex return token; } - public void add( Identifier node, Entry deobfEntry ) + public void addReference( Identifier node, EntryReference deobfReference ) { Token token = getToken( node ); if( token != null ) { - m_tokens.put( token, deobfEntry ); + m_tokenToReference.put( token, deobfReference ); + m_referenceToToken.put( deobfReference, token ); } } @@ -101,19 +105,16 @@ public class SourceIndex Token token = getToken( node ); if( token != null ) { - m_tokens.put( token, deobfEntry ); - m_declarations.put( deobfEntry, token ); + EntryReference reference = new EntryReference( deobfEntry ); + m_tokenToReference.put( token, reference ); + m_referenceToToken.put( reference, token ); + m_declarationToToken.put( deobfEntry, token ); } } - public Token getToken( int pos ) + public Token getReferenceToken( int pos ) { - Map.Entry mapEntry = m_tokens.floorEntry( new Token( pos, pos ) ); - if( mapEntry == null ) - { - return null; - } - Token token = mapEntry.getKey(); + Token token = m_tokenToReference.floorKey( new Token( pos, pos ) ); if( token.contains( pos ) ) { return token; @@ -121,23 +122,33 @@ public class SourceIndex return null; } - public Entry getEntry( Token token ) + public Token getReferenceToken( EntryReference deobfReference ) + { + return m_referenceToToken.get( deobfReference ); + } + + public EntryReference getDeobfReference( Token token ) { if( token == null ) { return null; } - return m_tokens.get( token ); + return m_tokenToReference.get( token ); + } + + public Iterable referenceTokens( ) + { + return m_tokenToReference.keySet(); } - public Iterable tokens( ) + public Iterable declarationTokens( ) { - return m_tokens.keySet(); + return m_declarationToToken.values(); } public Token getDeclarationToken( Entry deobfEntry ) { - return m_declarations.get( deobfEntry ); + return m_declarationToToken.get( deobfEntry ); } private int toPos( int line, int col ) diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java new file mode 100644 index 00000000..d3386c51 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; +import com.strobel.assembler.metadata.MemberReference; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.ParameterDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.decompiler.languages.TextLocation; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.SimpleType; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class SourceIndexBehaviorVisitor extends SourceIndexVisitor +{ + private BehaviorEntry m_behaviorEntry; + private Multiset m_indices; + + public SourceIndexBehaviorVisitor( BehaviorEntry behaviorEntry ) + { + m_behaviorEntry = behaviorEntry; + m_indices = HashMultiset.create(); + } + + @Override + public Void visitMethodDeclaration( MethodDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitConstructorDeclaration( ConstructorDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitInvocationExpression( InvocationExpression node, SourceIndex index ) + { + MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); + ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); + MethodEntry methodEntry = new MethodEntry( classEntry, ref.getName(), ref.getSignature() ); + if( node.getTarget() instanceof MemberReferenceExpression ) + { + m_indices.add( methodEntry ); + index.addReference( + ((MemberReferenceExpression)node.getTarget()).getMemberNameToken(), + new EntryReference( methodEntry, m_behaviorEntry, m_indices.count( methodEntry ) ) + ); + } + + return recurse( node, index ); + } + + @Override + public Void visitMemberReferenceExpression( MemberReferenceExpression node, SourceIndex index ) + { + MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); + if( ref != null ) + { + ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); + FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); + m_indices.add( fieldEntry ); + index.addReference( + node.getMemberNameToken(), + new EntryReference( fieldEntry, m_behaviorEntry, m_indices.count( fieldEntry ) ) + ); + } + + return recurse( node, index ); + } + + @Override + public Void visitSimpleType( SimpleType node, SourceIndex index ) + { + TypeReference ref = node.getUserData( Keys.TYPE_REFERENCE ); + if( node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY ) + { + ClassEntry classEntry = new ClassEntry( ref.getInternalName() ); + m_indices.add( classEntry ); + index.addReference( + node.getIdentifierToken(), + new EntryReference( classEntry, m_behaviorEntry, m_indices.count( classEntry ) ) + ); + } + + return recurse( node, index ); + } + + @Override + public Void visitParameterDeclaration( ParameterDeclaration node, SourceIndex index ) + { + ParameterDefinition def = node.getUserData( Keys.PARAMETER_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + MethodDefinition methodDef = (MethodDefinition)def.getMethod(); + MethodEntry methodEntry = new MethodEntry( classEntry, methodDef.getName(), methodDef.getSignature() ); + ArgumentEntry argumentEntry = new ArgumentEntry( methodEntry, def.getPosition(), def.getName() ); + index.addDeclaration( node.getNameToken(), argumentEntry ); + + return recurse( node, index ); + } + + @Override + public Void visitIdentifierExpression( IdentifierExpression node, SourceIndex index ) + { + MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); + if( ref != null ) + { + ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); + FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); + m_indices.add( fieldEntry ); + index.addReference( + node.getIdentifierToken(), + new EntryReference( fieldEntry, m_behaviorEntry, m_indices.count( fieldEntry ) ) + ); + } + + return recurse( node, index ); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java new file mode 100644 index 00000000..2d4c0f57 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; +import com.strobel.assembler.metadata.FieldDefinition; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.decompiler.languages.TextLocation; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class SourceIndexClassVisitor extends SourceIndexVisitor +{ + private ClassEntry m_classEntry; + private Multiset m_indices; + + public SourceIndexClassVisitor( ClassEntry classEntry ) + { + m_classEntry = classEntry; + m_indices = HashMultiset.create(); + } + + @Override + public Void visitTypeDeclaration( TypeDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitSimpleType( SimpleType node, SourceIndex index ) + { + TypeReference ref = node.getUserData( Keys.TYPE_REFERENCE ); + if( node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY ) + { + ClassEntry classEntry = new ClassEntry( ref.getInternalName() ); + index.addReference( + node.getIdentifierToken(), + new EntryReference( classEntry, m_classEntry, m_indices.count( classEntry ) ) + ); + } + + return recurse( node, index ); + } + + @Override + public Void visitMethodDeclaration( MethodDeclaration node, SourceIndex index ) + { + MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + MethodEntry methodEntry = new MethodEntry( classEntry, def.getName(), def.getSignature() ); + index.addDeclaration( node.getNameToken(), methodEntry ); + //if( !def.getName().equals( "" ) ) + + return node.acceptVisitor( new SourceIndexBehaviorVisitor( methodEntry ), index ); + } + + @Override + public Void visitConstructorDeclaration( ConstructorDeclaration node, SourceIndex index ) + { + MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, def.getSignature() ); + index.addDeclaration( node.getNameToken(), constructorEntry ); + + return recurse( node, index ); + } + + @Override + public Void visitFieldDeclaration( FieldDeclaration node, SourceIndex index ) + { + FieldDefinition def = node.getUserData( Keys.FIELD_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + FieldEntry fieldEntry = new FieldEntry( classEntry, def.getName() ); + assert( node.getVariables().size() == 1 ); + VariableInitializer variable = node.getVariables().firstOrNullObject(); + index.addDeclaration( variable.getNameToken(), fieldEntry ); + + return recurse( node, index ); + } + + @Override + public Void visitEnumValueDeclaration( EnumValueDeclaration node, SourceIndex index ) + { + // treat enum declarations as field declarations + FieldDefinition def = node.getUserData( Keys.FIELD_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + FieldEntry fieldEntry = new FieldEntry( classEntry, def.getName() ); + index.addDeclaration( node.getNameToken(), fieldEntry ); + + return recurse( node, index ); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java deleted file mode 100644 index 841d1767..00000000 --- a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ /dev/null @@ -1,600 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import com.strobel.assembler.metadata.FieldDefinition; -import com.strobel.assembler.metadata.MemberReference; -import com.strobel.assembler.metadata.MethodDefinition; -import com.strobel.assembler.metadata.ParameterDefinition; -import com.strobel.assembler.metadata.TypeDefinition; -import com.strobel.assembler.metadata.TypeReference; -import com.strobel.decompiler.languages.TextLocation; -import com.strobel.decompiler.languages.java.ast.Annotation; -import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; -import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; -import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; -import com.strobel.decompiler.languages.java.ast.ArraySpecifier; -import com.strobel.decompiler.languages.java.ast.AssertStatement; -import com.strobel.decompiler.languages.java.ast.AssignmentExpression; -import com.strobel.decompiler.languages.java.ast.AstNode; -import com.strobel.decompiler.languages.java.ast.BinaryOperatorExpression; -import com.strobel.decompiler.languages.java.ast.BlockStatement; -import com.strobel.decompiler.languages.java.ast.BreakStatement; -import com.strobel.decompiler.languages.java.ast.CaseLabel; -import com.strobel.decompiler.languages.java.ast.CastExpression; -import com.strobel.decompiler.languages.java.ast.CatchClause; -import com.strobel.decompiler.languages.java.ast.ClassOfExpression; -import com.strobel.decompiler.languages.java.ast.Comment; -import com.strobel.decompiler.languages.java.ast.CompilationUnit; -import com.strobel.decompiler.languages.java.ast.ComposedType; -import com.strobel.decompiler.languages.java.ast.ConditionalExpression; -import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; -import com.strobel.decompiler.languages.java.ast.ContinueStatement; -import com.strobel.decompiler.languages.java.ast.DoWhileStatement; -import com.strobel.decompiler.languages.java.ast.EmptyStatement; -import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; -import com.strobel.decompiler.languages.java.ast.ExpressionStatement; -import com.strobel.decompiler.languages.java.ast.FieldDeclaration; -import com.strobel.decompiler.languages.java.ast.ForEachStatement; -import com.strobel.decompiler.languages.java.ast.ForStatement; -import com.strobel.decompiler.languages.java.ast.GotoStatement; -import com.strobel.decompiler.languages.java.ast.IAstVisitor; -import com.strobel.decompiler.languages.java.ast.Identifier; -import com.strobel.decompiler.languages.java.ast.IdentifierExpression; -import com.strobel.decompiler.languages.java.ast.IfElseStatement; -import com.strobel.decompiler.languages.java.ast.ImportDeclaration; -import com.strobel.decompiler.languages.java.ast.IndexerExpression; -import com.strobel.decompiler.languages.java.ast.InstanceInitializer; -import com.strobel.decompiler.languages.java.ast.InstanceOfExpression; -import com.strobel.decompiler.languages.java.ast.InvocationExpression; -import com.strobel.decompiler.languages.java.ast.JavaTokenNode; -import com.strobel.decompiler.languages.java.ast.Keys; -import com.strobel.decompiler.languages.java.ast.LabelStatement; -import com.strobel.decompiler.languages.java.ast.LabeledStatement; -import com.strobel.decompiler.languages.java.ast.LambdaExpression; -import com.strobel.decompiler.languages.java.ast.LocalTypeDeclarationStatement; -import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; -import com.strobel.decompiler.languages.java.ast.MethodDeclaration; -import com.strobel.decompiler.languages.java.ast.MethodGroupExpression; -import com.strobel.decompiler.languages.java.ast.NewLineNode; -import com.strobel.decompiler.languages.java.ast.NullReferenceExpression; -import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; -import com.strobel.decompiler.languages.java.ast.PackageDeclaration; -import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; -import com.strobel.decompiler.languages.java.ast.ParenthesizedExpression; -import com.strobel.decompiler.languages.java.ast.PrimitiveExpression; -import com.strobel.decompiler.languages.java.ast.ReturnStatement; -import com.strobel.decompiler.languages.java.ast.SimpleType; -import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; -import com.strobel.decompiler.languages.java.ast.SwitchSection; -import com.strobel.decompiler.languages.java.ast.SwitchStatement; -import com.strobel.decompiler.languages.java.ast.SynchronizedStatement; -import com.strobel.decompiler.languages.java.ast.TextNode; -import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; -import com.strobel.decompiler.languages.java.ast.ThrowStatement; -import com.strobel.decompiler.languages.java.ast.TryCatchStatement; -import com.strobel.decompiler.languages.java.ast.TypeDeclaration; -import com.strobel.decompiler.languages.java.ast.TypeParameterDeclaration; -import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; -import com.strobel.decompiler.languages.java.ast.UnaryOperatorExpression; -import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; -import com.strobel.decompiler.languages.java.ast.VariableInitializer; -import com.strobel.decompiler.languages.java.ast.WhileStatement; -import com.strobel.decompiler.languages.java.ast.WildcardType; -import com.strobel.decompiler.patterns.Pattern; - -import cuchaz.enigma.mapping.ArgumentEntry; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.ConstructorEntry; -import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.MethodEntry; - -public class SourceIndexVisitor implements IAstVisitor -{ - @Override - public Void visitInvocationExpression( InvocationExpression node, SourceIndex index ) - { - MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); - ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); - MethodEntry methodEntry = new MethodEntry( classEntry, ref.getName(), ref.getSignature() ); - if( node.getTarget() instanceof MemberReferenceExpression ) - { - index.add( ((MemberReferenceExpression)node.getTarget()).getMemberNameToken(), methodEntry ); - } - - return recurse( node, index ); - } - - @Override - public Void visitMemberReferenceExpression( MemberReferenceExpression node, SourceIndex index ) - { - MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); - if( ref != null ) - { - ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); - FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); - index.add( node.getMemberNameToken(), fieldEntry ); - } - - return recurse( node, index ); - } - - @Override - public Void visitSimpleType( SimpleType node, SourceIndex index ) - { - TypeReference ref = node.getUserData( Keys.TYPE_REFERENCE ); - if( node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY ) - { - index.add( node.getIdentifierToken(), new ClassEntry( ref.getInternalName() ) ); - } - - return recurse( node, index ); - } - - @Override - public Void visitMethodDeclaration( MethodDeclaration node, SourceIndex index ) - { - MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); - - // static initializers don't have identifier tokens - if( !def.getName().equals( "" ) ) - { - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - MethodEntry methodEntry = new MethodEntry( classEntry, def.getName(), def.getSignature() ); - index.addDeclaration( node.getNameToken(), methodEntry ); - } - - return recurse( node, index ); - } - - @Override - public Void visitConstructorDeclaration( ConstructorDeclaration node, SourceIndex index ) - { - MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, def.getSignature() ); - index.addDeclaration( node.getNameToken(), constructorEntry ); - - return recurse( node, index ); - } - - @Override - public Void visitParameterDeclaration( ParameterDeclaration node, SourceIndex index ) - { - ParameterDefinition def = node.getUserData( Keys.PARAMETER_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - MethodDefinition methodDef = (MethodDefinition)def.getMethod(); - MethodEntry methodEntry = new MethodEntry( classEntry, methodDef.getName(), methodDef.getSignature() ); - ArgumentEntry argumentEntry = new ArgumentEntry( methodEntry, def.getPosition(), def.getName() ); - index.addDeclaration( node.getNameToken(), argumentEntry ); - - return recurse( node, index ); - } - - @Override - public Void visitFieldDeclaration( FieldDeclaration node, SourceIndex index ) - { - FieldDefinition def = node.getUserData( Keys.FIELD_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - FieldEntry fieldEntry = new FieldEntry( classEntry, def.getName() ); - assert( node.getVariables().size() == 1 ); - VariableInitializer variable = node.getVariables().firstOrNullObject(); - index.addDeclaration( variable.getNameToken(), fieldEntry ); - - return recurse( node, index ); - } - - @Override - public Void visitTypeDeclaration( TypeDeclaration node, SourceIndex index ) - { - TypeDefinition def = node.getUserData( Keys.TYPE_DEFINITION ); - index.addDeclaration( node.getNameToken(), new ClassEntry( def.getInternalName() ) ); - - return recurse( node, index ); - } - - @Override - public Void visitEnumValueDeclaration( EnumValueDeclaration node, SourceIndex index ) - { - // treat enum declarations as field declarations - FieldDefinition def = node.getUserData( Keys.FIELD_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - FieldEntry fieldEntry = new FieldEntry( classEntry, def.getName() ); - index.addDeclaration( node.getNameToken(), fieldEntry ); - - return recurse( node, index ); - } - - @Override - public Void visitIdentifierExpression( IdentifierExpression node, SourceIndex index ) - { - MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); - if( ref != null ) - { - ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); - FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); - index.add( node.getIdentifierToken(), fieldEntry ); - } - - return recurse( node, index ); - } - - private Void recurse( AstNode node, SourceIndex index ) - { - for( final AstNode child : node.getChildren() ) - { - child.acceptVisitor( this, index ); - } - return null; - } - - // OVERRIDES WE DON'T CARE ABOUT - - @Override - public Void visitComment( Comment node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitPatternPlaceholder( AstNode node, Pattern pattern, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitTypeReference( TypeReferenceExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitJavaTokenNode( JavaTokenNode node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitIdentifier( Identifier node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitNullReferenceExpression( NullReferenceExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitThisReferenceExpression( ThisReferenceExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitSuperReferenceExpression( SuperReferenceExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitClassOfExpression( ClassOfExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitBlockStatement( BlockStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitExpressionStatement( ExpressionStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitBreakStatement( BreakStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitContinueStatement( ContinueStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitDoWhileStatement( DoWhileStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitEmptyStatement( EmptyStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitIfElseStatement( IfElseStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitLabelStatement( LabelStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitLabeledStatement( LabeledStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitReturnStatement( ReturnStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitSwitchStatement( SwitchStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitSwitchSection( SwitchSection node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitCaseLabel( CaseLabel node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitThrowStatement( ThrowStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitCatchClause( CatchClause node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitAnnotation( Annotation node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitNewLine( NewLineNode node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitVariableDeclaration( VariableDeclarationStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitVariableInitializer( VariableInitializer node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitText( TextNode node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitImportDeclaration( ImportDeclaration node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitInitializerBlock( InstanceInitializer node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitTypeParameterDeclaration( TypeParameterDeclaration node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitCompilationUnit( CompilationUnit node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitPackageDeclaration( PackageDeclaration node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitArraySpecifier( ArraySpecifier node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitComposedType( ComposedType node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitWhileStatement( WhileStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitPrimitiveExpression( PrimitiveExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitCastExpression( CastExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitBinaryOperatorExpression( BinaryOperatorExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitInstanceOfExpression( InstanceOfExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitIndexerExpression( IndexerExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitUnaryOperatorExpression( UnaryOperatorExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitConditionalExpression( ConditionalExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitArrayInitializerExpression( ArrayInitializerExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitObjectCreationExpression( ObjectCreationExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitArrayCreationExpression( ArrayCreationExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitAssignmentExpression( AssignmentExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitForStatement( ForStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitForEachStatement( ForEachStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitTryCatchStatement( TryCatchStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitGotoStatement( GotoStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitParenthesizedExpression( ParenthesizedExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitSynchronizedStatement( SynchronizedStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitAnonymousObjectCreationExpression( AnonymousObjectCreationExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitWildcardType( WildcardType node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitMethodGroupExpression( MethodGroupExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitAssertStatement( AssertStatement node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitLambdaExpression( LambdaExpression node, SourceIndex index ) - { - return recurse( node, index ); - } - - @Override - public Void visitLocalTypeDeclarationStatement( LocalTypeDeclarationStatement node, SourceIndex index ) - { - return recurse( node, index ); - } -} diff --git a/src/cuchaz/enigma/analysis/Token.java b/src/cuchaz/enigma/analysis/Token.java index 74023e32..d0f2b70b 100644 --- a/src/cuchaz/enigma/analysis/Token.java +++ b/src/cuchaz/enigma/analysis/Token.java @@ -46,4 +46,10 @@ public class Token implements Comparable { return start == other.start && end == other.end; } + + @Override + public String toString( ) + { + return String.format( "[%d,%d]", start, end ); + } } diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 43a0cdad..341c1493 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -68,6 +68,7 @@ import com.google.common.collect.Lists; import cuchaz.enigma.Constants; import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.FieldReferenceTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.analysis.ReferenceTreeNode; @@ -76,7 +77,6 @@ import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; -import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.IllegalNameException; import cuchaz.enigma.mapping.MappingParseException; @@ -160,7 +160,7 @@ public class Gui private JMenuItem m_showCallsMenu; // state - private EntryPair m_selectedEntryPair; + private EntryReference m_reference; private JFileChooser m_jarFileChooser; private JFileChooser m_mappingsFileChooser; @@ -192,6 +192,10 @@ public class Gui String selected = m_obfClasses.getSelectedValue(); if( selected != null ) { + if( m_reference != null ) + { + m_controller.savePreviousReference( m_reference ); + } m_controller.openDeclaration( new ClassEntry( selected ) ); } } @@ -218,6 +222,10 @@ public class Gui String selected = m_deobfClasses.getSelectedValue(); if( selected != null ) { + if( m_reference != null ) + { + m_controller.savePreviousReference( m_reference ); + } m_controller.openDeclaration( new ClassEntry( selected ) ); } } @@ -234,7 +242,7 @@ public class Gui m_infoPanel.setLayout( new GridLayout( 4, 1, 0, 0 ) ); m_infoPanel.setPreferredSize( new Dimension( 0, 100 ) ); m_infoPanel.setBorder( BorderFactory.createTitledBorder( "Identifier Info" ) ); - clearEntryPair(); + clearReference(); // init editor DefaultSyntaxKit.initKit(); @@ -273,7 +281,7 @@ public class Gui break; case KeyEvent.VK_P: - m_controller.openPreviousLocation(); + m_controller.openPreviousReference(); break; case KeyEvent.VK_C: @@ -357,7 +365,7 @@ public class Gui @Override public void actionPerformed( ActionEvent event ) { - m_controller.openPreviousLocation(); + m_controller.openPreviousReference(); } } ); menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_P, 0 ) ); @@ -386,6 +394,10 @@ public class Gui Object node = path.getLastPathComponent(); if( node instanceof ClassInheritanceTreeNode ) { + if( m_reference != null ) + { + m_controller.savePreviousReference( m_reference ); + } m_controller.openDeclaration( new ClassEntry( ((ClassInheritanceTreeNode)node).getObfClassName() ) ); } else if( node instanceof MethodInheritanceTreeNode ) @@ -393,6 +405,10 @@ public class Gui MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode)node; if( methodNode.isImplemented() ) { + if( m_reference != null ) + { + m_controller.savePreviousReference( m_reference ); + } m_controller.openDeclaration( methodNode.getMethodEntry() ); } } @@ -424,7 +440,11 @@ public class Gui Object node = path.getLastPathComponent(); if( node instanceof ReferenceTreeNode ) { - ReferenceTreeNode referenceNode = ((ReferenceTreeNode)node); + if( m_reference != null ) + { + m_controller.savePreviousReference( m_reference ); + } + ReferenceTreeNode referenceNode = ((ReferenceTreeNode)node); if( referenceNode.getReference() != null ) { m_controller.openReference( referenceNode.getReference() ); @@ -715,6 +735,10 @@ public class Gui public void showToken( Token token ) { + if( token == null ) + { + throw new IllegalArgumentException( "Token cannot be null!" ); + } m_editor.setCaretPosition( token.start ); m_editor.grabFocus(); } @@ -752,7 +776,7 @@ public class Gui } } - private void clearEntryPair( ) + private void clearReference( ) { m_infoPanel.removeAll(); JLabel label = new JLabel( "No identifier selected" ); @@ -763,76 +787,75 @@ public class Gui redraw(); } - @SuppressWarnings( "unchecked" ) - private void showEntryPair( EntryPair pair ) + private void showReference( EntryReference reference ) { - if( pair == null ) + if( reference == null ) { - clearEntryPair(); + clearReference(); return; } - m_selectedEntryPair = pair; + m_reference = reference; m_infoPanel.removeAll(); - if( pair.deobf instanceof ClassEntry ) + if( reference.entry instanceof ClassEntry ) { - showClassEntryPair( (EntryPair)pair ); + showClassEntry( (ClassEntry)m_reference.entry ); } - else if( pair.deobf instanceof FieldEntry ) + else if( m_reference.entry instanceof FieldEntry ) { - showFieldEntryPair( (EntryPair)pair ); + showFieldEntry( (FieldEntry)m_reference.entry ); } - else if( pair.deobf instanceof MethodEntry ) + else if( m_reference.entry instanceof MethodEntry ) { - showMethodEntryPair( (EntryPair)pair ); + showMethodEntry( (MethodEntry)m_reference.entry ); } - else if( pair.deobf instanceof ConstructorEntry ) + else if( m_reference.entry instanceof ConstructorEntry ) { - showConstructorEntryPair( (EntryPair)pair ); + showConstructorEntry( (ConstructorEntry)m_reference.entry ); } - else if( pair.deobf instanceof ArgumentEntry ) + else if( m_reference.entry instanceof ArgumentEntry ) { - showArgumentEntryPair( (EntryPair)pair ); + showArgumentEntry( (ArgumentEntry)m_reference.entry ); } else { - throw new Error( "Unknown entry type: " + pair.deobf.getClass().getName() ); + throw new Error( "Unknown entry type: " + m_reference.entry.getClass().getName() ); } redraw(); } - private void showClassEntryPair( EntryPair pair ) + private void showClassEntry( ClassEntry entry ) { - addNameValue( m_infoPanel, "Class", pair.deobf.getName() ); + addNameValue( m_infoPanel, "Class", entry.getName() ); } - private void showFieldEntryPair( EntryPair pair ) + private void showFieldEntry( FieldEntry entry ) { - addNameValue( m_infoPanel, "Field", pair.deobf.getName() ); - addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() ); + addNameValue( m_infoPanel, "Field", entry.getName() ); + addNameValue( m_infoPanel, "Class", entry.getClassEntry().getName() ); } - private void showMethodEntryPair( EntryPair pair ) + private void showMethodEntry( MethodEntry entry ) { - addNameValue( m_infoPanel, "Method", pair.deobf.getName() ); - addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() ); - addNameValue( m_infoPanel, "Signature", pair.deobf.getSignature() ); + addNameValue( m_infoPanel, "Method", entry.getName() ); + addNameValue( m_infoPanel, "Class", entry.getClassEntry().getName() ); + addNameValue( m_infoPanel, "Signature", entry.getSignature() ); } - private void showConstructorEntryPair( EntryPair pair ) + private void showConstructorEntry( ConstructorEntry entry ) { - addNameValue( m_infoPanel, "Constructor", pair.deobf.getClassEntry().getName() ); - addNameValue( m_infoPanel, "Signature", pair.deobf.getSignature() ); + addNameValue( m_infoPanel, "Constructor", entry.getClassEntry().getName() ); + addNameValue( m_infoPanel, "Signature", entry.getSignature() ); } - private void showArgumentEntryPair( EntryPair pair ) + private void showArgumentEntry( ArgumentEntry entry ) { - addNameValue( m_infoPanel, "Argument", pair.deobf.getName() ); - addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() ); - addNameValue( m_infoPanel, "Method", pair.deobf.getMethodEntry().getName() ); - addNameValue( m_infoPanel, "Index", Integer.toString( pair.deobf.getIndex() ) ); + addNameValue( m_infoPanel, "Argument", entry.getName() ); + addNameValue( m_infoPanel, "Class", entry.getClassEntry().getName() ); + addNameValue( m_infoPanel, "Method", entry.getMethodEntry().getName() ); + addNameValue( m_infoPanel, "Index", Integer.toString( entry.getIndex() ) ); } private void addNameValue( JPanel container, String name, String value ) @@ -853,19 +876,19 @@ public class Gui Token token = m_controller.getToken( pos ); boolean isToken = token != null; - m_selectedEntryPair = m_controller.getEntryPair( token ); - boolean isClassEntry = isToken && m_selectedEntryPair.obf instanceof ClassEntry; - boolean isFieldEntry = isToken && m_selectedEntryPair.obf instanceof FieldEntry; - boolean isMethodEntry = isToken && m_selectedEntryPair.obf instanceof MethodEntry; - boolean isConstructorEntry = isToken && m_selectedEntryPair.obf instanceof ConstructorEntry; + m_reference = m_controller.getDeobfReference( token ); + boolean isClassEntry = isToken && m_reference.entry instanceof ClassEntry; + boolean isFieldEntry = isToken && m_reference.entry instanceof FieldEntry; + boolean isMethodEntry = isToken && m_reference.entry instanceof MethodEntry; + boolean isConstructorEntry = isToken && m_reference.entry instanceof ConstructorEntry; if( isToken ) { - showEntryPair( m_selectedEntryPair ); + showReference( m_reference ); } else { - clearEntryPair(); + clearReference(); } m_renameMenu.setEnabled( isToken ); @@ -878,11 +901,11 @@ public class Gui private void startRename( ) { // get the class name - String className = m_selectedEntryPair.deobf.getName(); - int pos = className.lastIndexOf( '$' ); - if( pos >= 0 ) + ClassEntry classEntry = m_reference.entry.getClassEntry(); + String className = classEntry.getName(); + if( classEntry.isInnerClass() ) { - className = className.substring( pos + 1 ); + className = classEntry.getInnerClassName(); } // init the text box @@ -924,7 +947,7 @@ public class Gui { try { - m_controller.rename( m_selectedEntryPair.obf, newName ); + m_controller.rename( m_reference, newName ); } catch( IllegalNameException ex ) { @@ -937,7 +960,7 @@ public class Gui // abort the rename JPanel panel = (JPanel)m_infoPanel.getComponent( 0 ); panel.remove( panel.getComponentCount() - 1 ); - panel.add( unboldLabel( new JLabel( m_selectedEntryPair.deobf.getName(), JLabel.LEFT ) ) ); + panel.add( unboldLabel( new JLabel( m_reference.entry.getName(), JLabel.LEFT ) ) ); m_editor.grabFocus(); @@ -946,15 +969,15 @@ public class Gui private void showInheritance( ) { - if( m_selectedEntryPair == null ) + if( m_reference == null ) { return; } - if( m_selectedEntryPair.obf instanceof ClassEntry ) + if( m_reference.entry instanceof ClassEntry ) { // get the class inheritance - ClassInheritanceTreeNode classNode = m_controller.getClassInheritance( (ClassEntry)m_selectedEntryPair.obf ); + ClassInheritanceTreeNode classNode = m_controller.getClassInheritance( (ClassEntry)m_reference.entry ); // show the tree at the root TreePath path = getPathToRoot( classNode ); @@ -962,10 +985,10 @@ public class Gui m_inheritanceTree.expandPath( path ); m_inheritanceTree.setSelectionRow( m_inheritanceTree.getRowForPath( path ) ); } - else if( m_selectedEntryPair.obf instanceof MethodEntry ) + else if( m_reference.entry instanceof MethodEntry ) { // get the method inheritance - MethodInheritanceTreeNode classNode = m_controller.getMethodInheritance( (MethodEntry)m_selectedEntryPair.obf ); + MethodInheritanceTreeNode classNode = m_controller.getMethodInheritance( (MethodEntry)m_reference.entry ); // show the tree at the root TreePath path = getPathToRoot( classNode ); @@ -980,24 +1003,24 @@ public class Gui private void showCalls( ) { - if( m_selectedEntryPair == null ) + if( m_reference == null ) { return; } - if( m_selectedEntryPair.obf instanceof FieldEntry ) + if( m_reference.entry instanceof FieldEntry ) { - FieldReferenceTreeNode node = m_controller.getFieldReferences( (FieldEntry)m_selectedEntryPair.obf ); + FieldReferenceTreeNode node = m_controller.getFieldReferences( (FieldEntry)m_reference.entry ); m_callsTree.setModel( new DefaultTreeModel( node ) ); } - else if( m_selectedEntryPair.obf instanceof MethodEntry ) + else if( m_reference.entry instanceof MethodEntry ) { - BehaviorReferenceTreeNode node = m_controller.getMethodReferences( (MethodEntry)m_selectedEntryPair.obf ); + BehaviorReferenceTreeNode node = m_controller.getMethodReferences( (MethodEntry)m_reference.entry ); m_callsTree.setModel( new DefaultTreeModel( node ) ); } - else if( m_selectedEntryPair.obf instanceof ConstructorEntry ) + else if( m_reference.entry instanceof ConstructorEntry ) { - BehaviorReferenceTreeNode node = m_controller.getMethodReferences( (ConstructorEntry)m_selectedEntryPair.obf ); + BehaviorReferenceTreeNode node = m_controller.getMethodReferences( (ConstructorEntry)m_reference.entry ); m_callsTree.setModel( new DefaultTreeModel( node ) ); } @@ -1021,11 +1044,12 @@ public class Gui private void openDeclaration( ) { - if( m_selectedEntryPair == null ) + if( m_reference == null ) { return; } - m_controller.openDeclaration( m_selectedEntryPair.obf ); + m_controller.savePreviousReference( m_reference ); + m_controller.openDeclaration( m_reference.entry ); } private void close( ) diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index f80bec7e..dfa25576 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -14,10 +14,11 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.Deque; import java.util.List; -import java.util.Stack; import com.google.common.collect.Lists; +import com.google.common.collect.Queues; import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; @@ -30,7 +31,6 @@ import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; -import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.MappingsReader; @@ -43,18 +43,18 @@ public class GuiController private Deobfuscator m_deobfuscator; private Gui m_gui; private SourceIndex m_index; - private ClassEntry m_currentClass; + private ClassEntry m_currentObfClass; private boolean m_isDirty; - private Stack m_locationStack; // TODO: make a location class, can be either Entry or EntryReference + private Deque> m_referenceStack; // TODO: make a location class, can be either Entry or EntryReference public GuiController( Gui gui ) { m_gui = gui; m_deobfuscator = null; m_index = null; - m_currentClass = null; + m_currentObfClass = null; m_isDirty = false; - m_locationStack = new Stack(); + m_referenceStack = Queues.newArrayDeque(); } public boolean isDirty( ) @@ -112,22 +112,16 @@ public class GuiController return null; } - return m_index.getToken( pos ); + return m_index.getReferenceToken( pos ); } - public EntryPair getEntryPair( Token token ) + public EntryReference getDeobfReference( Token token ) { if( m_index == null ) { return null; } - - Entry deobfEntry = m_index.getEntry( token ); - if( deobfEntry == null ) - { - return null; - } - return new EntryPair( m_deobfuscator.obfuscateEntry( deobfEntry ), deobfEntry ); + return m_index.getDeobfReference( token ); } public boolean entryHasMapping( Entry deobfEntry ) @@ -178,54 +172,63 @@ public class GuiController return rootNode; } - public void rename( Entry obfEntry, String newName ) + public void rename( EntryReference deobfReference, String newName ) { - m_deobfuscator.rename( obfEntry, newName ); + EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); + m_deobfuscator.rename( obfReference.entry, newName ); m_isDirty = true; refreshClasses(); - refreshCurrentClass( obfEntry ); + refreshCurrentClass( obfReference ); } public void openDeclaration( Entry entry ) { - // go to the entry - Entry obfEntry = m_deobfuscator.obfuscateEntry( entry ); - if( m_currentClass == null || !m_currentClass.equals( obfEntry.getClassEntry() ) ) + if( entry == null ) { - m_currentClass = new ClassEntry( obfEntry.getClassEntry() ); - deobfuscate( m_currentClass, obfEntry ); + throw new IllegalArgumentException( "Entry cannot be null!" ); } - else + openReference( new EntryReference( entry ) ); + } + + public void openReference( EntryReference deobfReference ) + { + if( deobfReference == null ) { - m_gui.showToken( m_index.getDeclarationToken( m_deobfuscator.deobfuscateEntry( obfEntry ) ) ); + throw new IllegalArgumentException( "Reference cannot be null!" ); } - if( m_locationStack.isEmpty() || !m_locationStack.peek().equals( obfEntry ) ) + // get the reference target class + EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); + ClassEntry obfClassEntry = obfReference.getClassEntry().getOuterClassEntry(); + if( m_currentObfClass == null || !m_currentObfClass.equals( obfClassEntry ) ) + { + // deobfuscate the class, then navigate to the reference + m_currentObfClass = obfClassEntry; + deobfuscate( m_currentObfClass, obfReference ); + } + else { - // update the stack - m_locationStack.push( obfEntry ); + // the class file is already open, just navigate to the reference + m_gui.showToken( m_index.getReferenceToken( deobfReference ) ); } } - public void openReference( EntryReference reference ) + public void savePreviousReference( EntryReference deobfReference ) { - // TODO: find out how to load the n-th reference in a caller - // TEMP: just go to the caller for now - openDeclaration( reference.caller ); + m_referenceStack.push( m_deobfuscator.obfuscateReference( deobfReference ) ); } - public void openPreviousLocation( ) + public void openPreviousReference( ) { if( hasPreviousLocation() ) { - m_locationStack.pop(); - openDeclaration( m_locationStack.peek() ); + openReference( m_deobfuscator.deobfuscateReference( m_referenceStack.pop() ) ); } } public boolean hasPreviousLocation( ) { - return m_locationStack.size() > 1; + return !m_referenceStack.isEmpty(); } private void refreshClasses( ) @@ -242,15 +245,15 @@ public class GuiController refreshCurrentClass( null ); } - private void refreshCurrentClass( Entry obfEntryToShow ) + private void refreshCurrentClass( EntryReference obfReferenceToShow ) { - if( m_currentClass != null ) + if( m_currentObfClass != null ) { - deobfuscate( m_currentClass, obfEntryToShow ); + deobfuscate( m_currentObfClass, obfReferenceToShow ); } } - private void deobfuscate( final ClassEntry classEntry, final Entry obfEntryToShow ) + private void deobfuscate( final ClassEntry classEntry, final EntryReference obfReferenceToShow ) { m_gui.setSource( "(deobfuscating...)" ); @@ -263,29 +266,32 @@ public class GuiController // decompile,deobfuscate the bytecode m_index = m_deobfuscator.getSource( classEntry.getClassName() ); m_gui.setSource( m_index.getSource() ); - if( obfEntryToShow != null ) + if( obfReferenceToShow != null ) { - Entry deobfEntryToShow = m_deobfuscator.deobfuscateEntry( obfEntryToShow ); - Token token = m_index.getDeclarationToken( deobfEntryToShow ); + EntryReference deobfReferenceToShow = m_deobfuscator.deobfuscateReference( obfReferenceToShow ); + Token token = m_index.getReferenceToken( deobfReferenceToShow ); if( token == null ) { - // TEMP - System.out.println( "WARNING: can't find token for " + obfEntryToShow + " -> " + deobfEntryToShow ); + // DEBUG + System.out.println( "WARNING: can't find token for " + obfReferenceToShow + " -> " + deobfReferenceToShow ); + } + else + { + m_gui.showToken( token ); } - m_gui.showToken( token ); } // set the highlighted tokens List obfuscatedTokens = Lists.newArrayList(); List deobfuscatedTokens = Lists.newArrayList(); - for( Token token : m_index.tokens() ) + for( Token token : m_index.referenceTokens() ) { - Entry entry = m_index.getEntry( token ); - if( entryHasMapping( entry ) ) + EntryReference reference = m_index.getDeobfReference( token ); + if( entryHasMapping( reference.entry ) ) { deobfuscatedTokens.add( token ); } - else if( entryIsObfuscatedIdenfitier( entry ) ) + else if( entryIsObfuscatedIdenfitier( reference.entry ) ) { obfuscatedTokens.add( token ); } diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index a1230db3..e34c31bf 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -30,6 +30,34 @@ public class Translator m_ancestries = ancestries; } + public Entry translateEntry( Entry entry ) + { + if( entry instanceof ClassEntry ) + { + return translateEntry( (ClassEntry)entry ); + } + else if( entry instanceof FieldEntry ) + { + return translateEntry( (FieldEntry)entry ); + } + else if( entry instanceof MethodEntry ) + { + return translateEntry( (MethodEntry)entry ); + } + else if( entry instanceof ConstructorEntry ) + { + return translateEntry( (ConstructorEntry)entry ); + } + else if( entry instanceof ArgumentEntry ) + { + return translateEntry( (ArgumentEntry)entry ); + } + else + { + throw new Error( "Unknown entry type: " + entry.getClass().getName() ); + } + } + public String translateClass( String className ) { return translate( new ClassEntry( className ) ); -- cgit v1.2.3 From fbeb2b73ab51f3244454a63893fee6d847fd859d Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 20 Aug 2014 01:22:38 -0400 Subject: missed a file --- src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 524 +++++++++++++++++++++ 1 file changed, 524 insertions(+) create mode 100644 src/cuchaz/enigma/analysis/SourceIndexVisitor.java diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java new file mode 100644 index 00000000..4e98989e --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -0,0 +1,524 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.decompiler.languages.java.ast.Annotation; +import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; +import com.strobel.decompiler.languages.java.ast.ArraySpecifier; +import com.strobel.decompiler.languages.java.ast.AssertStatement; +import com.strobel.decompiler.languages.java.ast.AssignmentExpression; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.BinaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.BlockStatement; +import com.strobel.decompiler.languages.java.ast.BreakStatement; +import com.strobel.decompiler.languages.java.ast.CaseLabel; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.CatchClause; +import com.strobel.decompiler.languages.java.ast.ClassOfExpression; +import com.strobel.decompiler.languages.java.ast.Comment; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; +import com.strobel.decompiler.languages.java.ast.ComposedType; +import com.strobel.decompiler.languages.java.ast.ConditionalExpression; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.ContinueStatement; +import com.strobel.decompiler.languages.java.ast.DoWhileStatement; +import com.strobel.decompiler.languages.java.ast.EmptyStatement; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.ExpressionStatement; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.ForEachStatement; +import com.strobel.decompiler.languages.java.ast.ForStatement; +import com.strobel.decompiler.languages.java.ast.GotoStatement; +import com.strobel.decompiler.languages.java.ast.IAstVisitor; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.IfElseStatement; +import com.strobel.decompiler.languages.java.ast.ImportDeclaration; +import com.strobel.decompiler.languages.java.ast.IndexerExpression; +import com.strobel.decompiler.languages.java.ast.InstanceInitializer; +import com.strobel.decompiler.languages.java.ast.InstanceOfExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.JavaTokenNode; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.LabelStatement; +import com.strobel.decompiler.languages.java.ast.LabeledStatement; +import com.strobel.decompiler.languages.java.ast.LambdaExpression; +import com.strobel.decompiler.languages.java.ast.LocalTypeDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.MethodGroupExpression; +import com.strobel.decompiler.languages.java.ast.NewLineNode; +import com.strobel.decompiler.languages.java.ast.NullReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.PackageDeclaration; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.ParenthesizedExpression; +import com.strobel.decompiler.languages.java.ast.PrimitiveExpression; +import com.strobel.decompiler.languages.java.ast.ReturnStatement; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.SwitchSection; +import com.strobel.decompiler.languages.java.ast.SwitchStatement; +import com.strobel.decompiler.languages.java.ast.SynchronizedStatement; +import com.strobel.decompiler.languages.java.ast.TextNode; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThrowStatement; +import com.strobel.decompiler.languages.java.ast.TryCatchStatement; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; +import com.strobel.decompiler.languages.java.ast.UnaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; +import com.strobel.decompiler.languages.java.ast.WhileStatement; +import com.strobel.decompiler.languages.java.ast.WildcardType; +import com.strobel.decompiler.patterns.Pattern; + +import cuchaz.enigma.mapping.ClassEntry; + +public class SourceIndexVisitor implements IAstVisitor +{ + @Override + public Void visitTypeDeclaration( TypeDeclaration node, SourceIndex index ) + { + TypeDefinition def = node.getUserData( Keys.TYPE_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getInternalName() ); + index.addDeclaration( node.getNameToken(), classEntry ); + + return node.acceptVisitor( new SourceIndexClassVisitor( classEntry ), index ); + } + + protected Void recurse( AstNode node, SourceIndex index ) + { + for( final AstNode child : node.getChildren() ) + { + child.acceptVisitor( this, index ); + } + return null; + } + + @Override + public Void visitMethodDeclaration( MethodDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitConstructorDeclaration( ConstructorDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitFieldDeclaration( FieldDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitEnumValueDeclaration( EnumValueDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitParameterDeclaration( ParameterDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitInvocationExpression( InvocationExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitMemberReferenceExpression( MemberReferenceExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitSimpleType( SimpleType node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitIdentifierExpression( IdentifierExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitComment( Comment node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitPatternPlaceholder( AstNode node, Pattern pattern, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitTypeReference( TypeReferenceExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitJavaTokenNode( JavaTokenNode node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitIdentifier( Identifier node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitNullReferenceExpression( NullReferenceExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitThisReferenceExpression( ThisReferenceExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitSuperReferenceExpression( SuperReferenceExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitClassOfExpression( ClassOfExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitBlockStatement( BlockStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitExpressionStatement( ExpressionStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitBreakStatement( BreakStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitContinueStatement( ContinueStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitDoWhileStatement( DoWhileStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitEmptyStatement( EmptyStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitIfElseStatement( IfElseStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitLabelStatement( LabelStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitLabeledStatement( LabeledStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitReturnStatement( ReturnStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitSwitchStatement( SwitchStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitSwitchSection( SwitchSection node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitCaseLabel( CaseLabel node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitThrowStatement( ThrowStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitCatchClause( CatchClause node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitAnnotation( Annotation node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitNewLine( NewLineNode node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitVariableDeclaration( VariableDeclarationStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitVariableInitializer( VariableInitializer node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitText( TextNode node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitImportDeclaration( ImportDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitInitializerBlock( InstanceInitializer node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitTypeParameterDeclaration( TypeParameterDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitCompilationUnit( CompilationUnit node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitPackageDeclaration( PackageDeclaration node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitArraySpecifier( ArraySpecifier node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitComposedType( ComposedType node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitWhileStatement( WhileStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitPrimitiveExpression( PrimitiveExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitCastExpression( CastExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitBinaryOperatorExpression( BinaryOperatorExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitInstanceOfExpression( InstanceOfExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitIndexerExpression( IndexerExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitUnaryOperatorExpression( UnaryOperatorExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitConditionalExpression( ConditionalExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitArrayInitializerExpression( ArrayInitializerExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitObjectCreationExpression( ObjectCreationExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitArrayCreationExpression( ArrayCreationExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitAssignmentExpression( AssignmentExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitForStatement( ForStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitForEachStatement( ForEachStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitTryCatchStatement( TryCatchStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitGotoStatement( GotoStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitParenthesizedExpression( ParenthesizedExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitSynchronizedStatement( SynchronizedStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitAnonymousObjectCreationExpression( AnonymousObjectCreationExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitWildcardType( WildcardType node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitMethodGroupExpression( MethodGroupExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitAssertStatement( AssertStatement node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitLambdaExpression( LambdaExpression node, SourceIndex index ) + { + return recurse( node, index ); + } + + @Override + public Void visitLocalTypeDeclarationStatement( LocalTypeDeclarationStatement node, SourceIndex index ) + { + return recurse( node, index ); + } +} -- cgit v1.2.3 From bdb61cf849c67f7da6a0bb7c047b506d8462058f Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 20 Aug 2014 22:30:12 -0400 Subject: fixed recognition of inner class tokens fixed recognition of reference tokens in constructors --- src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 2d4c0f57..52570886 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -14,6 +14,7 @@ import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; import com.strobel.assembler.metadata.FieldDefinition; import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.assembler.metadata.TypeReference; import com.strobel.decompiler.languages.TextLocation; import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; @@ -45,6 +46,16 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor @Override public Void visitTypeDeclaration( TypeDeclaration node, SourceIndex index ) { + // is this this class, or a subtype? + TypeDefinition def = node.getUserData( Keys.TYPE_DEFINITION ); + ClassEntry classEntry = new ClassEntry( def.getInternalName() ); + if( !classEntry.equals( m_classEntry ) ) + { + // it's a sub-type, recurse + index.addDeclaration( node.getNameToken(), classEntry ); + return node.acceptVisitor( new SourceIndexClassVisitor( classEntry ), index ); + } + return recurse( node, index ); } @@ -84,7 +95,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, def.getSignature() ); index.addDeclaration( node.getNameToken(), constructorEntry ); - return recurse( node, index ); + return node.acceptVisitor( new SourceIndexBehaviorVisitor( constructorEntry ), index ); } @Override -- cgit v1.2.3 From 237b2ed2a6b6f537cdbdf9fc9c6d0c7743f34375 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 21 Aug 2014 01:10:37 -0400 Subject: fixed call graph searching added system to navigate multiple tokens for the same entry in a behavior --- src/cuchaz/enigma/Deobfuscator.java | 18 +++--- src/cuchaz/enigma/analysis/EntryReference.java | 13 ++-- src/cuchaz/enigma/analysis/JarIndex.java | 30 ++++----- src/cuchaz/enigma/analysis/SourceIndex.java | 37 +++++++++-- .../analysis/SourceIndexBehaviorVisitor.java | 16 ++--- .../enigma/analysis/SourceIndexClassVisitor.java | 23 ++++--- src/cuchaz/enigma/gui/Gui.java | 53 +++++++++++---- src/cuchaz/enigma/gui/GuiController.java | 75 ++++++++++++++-------- src/cuchaz/enigma/gui/ReadableToken.java | 38 +++++++++++ src/cuchaz/enigma/gui/TokenListCellRenderer.java | 40 ++++++++++++ src/cuchaz/enigma/mapping/ConstructorEntry.java | 54 +++++++++++++--- src/cuchaz/enigma/mapping/Translator.java | 28 +++++--- 12 files changed, 302 insertions(+), 123 deletions(-) create mode 100644 src/cuchaz/enigma/gui/ReadableToken.java create mode 100644 src/cuchaz/enigma/gui/TokenListCellRenderer.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 34e6033b..a5feaa9f 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -181,7 +181,7 @@ public class Deobfuscator return index; } - public Entry obfuscateEntry( Entry deobfEntry ) + public T obfuscateEntry( T deobfEntry ) { if( deobfEntry == null ) { @@ -190,7 +190,7 @@ public class Deobfuscator return getTranslator( TranslationDirection.Obfuscating ).translateEntry( deobfEntry ); } - public Entry deobfuscateEntry( Entry obfEntry ) + public T deobfuscateEntry( T obfEntry ) { if( obfEntry == null ) { @@ -199,29 +199,27 @@ public class Deobfuscator return getTranslator( TranslationDirection.Deobfuscating ).translateEntry( obfEntry ); } - public EntryReference obfuscateReference( EntryReference deobfReference ) + public EntryReference obfuscateReference( EntryReference deobfReference ) { if( deobfReference == null ) { return null; } - return new EntryReference( + return new EntryReference( obfuscateEntry( deobfReference.entry ), - obfuscateEntry( deobfReference.context ), - deobfReference.pos + obfuscateEntry( deobfReference.context ) ); } - public EntryReference deobfuscateReference( EntryReference obfReference ) + public EntryReference deobfuscateReference( EntryReference obfReference ) { if( obfReference == null ) { return null; } - return new EntryReference( + return new EntryReference( deobfuscateEntry( obfReference.entry ), - deobfuscateEntry( obfReference.context ), - obfReference.pos + deobfuscateEntry( obfReference.context ) ); } diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java index 869e08c1..768c1132 100644 --- a/src/cuchaz/enigma/analysis/EntryReference.java +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -18,14 +18,13 @@ public class EntryReference { public E entry; public C context; - public int pos; public EntryReference( E entry ) { - this( entry, null, -1 ); + this( entry, null ); } - public EntryReference( E entry, C context, int pos ) + public EntryReference( E entry, C context ) { if( entry == null ) { @@ -34,7 +33,6 @@ public class EntryReference this.entry = entry; this.context = context; - this.pos = pos; } public ClassEntry getClassEntry( ) @@ -51,7 +49,7 @@ public class EntryReference { if( context != null ) { - return Util.combineHashesOrdered( entry.hashCode(), context.hashCode(), Integer.valueOf( pos ).hashCode() ); + return Util.combineHashesOrdered( entry.hashCode(), context.hashCode() ); } return entry.hashCode(); } @@ -82,7 +80,7 @@ public class EntryReference } else if( context != null && other.context != null ) { - return context.equals( other.context ) && pos == other.pos; + return context.equals( other.context ); } return false; } @@ -96,9 +94,6 @@ public class EntryReference { buf.append( " called from " ); buf.append( context ); - buf.append( " (" ); - buf.append( pos ); - buf.append( ")" ); } return buf.toString(); } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index f408d930..315a2864 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -146,10 +146,15 @@ public class JarIndex } else if( behavior instanceof CtConstructor ) { - thisEntry = new ConstructorEntry( - new ClassEntry( className ), - behavior.getSignature() - ); + boolean isStatic = behavior.getName().equals( "" ); + if( isStatic ) + { + thisEntry = new ConstructorEntry( new ClassEntry( className ) ); + } + else + { + thisEntry = new ConstructorEntry( new ClassEntry( className ), behavior.getSignature() ); + } } else { @@ -159,7 +164,6 @@ public class JarIndex // index method calls try { - final Multiset callNumbers = HashMultiset.create(); behavior.instrument( new ExprEditor( ) { @Override @@ -171,11 +175,9 @@ public class JarIndex call.getMethodName(), call.getSignature() ); - callNumbers.add( calledMethodEntry ); EntryReference reference = new EntryReference( calledMethodEntry, - thisEntry, - callNumbers.count( calledMethodEntry ) - 1 + thisEntry ); m_behaviorReferences.put( calledMethodEntry, reference ); } @@ -188,11 +190,9 @@ public class JarIndex new ClassEntry( className ), call.getFieldName() ); - callNumbers.add( calledFieldEntry ); EntryReference reference = new EntryReference( calledFieldEntry, - thisEntry, - callNumbers.count( calledFieldEntry ) - 1 + thisEntry ); m_fieldReferences.put( calledFieldEntry, reference ); } @@ -208,11 +208,9 @@ public class JarIndex new ClassEntry( className ), call.getSignature() ); - callNumbers.add( calledConstructorEntry ); EntryReference reference = new EntryReference( calledConstructorEntry, - thisEntry, - callNumbers.count( calledConstructorEntry ) - 1 + thisEntry ); m_behaviorReferences.put( calledConstructorEntry, reference ); } @@ -225,11 +223,9 @@ public class JarIndex new ClassEntry( className ), call.getSignature() ); - callNumbers.add( calledConstructorEntry ); EntryReference reference = new EntryReference( calledConstructorEntry, - thisEntry, - callNumbers.count( calledConstructorEntry ) - 1 + thisEntry ); m_behaviorReferences.put( calledConstructorEntry, reference ); } diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index bf890e30..960ec36f 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -10,13 +10,15 @@ ******************************************************************************/ package cuchaz.enigma.analysis; -import java.util.HashMap; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.TreeMap; +import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; import com.strobel.decompiler.languages.Region; import com.strobel.decompiler.languages.java.ast.Identifier; @@ -26,7 +28,7 @@ public class SourceIndex { private String m_source; private TreeMap> m_tokenToReference; - private HashMap,Token> m_referenceToToken; + private Multimap,Token> m_referenceToTokens; private Map m_declarationToToken; private List m_lineOffsets; @@ -34,7 +36,7 @@ public class SourceIndex { m_source = source; m_tokenToReference = Maps.newTreeMap(); - m_referenceToToken = Maps.newHashMap(); + m_referenceToTokens = HashMultimap.create(); m_declarationToToken = Maps.newHashMap(); m_lineOffsets = Lists.newArrayList(); @@ -96,7 +98,7 @@ public class SourceIndex if( token != null ) { m_tokenToReference.put( token, deobfReference ); - m_referenceToToken.put( deobfReference, token ); + m_referenceToTokens.put( deobfReference, token ); } } @@ -107,7 +109,7 @@ public class SourceIndex { EntryReference reference = new EntryReference( deobfEntry ); m_tokenToReference.put( token, reference ); - m_referenceToToken.put( reference, token ); + m_referenceToTokens.put( reference, token ); m_declarationToToken.put( deobfEntry, token ); } } @@ -122,9 +124,9 @@ public class SourceIndex return null; } - public Token getReferenceToken( EntryReference deobfReference ) + public Collection getReferenceTokens( EntryReference deobfReference ) { - return m_referenceToToken.get( deobfReference ); + return m_referenceToTokens.get( deobfReference ); } public EntryReference getDeobfReference( Token token ) @@ -151,6 +153,27 @@ public class SourceIndex return m_declarationToToken.get( deobfEntry ); } + public int getLineNumber( int pos ) + { + // line number is 1-based + int line = 0; + for( Integer offset : m_lineOffsets ) + { + if( offset > pos ) + { + break; + } + line++; + } + return line; + } + + public int getColumnNumber( int pos ) + { + // column number is 1-based + return pos - m_lineOffsets.get( getLineNumber( pos ) - 1 ) + 1; + } + private int toPos( int line, int col ) { // line and col are 1-based diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index d3386c51..a9438588 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -10,8 +10,6 @@ ******************************************************************************/ package cuchaz.enigma.analysis; -import com.google.common.collect.HashMultiset; -import com.google.common.collect.Multiset; import com.strobel.assembler.metadata.MemberReference; import com.strobel.assembler.metadata.MethodDefinition; import com.strobel.assembler.metadata.ParameterDefinition; @@ -36,12 +34,10 @@ import cuchaz.enigma.mapping.MethodEntry; public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { private BehaviorEntry m_behaviorEntry; - private Multiset m_indices; public SourceIndexBehaviorVisitor( BehaviorEntry behaviorEntry ) { m_behaviorEntry = behaviorEntry; - m_indices = HashMultiset.create(); } @Override @@ -64,10 +60,9 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor MethodEntry methodEntry = new MethodEntry( classEntry, ref.getName(), ref.getSignature() ); if( node.getTarget() instanceof MemberReferenceExpression ) { - m_indices.add( methodEntry ); index.addReference( ((MemberReferenceExpression)node.getTarget()).getMemberNameToken(), - new EntryReference( methodEntry, m_behaviorEntry, m_indices.count( methodEntry ) ) + new EntryReference( methodEntry, m_behaviorEntry ) ); } @@ -82,10 +77,9 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); - m_indices.add( fieldEntry ); index.addReference( node.getMemberNameToken(), - new EntryReference( fieldEntry, m_behaviorEntry, m_indices.count( fieldEntry ) ) + new EntryReference( fieldEntry, m_behaviorEntry ) ); } @@ -99,10 +93,9 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor if( node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY ) { ClassEntry classEntry = new ClassEntry( ref.getInternalName() ); - m_indices.add( classEntry ); index.addReference( node.getIdentifierToken(), - new EntryReference( classEntry, m_behaviorEntry, m_indices.count( classEntry ) ) + new EntryReference( classEntry, m_behaviorEntry ) ); } @@ -130,10 +123,9 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); - m_indices.add( fieldEntry ); index.addReference( node.getIdentifierToken(), - new EntryReference( fieldEntry, m_behaviorEntry, m_indices.count( fieldEntry ) ) + new EntryReference( fieldEntry, m_behaviorEntry ) ); } diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 52570886..a1c82711 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -10,8 +10,6 @@ ******************************************************************************/ package cuchaz.enigma.analysis; -import com.google.common.collect.HashMultiset; -import com.google.common.collect.Multiset; import com.strobel.assembler.metadata.FieldDefinition; import com.strobel.assembler.metadata.MethodDefinition; import com.strobel.assembler.metadata.TypeDefinition; @@ -26,6 +24,7 @@ import com.strobel.decompiler.languages.java.ast.SimpleType; import com.strobel.decompiler.languages.java.ast.TypeDeclaration; import com.strobel.decompiler.languages.java.ast.VariableInitializer; +import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; @@ -35,12 +34,10 @@ import cuchaz.enigma.mapping.MethodEntry; public class SourceIndexClassVisitor extends SourceIndexVisitor { private ClassEntry m_classEntry; - private Multiset m_indices; public SourceIndexClassVisitor( ClassEntry classEntry ) { m_classEntry = classEntry; - m_indices = HashMultiset.create(); } @Override @@ -68,7 +65,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor ClassEntry classEntry = new ClassEntry( ref.getInternalName() ); index.addReference( node.getIdentifierToken(), - new EntryReference( classEntry, m_classEntry, m_indices.count( classEntry ) ) + new EntryReference( classEntry, m_classEntry ) ); } @@ -80,11 +77,17 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - MethodEntry methodEntry = new MethodEntry( classEntry, def.getName(), def.getSignature() ); - index.addDeclaration( node.getNameToken(), methodEntry ); - //if( !def.getName().equals( "" ) ) - - return node.acceptVisitor( new SourceIndexBehaviorVisitor( methodEntry ), index ); + BehaviorEntry behaviorEntry; + if( def.getName().equals( "" ) ) + { + behaviorEntry = new ConstructorEntry( classEntry ); + } + else + { + behaviorEntry = new MethodEntry( classEntry, def.getName(), def.getSignature() ); + } + index.addDeclaration( node.getNameToken(), behaviorEntry ); + return node.acceptVisitor( new SourceIndexBehaviorVisitor( behaviorEntry ), index ); } @Override diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 341c1493..a5471a74 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -28,6 +28,7 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -145,6 +146,7 @@ public class Gui private BoxHighlightPainter m_deobfuscatedHighlightPainter; private JTree m_inheritanceTree; private JTree m_callsTree; + private JList m_tokens; private JTabbedPane m_tabs; // dynamic menu items @@ -457,9 +459,29 @@ public class Gui } } } ); - JPanel callPanel = new JPanel(); - callPanel.setLayout( new BorderLayout() ); - callPanel.add( new JScrollPane( m_callsTree ) ); + m_tokens = new JList(); + m_tokens.setCellRenderer( new TokenListCellRenderer( m_controller ) ); + m_tokens.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + m_tokens.setLayoutOrientation( JList.VERTICAL ); + m_tokens.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent event ) + { + if( event.getClickCount() == 2 ) + { + Token selected = m_tokens.getSelectedValue(); + if( selected != null ) + { + showToken( selected ); + } + } + } + } ); + m_tokens.setPreferredSize( new Dimension( 0, 200 ) ); + JSplitPane callPanel = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, new JScrollPane( m_callsTree ), new JScrollPane( m_tokens ) ); + callPanel.setResizeWeight( 1 ); // let the top side take all the slack + callPanel.resetToPreferredSizes(); // layout controls JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); @@ -743,6 +765,21 @@ public class Gui m_editor.grabFocus(); } + public void showTokens( Collection tokens ) + { + Vector sortedTokens = new Vector( tokens ); + Collections.sort( sortedTokens ); + if( sortedTokens.size() > 1 ) + { + // sort the tokens and update the tokens panel + m_tokens.setListData( sortedTokens ); + m_tokens.setSelectedIndex( 0 ); + } + + // show the first token + showToken( sortedTokens.get( 0 ) ); + } + public void setHighlightedTokens( Iterable obfuscatedTokens, Iterable deobfuscatedTokens ) { // remove any old highlighters @@ -900,17 +937,9 @@ public class Gui private void startRename( ) { - // get the class name - ClassEntry classEntry = m_reference.entry.getClassEntry(); - String className = classEntry.getName(); - if( classEntry.isInnerClass() ) - { - className = classEntry.getInnerClassName(); - } - // init the text box final JTextField text = new JTextField(); - text.setText( className ); + text.setText( m_reference.entry.getName() ); text.setPreferredSize( new Dimension( 360, text.getPreferredSize().height ) ); text.addKeyListener( new KeyAdapter( ) { diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index dfa25576..a35db056 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -14,6 +14,7 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.Collection; import java.util.Deque; import java.util.List; @@ -45,7 +46,7 @@ public class GuiController private SourceIndex m_index; private ClassEntry m_currentObfClass; private boolean m_isDirty; - private Deque> m_referenceStack; // TODO: make a location class, can be either Entry or EntryReference + private Deque> m_referenceStack; public GuiController( Gui gui ) { @@ -111,7 +112,6 @@ public class GuiController { return null; } - return m_index.getReferenceToken( pos ); } @@ -124,6 +124,19 @@ public class GuiController return m_index.getDeobfReference( token ); } + public ReadableToken getReadableToken( Token token ) + { + if( m_index == null ) + { + return null; + } + return new ReadableToken( + m_index.getLineNumber( token.start ), + m_index.getColumnNumber( token.start ), + m_index.getColumnNumber( token.end ) + ); + } + public boolean entryHasMapping( Entry deobfEntry ) { return m_deobfuscator.hasMapping( m_deobfuscator.obfuscateEntry( deobfEntry ) ); @@ -134,8 +147,9 @@ public class GuiController return m_deobfuscator.isObfuscatedIdentifier( m_deobfuscator.obfuscateEntry( deobfEntry ) ); } - public ClassInheritanceTreeNode getClassInheritance( ClassEntry obfClassEntry ) + public ClassInheritanceTreeNode getClassInheritance( ClassEntry deobfClassEntry ) { + ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry( deobfClassEntry ); ClassInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getClassInheritance( m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), obfClassEntry @@ -143,8 +157,9 @@ public class GuiController return ClassInheritanceTreeNode.findNode( rootNode, obfClassEntry ); } - public MethodInheritanceTreeNode getMethodInheritance( MethodEntry obfMethodEntry ) + public MethodInheritanceTreeNode getMethodInheritance( MethodEntry deobfMethodEntry ) { + MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry( deobfMethodEntry ); MethodInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodInheritance( m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), obfMethodEntry @@ -152,8 +167,9 @@ public class GuiController return MethodInheritanceTreeNode.findNode( rootNode, obfMethodEntry ); } - public FieldReferenceTreeNode getFieldReferences( FieldEntry obfFieldEntry ) + public FieldReferenceTreeNode getFieldReferences( FieldEntry deobfFieldEntry ) { + FieldEntry obfFieldEntry = m_deobfuscator.obfuscateEntry( deobfFieldEntry ); FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode( m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), obfFieldEntry @@ -162,11 +178,12 @@ public class GuiController return rootNode; } - public BehaviorReferenceTreeNode getMethodReferences( BehaviorEntry obfEntry ) + public BehaviorReferenceTreeNode getMethodReferences( BehaviorEntry deobfBehaviorEntry ) { + BehaviorEntry obfBehaviorEntry = m_deobfuscator.obfuscateEntry( deobfBehaviorEntry ); BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode( m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), - obfEntry + obfBehaviorEntry ); rootNode.load( m_deobfuscator.getJarIndex(), true ); return rootNode; @@ -181,13 +198,13 @@ public class GuiController refreshCurrentClass( obfReference ); } - public void openDeclaration( Entry entry ) + public void openDeclaration( Entry deobfEntry ) { - if( entry == null ) + if( deobfEntry == null ) { throw new IllegalArgumentException( "Entry cannot be null!" ); } - openReference( new EntryReference( entry ) ); + openReference( new EntryReference( deobfEntry ) ); } public void openReference( EntryReference deobfReference ) @@ -208,8 +225,22 @@ public class GuiController } else { - // the class file is already open, just navigate to the reference - m_gui.showToken( m_index.getReferenceToken( deobfReference ) ); + showReference( obfReference ); + } + } + + private void showReference( EntryReference obfReference ) + { + EntryReference deobfReference = m_deobfuscator.deobfuscateReference( obfReference ); + Collection tokens = m_index.getReferenceTokens( deobfReference ); + if( tokens.isEmpty() ) + { + // DEBUG + System.err.println( String.format( "WARNING: no tokens found for %s in %s", deobfReference, m_currentObfClass ) ); + } + else + { + m_gui.showTokens( tokens ); } } @@ -245,15 +276,15 @@ public class GuiController refreshCurrentClass( null ); } - private void refreshCurrentClass( EntryReference obfReferenceToShow ) + private void refreshCurrentClass( EntryReference obfReference ) { if( m_currentObfClass != null ) { - deobfuscate( m_currentObfClass, obfReferenceToShow ); + deobfuscate( m_currentObfClass, obfReference ); } } - private void deobfuscate( final ClassEntry classEntry, final EntryReference obfReferenceToShow ) + private void deobfuscate( final ClassEntry classEntry, final EntryReference obfReference ) { m_gui.setSource( "(deobfuscating...)" ); @@ -266,19 +297,9 @@ public class GuiController // decompile,deobfuscate the bytecode m_index = m_deobfuscator.getSource( classEntry.getClassName() ); m_gui.setSource( m_index.getSource() ); - if( obfReferenceToShow != null ) + if( obfReference != null ) { - EntryReference deobfReferenceToShow = m_deobfuscator.deobfuscateReference( obfReferenceToShow ); - Token token = m_index.getReferenceToken( deobfReferenceToShow ); - if( token == null ) - { - // DEBUG - System.out.println( "WARNING: can't find token for " + obfReferenceToShow + " -> " + deobfReferenceToShow ); - } - else - { - m_gui.showToken( token ); - } + showReference( obfReference ); } // set the highlighted tokens diff --git a/src/cuchaz/enigma/gui/ReadableToken.java b/src/cuchaz/enigma/gui/ReadableToken.java new file mode 100644 index 00000000..3f430453 --- /dev/null +++ b/src/cuchaz/enigma/gui/ReadableToken.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +public class ReadableToken +{ + public int line; + public int startColumn; + public int endColumn; + + public ReadableToken( int line, int startColumn, int endColumn ) + { + this.line = line; + this.startColumn = startColumn; + this.endColumn = endColumn; + } + + @Override + public String toString( ) + { + StringBuilder buf = new StringBuilder(); + buf.append( "line " ); + buf.append( line ); + buf.append( " columns " ); + buf.append( startColumn ); + buf.append( "-" ); + buf.append( endColumn ); + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/gui/TokenListCellRenderer.java b/src/cuchaz/enigma/gui/TokenListCellRenderer.java new file mode 100644 index 00000000..9247c066 --- /dev/null +++ b/src/cuchaz/enigma/gui/TokenListCellRenderer.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Component; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +import cuchaz.enigma.analysis.Token; + +public class TokenListCellRenderer implements ListCellRenderer +{ + private GuiController m_controller; + private DefaultListCellRenderer m_defaultRenderer; + + public TokenListCellRenderer( GuiController controller ) + { + m_controller = controller; + m_defaultRenderer = new DefaultListCellRenderer(); + } + + @Override + public Component getListCellRendererComponent( JList list, Token token, int index, boolean isSelected, boolean hasFocus ) + { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, token, index, isSelected, hasFocus ); + label.setText( m_controller.getReadableToken( token ).toString() ); + return label; + } +} diff --git a/src/cuchaz/enigma/mapping/ConstructorEntry.java b/src/cuchaz/enigma/mapping/ConstructorEntry.java index 0f7dab68..ad029e1c 100644 --- a/src/cuchaz/enigma/mapping/ConstructorEntry.java +++ b/src/cuchaz/enigma/mapping/ConstructorEntry.java @@ -21,16 +21,17 @@ public class ConstructorEntry implements BehaviorEntry, Serializable private ClassEntry m_classEntry; private String m_signature; + public ConstructorEntry( ClassEntry classEntry ) + { + this( classEntry, null ); + } + public ConstructorEntry( ClassEntry classEntry, String signature ) { if( classEntry == null ) { throw new IllegalArgumentException( "Class cannot be null!" ); } - if( signature == null ) - { - throw new IllegalArgumentException( "Method signature cannot be null!" ); - } m_classEntry = classEntry; m_signature = signature; @@ -47,11 +48,20 @@ public class ConstructorEntry implements BehaviorEntry, Serializable { return m_classEntry; } - + @Override public String getName( ) { - return m_classEntry.getName(); + if( isStatic() ) + { + return ""; + } + return ""; + } + + public boolean isStatic( ) + { + return m_signature == null; } @Override @@ -69,7 +79,14 @@ public class ConstructorEntry implements BehaviorEntry, Serializable @Override public int hashCode( ) { - return Util.combineHashesOrdered( m_classEntry, m_signature ); + if( isStatic() ) + { + return Util.combineHashesOrdered( m_classEntry ); + } + else + { + return Util.combineHashesOrdered( m_classEntry, m_signature ); + } } @Override @@ -84,12 +101,31 @@ public class ConstructorEntry implements BehaviorEntry, Serializable public boolean equals( ConstructorEntry other ) { - return m_classEntry.equals( other.m_classEntry ) && m_signature.equals( other.m_signature ); + if( isStatic() != other.isStatic() ) + { + return false; + } + + if( isStatic() ) + { + return m_classEntry.equals( other.m_classEntry ); + } + else + { + return m_classEntry.equals( other.m_classEntry ) && m_signature.equals( other.m_signature ); + } } @Override public String toString( ) { - return m_classEntry.getName() + m_signature; + if( isStatic() ) + { + return m_classEntry.getName() + "." + getName(); + } + else + { + return m_classEntry.getName() + "." + getName() + m_signature; + } } } diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index e34c31bf..a671c275 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -30,27 +30,28 @@ public class Translator m_ancestries = ancestries; } - public Entry translateEntry( Entry entry ) + @SuppressWarnings( "unchecked" ) + public T translateEntry( T entry ) { if( entry instanceof ClassEntry ) { - return translateEntry( (ClassEntry)entry ); + return (T)translateEntry( (ClassEntry)entry ); } else if( entry instanceof FieldEntry ) { - return translateEntry( (FieldEntry)entry ); + return (T)translateEntry( (FieldEntry)entry ); } else if( entry instanceof MethodEntry ) { - return translateEntry( (MethodEntry)entry ); + return (T)translateEntry( (MethodEntry)entry ); } else if( entry instanceof ConstructorEntry ) { - return translateEntry( (ConstructorEntry)entry ); + return (T)translateEntry( (ConstructorEntry)entry ); } else if( entry instanceof ArgumentEntry ) { - return translateEntry( (ArgumentEntry)entry ); + return (T)translateEntry( (ArgumentEntry)entry ); } else { @@ -194,10 +195,17 @@ public class Translator public ConstructorEntry translateEntry( ConstructorEntry in ) { - return new ConstructorEntry( - translateEntry( in.getClassEntry() ), - translateSignature( in.getSignature() ) - ); + if( in.isStatic() ) + { + return new ConstructorEntry( translateEntry( in.getClassEntry() ) ); + } + else + { + return new ConstructorEntry( + translateEntry( in.getClassEntry() ), + translateSignature( in.getSignature() ) + ); + } } public BehaviorEntry translateEntry( BehaviorEntry in ) -- cgit v1.2.3 From 79e8b7ea7ed27d65582d1c399a78e540d0f42895 Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 22 Aug 2014 00:37:31 -0400 Subject: fixed constructor references in call graph searches clear reference list when opening single reference (instead of list of references) --- src/cuchaz/enigma/Deobfuscator.java | 3 +-- src/cuchaz/enigma/analysis/BridgeFixer.java | 1 - .../enigma/analysis/SourceIndexBehaviorVisitor.java | 21 ++++++++++++++++++++- src/cuchaz/enigma/gui/Gui.java | 5 +++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index a5feaa9f..38f7af56 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -30,7 +30,6 @@ import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; -import cuchaz.enigma.analysis.TreeDumpVisitor; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; @@ -163,7 +162,7 @@ public class Deobfuscator StringWriter buf = new StringWriter(); root.acceptVisitor( new InsertParenthesesVisitor(), null ); // DEBUG - root.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); + //root.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); root.acceptVisitor( new JavaOutputVisitor( new PlainTextOutput( buf ), m_settings ), null ); // build the source index diff --git a/src/cuchaz/enigma/analysis/BridgeFixer.java b/src/cuchaz/enigma/analysis/BridgeFixer.java index ee90f513..f13f68f4 100644 --- a/src/cuchaz/enigma/analysis/BridgeFixer.java +++ b/src/cuchaz/enigma/analysis/BridgeFixer.java @@ -86,7 +86,6 @@ public class BridgeFixer catch( NotFoundException ex ) { // can't find the type? not a bridge method - ex.printStackTrace( System.err ); return null; } } diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index a9438588..ab505528 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -21,12 +21,14 @@ import com.strobel.decompiler.languages.java.ast.InvocationExpression; import com.strobel.decompiler.languages.java.ast.Keys; import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; import com.strobel.decompiler.languages.java.ast.SimpleType; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; @@ -57,9 +59,9 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); - MethodEntry methodEntry = new MethodEntry( classEntry, ref.getName(), ref.getSignature() ); if( node.getTarget() instanceof MemberReferenceExpression ) { + MethodEntry methodEntry = new MethodEntry( classEntry, ref.getName(), ref.getSignature() ); index.addReference( ((MemberReferenceExpression)node.getTarget()).getMemberNameToken(), new EntryReference( methodEntry, m_behaviorEntry ) @@ -131,4 +133,21 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor return recurse( node, index ); } + + @Override + public Void visitObjectCreationExpression( ObjectCreationExpression node, SourceIndex index ) + { + MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); + ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); + ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, ref.getSignature() ); + if( node.getType() instanceof SimpleType ) + { + index.addReference( + ((SimpleType)node.getType()).getIdentifierToken(), + new EntryReference( constructorEntry, m_behaviorEntry ) + ); + } + + return recurse( node, index ); + } } diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index a5471a74..cd0fac76 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -479,6 +479,7 @@ public class Gui } } ); m_tokens.setPreferredSize( new Dimension( 0, 200 ) ); + m_tokens.setMinimumSize( new Dimension( 0, 200 ) ); JSplitPane callPanel = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, new JScrollPane( m_callsTree ), new JScrollPane( m_tokens ) ); callPanel.setResizeWeight( 1 ); // let the top side take all the slack callPanel.resetToPreferredSizes(); @@ -775,6 +776,10 @@ public class Gui m_tokens.setListData( sortedTokens ); m_tokens.setSelectedIndex( 0 ); } + else + { + m_tokens.setListData( new Vector() ); + } // show the first token showToken( sortedTokens.get( 0 ) ); -- cgit v1.2.3 From 32b7ea70ff20d3584f8021e598141c20c2200398 Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 22 Aug 2014 01:25:52 -0400 Subject: added show token effects --- src/cuchaz/enigma/analysis/SourceIndex.java | 2 +- src/cuchaz/enigma/gui/BoxHighlightPainter.java | 23 +++++--- src/cuchaz/enigma/gui/Gui.java | 61 ++++++++++++++++++++-- .../enigma/gui/SelectionHighlightPainter.java | 35 +++++++++++++ 4 files changed, 109 insertions(+), 12 deletions(-) create mode 100644 src/cuchaz/enigma/gui/SelectionHighlightPainter.java diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 960ec36f..1a5a80d6 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -117,7 +117,7 @@ public class SourceIndex public Token getReferenceToken( int pos ) { Token token = m_tokenToReference.floorKey( new Token( pos, pos ) ); - if( token.contains( pos ) ) + if( token != null && token.contains( pos ) ) { return token; } diff --git a/src/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/cuchaz/enigma/gui/BoxHighlightPainter.java index b9474ff8..2c118340 100644 --- a/src/cuchaz/enigma/gui/BoxHighlightPainter.java +++ b/src/cuchaz/enigma/gui/BoxHighlightPainter.java @@ -32,11 +32,24 @@ public abstract class BoxHighlightPainter implements Highlighter.HighlightPainte @Override public void paint( Graphics g, int start, int end, Shape shape, JTextComponent text ) + { + Rectangle bounds = getBounds( text, start, end ); + + // fill the area + g.setColor( m_fillColor ); + g.fillRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); + + // draw a box around the area + g.setColor( m_borderColor ); + g.drawRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); + } + + protected static Rectangle getBounds( JTextComponent text, int start, int end ) { try { // determine the bounds of the text - Rectangle bounds = text.getUI().modelToView( text, start ).union( text.getUI().modelToView( text, end ) ); + Rectangle bounds = text.modelToView( start ).union( text.modelToView( end ) ); // adjust the box so it looks nice bounds.x -= 2; @@ -44,13 +57,7 @@ public abstract class BoxHighlightPainter implements Highlighter.HighlightPainte bounds.y += 1; bounds.height -= 2; - // fill the area - g.setColor( m_fillColor ); - g.fillRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); - - // draw a box around the area - g.setColor( m_borderColor ); - g.drawRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); + return bounds; } catch( BadLocationException ex ) { diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index cd0fac76..914359b4 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -17,6 +17,7 @@ import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GridLayout; +import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; @@ -53,6 +54,7 @@ import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; +import javax.swing.Timer; import javax.swing.WindowConstants; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; @@ -142,8 +144,9 @@ public class Gui private JList m_deobfClasses; private JEditorPane m_editor; private JPanel m_infoPanel; - private BoxHighlightPainter m_obfuscatedHighlightPainter; - private BoxHighlightPainter m_deobfuscatedHighlightPainter; + private ObfuscatedHighlightPainter m_obfuscatedHighlightPainter; + private DeobfuscatedHighlightPainter m_deobfuscatedHighlightPainter; + private SelectionHighlightPainter m_selectionHighlightPainter; private JTree m_inheritanceTree; private JTree m_callsTree; private JList m_tokens; @@ -250,6 +253,7 @@ public class Gui DefaultSyntaxKit.initKit(); m_obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); m_deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); + m_selectionHighlightPainter = new SelectionHighlightPainter(); m_editor = new JEditorPane(); m_editor.setEditable( false ); m_editor.setCaret( new BrowserCaret() ); @@ -756,14 +760,64 @@ public class Gui m_editor.setText( source ); } - public void showToken( Token token ) + public void showToken( final Token token ) { if( token == null ) { throw new IllegalArgumentException( "Token cannot be null!" ); } + + // set the caret position to the token m_editor.setCaretPosition( token.start ); m_editor.grabFocus(); + + try + { + // make sure the token is visible in the scroll window + Rectangle start = m_editor.modelToView( token.start ); + Rectangle end = m_editor.modelToView( token.end ); + Rectangle show = start.union( end ); + show.grow( 0, start.height*6 ); + m_editor.scrollRectToVisible( show ); + } + catch( BadLocationException ex ) + { + throw new Error( ex ); + } + + // highlight the token momentarily + final Timer timer = new Timer( 200, new ActionListener( ) + { + private int m_counter = 0; + private Object m_highlight = null; + + @Override + public void actionPerformed( ActionEvent event ) + { + if( m_counter % 2 == 0 ) + { + try + { + m_highlight = m_editor.getHighlighter().addHighlight( token.start, token.end, m_selectionHighlightPainter ); + } + catch( BadLocationException ex ) + { + // don't care + } + } + else if( m_highlight != null ) + { + m_editor.getHighlighter().removeHighlight( m_highlight ); + } + + if( m_counter++ > 6 ) + { + Timer timer = (Timer)event.getSource(); + timer.stop(); + } + } + } ); + timer.start(); } public void showTokens( Collection tokens ) @@ -790,6 +844,7 @@ public class Gui // remove any old highlighters m_editor.getHighlighter().removeAllHighlights(); + // color things based on the index if( obfuscatedTokens != null ) { diff --git a/src/cuchaz/enigma/gui/SelectionHighlightPainter.java b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java new file mode 100644 index 00000000..35f94518 --- /dev/null +++ b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; + +public class SelectionHighlightPainter implements Highlighter.HighlightPainter +{ + @Override + public void paint( Graphics g, int start, int end, Shape shape, JTextComponent text ) + { + // draw a thick border + Graphics2D g2d = (Graphics2D)g; + Rectangle bounds = BoxHighlightPainter.getBounds( text, start, end ); + g2d.setColor( Color.black ); + g2d.setStroke( new BasicStroke( 2.0f ) ); + g2d.drawRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); + } +} -- cgit v1.2.3 From b4b9f296e9bcdbfcabb1cf49885a1747ce094c61 Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 22 Aug 2014 01:41:41 -0400 Subject: fixed recursion of field reference nodes tweaked scroll into view for showing tokens --- src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java | 4 ++++ src/cuchaz/enigma/gui/BoxHighlightPainter.java | 3 ++- src/cuchaz/enigma/gui/Gui.java | 16 +++++++++++++--- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java index 645e6821..94d0da7e 100644 --- a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -86,6 +86,10 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re { ((BehaviorReferenceTreeNode)node).load( index, true ); } + else if( node instanceof FieldReferenceTreeNode ) + { + ((FieldReferenceTreeNode)node).load( index, true ); + } } } } diff --git a/src/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/cuchaz/enigma/gui/BoxHighlightPainter.java index 2c118340..30b2b021 100644 --- a/src/cuchaz/enigma/gui/BoxHighlightPainter.java +++ b/src/cuchaz/enigma/gui/BoxHighlightPainter.java @@ -61,7 +61,8 @@ public abstract class BoxHighlightPainter implements Highlighter.HighlightPainte } catch( BadLocationException ex ) { - throw new Error( ex ); + // don't care... just return something + return new Rectangle( 0, 0, 0, 0 ); } } } diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 914359b4..dd82b8e0 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -54,6 +54,7 @@ import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.WindowConstants; import javax.swing.event.CaretEvent; @@ -776,9 +777,16 @@ public class Gui // make sure the token is visible in the scroll window Rectangle start = m_editor.modelToView( token.start ); Rectangle end = m_editor.modelToView( token.end ); - Rectangle show = start.union( end ); - show.grow( 0, start.height*6 ); - m_editor.scrollRectToVisible( show ); + final Rectangle show = start.union( end ); + show.grow( start.width*10, start.height*6 ); + SwingUtilities.invokeLater( new Runnable( ) + { + @Override + public void run( ) + { + m_editor.scrollRectToVisible( show ); + } + } ); } catch( BadLocationException ex ) { @@ -818,6 +826,8 @@ public class Gui } } ); timer.start(); + + redraw(); } public void showTokens( Collection tokens ) -- cgit v1.2.3 From d9294b6e114303f7fbd461ff8b4d6fd7e1db4762 Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 22 Aug 2014 01:45:42 -0400 Subject: packaged for 0.3 beta --- build.gradle | 2 +- readme.txt | 2 +- src/cuchaz/enigma/Constants.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 05f12504..cb676140 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ targetCompatibility = 1.7 group = "com.cuchazinteractive" archivesBaseName = "enigma" -version = "0.1b" +version = "0.3b" sourceSets { diff --git a/readme.txt b/readme.txt index 944c4890..f1ed6d6d 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ -Enigma v0.2 beta +Enigma v0.3 beta A tool for deobfuscation of Java bytecode Copyright Jeff Martin, 2014 diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 0b431c71..7ae778dd 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -14,7 +14,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.2 beta"; + public static final String Version = "0.3 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024*1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte -- cgit v1.2.3 -- cgit v1.2.3 From a700b403d790c23989da524c934f0185b87c7b32 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 23 Aug 2014 16:20:15 -0400 Subject: added export command with progress bar --- src/cuchaz/enigma/Deobfuscator.java | 78 +++++++++++++++++++++++---- src/cuchaz/enigma/gui/Gui.java | 38 +++++++++---- src/cuchaz/enigma/gui/GuiController.java | 26 ++++++++- src/cuchaz/enigma/gui/GuiTricks.java | 25 +++++++++ src/cuchaz/enigma/gui/ProgressDialog.java | 89 +++++++++++++++++++++++++++++++ 5 files changed, 233 insertions(+), 23 deletions(-) create mode 100644 src/cuchaz/enigma/gui/GuiTricks.java create mode 100644 src/cuchaz/enigma/gui/ProgressDialog.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 38f7af56..5d87ad0a 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -11,6 +11,7 @@ package cuchaz.enigma; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.util.List; @@ -44,6 +45,12 @@ import cuchaz.enigma.mapping.Translator; public class Deobfuscator { + public interface ProgressListener + { + void init( int totalWork ); + void onProgress( int numDone, String message ); + } + private File m_file; private JarFile m_jar; private DecompilerSettings m_settings; @@ -137,7 +144,7 @@ public class Deobfuscator } } - public SourceIndex getSource( String className ) + public CompilationUnit getSourceTree( String className ) { // is this class deobfuscated? // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out @@ -156,19 +163,18 @@ public class Deobfuscator AstBuilder builder = new AstBuilder( context ); builder.addType( resolvedType ); builder.runTransformations( null ); - CompilationUnit root = builder.getCompilationUnit(); + return builder.getCompilationUnit(); + } + + public SourceIndex getSourceIndex( CompilationUnit sourceTree, String source ) + { + // build the source index + SourceIndex index = new SourceIndex( source ); + sourceTree.acceptVisitor( new SourceIndexVisitor(), index ); - // render the AST into source - StringWriter buf = new StringWriter(); - root.acceptVisitor( new InsertParenthesesVisitor(), null ); // DEBUG //root.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); - root.acceptVisitor( new JavaOutputVisitor( new PlainTextOutput( buf ), m_settings ), null ); - - // build the source index - SourceIndex index = new SourceIndex( buf.toString() ); - root.acceptVisitor( new SourceIndexVisitor(), index ); - + /* DEBUG for( Token token : index.referenceTokens() ) { @@ -180,6 +186,56 @@ public class Deobfuscator return index; } + public String getSource( CompilationUnit sourceTree ) + { + // render the AST into source + StringWriter buf = new StringWriter(); + sourceTree.acceptVisitor( new InsertParenthesesVisitor(), null ); + sourceTree.acceptVisitor( new JavaOutputVisitor( new PlainTextOutput( buf ), m_settings ), null ); + return buf.toString(); + } + + public void writeSources( File dirOut, ProgressListener progress ) + throws IOException + { + int numClasses = m_jarIndex.getObfClassNames().size(); + if( progress != null ) + { + progress.init( numClasses ); + } + int i = 0; + + // DEOBFUSCATE ALL THE THINGS!! @_@ + for( String obfClassName : m_jarIndex.getObfClassNames() ) + { + // skip inner classes + if( m_jarIndex.getOuterClass( obfClassName ) != null ) + { + continue; + } + + ClassEntry deobfClassEntry = deobfuscateEntry( new ClassEntry( obfClassName ) ); + if( progress != null ) + { + progress.onProgress( i++, deobfClassEntry.toString() ); + } + + // get the source + String source = getSource( getSourceTree( obfClassName ) ); + + // write the file + File file = new File( dirOut, deobfClassEntry.getName().replace( '.', '/' ) + ".java" ); + file.getParentFile().mkdirs(); + try( FileWriter out = new FileWriter( file ) ) + { + out.write( source ); + } + } + + // done! + progress.onProgress( numClasses, "Done!" ); + } + public T obfuscateEntry( T deobfEntry ) { if( deobfEntry == null ) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index dd82b8e0..febdfd43 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -15,7 +15,6 @@ import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; -import java.awt.Font; import java.awt.GridLayout; import java.awt.Rectangle; import java.awt.event.ActionEvent; @@ -169,6 +168,7 @@ public class Gui private EntryReference m_reference; private JFileChooser m_jarFileChooser; private JFileChooser m_mappingsFileChooser; + private JFileChooser m_exportFileChooser; public Gui( ) { @@ -177,6 +177,8 @@ public class Gui // init file choosers m_jarFileChooser = new JFileChooser(); m_mappingsFileChooser = new JFileChooser(); + m_exportFileChooser = new JFileChooser(); + m_exportFileChooser.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY ); // init frame m_frame = new JFrame( Constants.Name ); @@ -636,6 +638,22 @@ public class Gui m_closeMappingsMenu = item; } menu.addSeparator(); + { + JMenuItem item = new JMenuItem( "Export..." ); + menu.add( item ); + item.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + if( m_exportFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) + { + m_controller.export( m_exportFileChooser.getSelectedFile() ); + } + } + } ); + } + menu.addSeparator(); { JMenuItem item = new JMenuItem( "Exit" ); menu.add( item ); @@ -686,6 +704,11 @@ public class Gui m_frame.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE ); } + public JFrame getFrame( ) + { + return m_frame; + } + public GuiController getController( ) { return m_controller; @@ -887,7 +910,7 @@ public class Gui { m_infoPanel.removeAll(); JLabel label = new JLabel( "No identifier selected" ); - unboldLabel( label ); + GuiTricks.unboldLabel( label ); label.setHorizontalAlignment( JLabel.CENTER ); m_infoPanel.add( label ); @@ -975,7 +998,7 @@ public class Gui label.setPreferredSize( new Dimension( 100, label.getPreferredSize().height ) ); panel.add( label ); - panel.add( unboldLabel( new JLabel( value, JLabel.LEFT ) ) ); + panel.add( GuiTricks.unboldLabel( new JLabel( value, JLabel.LEFT ) ) ); } private void onCaretMove( int pos ) @@ -1059,7 +1082,7 @@ public class Gui // abort the rename JPanel panel = (JPanel)m_infoPanel.getComponent( 0 ); panel.remove( panel.getComponentCount() - 1 ); - panel.add( unboldLabel( new JLabel( m_reference.entry.getName(), JLabel.LEFT ) ) ); + panel.add( GuiTricks.unboldLabel( new JLabel( m_reference.entry.getName(), JLabel.LEFT ) ) ); m_editor.grabFocus(); @@ -1203,13 +1226,6 @@ public class Gui } } - private JLabel unboldLabel( JLabel label ) - { - Font font = label.getFont(); - label.setFont( font.deriveFont( font.getStyle() & ~Font.BOLD ) ); - return label; - } - private void redraw( ) { m_frame.validate(); diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index a35db056..cf4f002e 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -20,6 +20,7 @@ import java.util.List; import com.google.common.collect.Lists; import com.google.common.collect.Queues; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; @@ -106,6 +107,27 @@ public class GuiController refreshCurrentClass(); } + public void export( final File dirOut ) + { + new Thread( ) + { + @Override + public void run( ) + { + try + { + ProgressDialog progress = new ProgressDialog( m_gui.getFrame() ); + m_deobfuscator.writeSources( dirOut, progress ); + progress.close(); + } + catch( IOException ex ) + { + throw new Error( ex ); + } + } + }.start(); + } + public Token getToken( int pos ) { if( m_index == null ) @@ -295,7 +317,9 @@ public class GuiController public void run( ) { // decompile,deobfuscate the bytecode - m_index = m_deobfuscator.getSource( classEntry.getClassName() ); + CompilationUnit sourceTree = m_deobfuscator.getSourceTree( classEntry.getClassName() ); + String source = m_deobfuscator.getSource( sourceTree ); + m_index = m_deobfuscator.getSourceIndex( sourceTree, source ); m_gui.setSource( m_index.getSource() ); if( obfReference != null ) { diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java new file mode 100644 index 00000000..c79f4329 --- /dev/null +++ b/src/cuchaz/enigma/gui/GuiTricks.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Font; + +import javax.swing.JLabel; + +public class GuiTricks +{ + public static JLabel unboldLabel( JLabel label ) + { + Font font = label.getFont(); + label.setFont( font.deriveFont( font.getStyle() & ~Font.BOLD ) ); + return label; + } +} diff --git a/src/cuchaz/enigma/gui/ProgressDialog.java b/src/cuchaz/enigma/gui/ProgressDialog.java new file mode 100644 index 00000000..40ac6a69 --- /dev/null +++ b/src/cuchaz/enigma/gui/ProgressDialog.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; + +import javax.swing.BorderFactory; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.WindowConstants; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.Deobfuscator.ProgressListener; + +public class ProgressDialog implements ProgressListener +{ + private JFrame m_frame; + private JLabel m_text; + private JProgressBar m_progress; + + public ProgressDialog( JFrame parent ) + { + // init frame + m_frame = new JFrame( Constants.Name + " - Export" ); + final Container pane = m_frame.getContentPane(); + FlowLayout layout = new FlowLayout(); + layout.setAlignment( FlowLayout.LEFT ); + pane.setLayout( layout ); + + pane.add( new JLabel( "Decompiling classes..." ) ); + + // set up the progress bar + JPanel panel = new JPanel(); + pane.add( panel ); + panel.setLayout( new BorderLayout() ); + m_text = GuiTricks.unboldLabel( new JLabel() ); + m_progress = new JProgressBar(); + m_text.setBorder( BorderFactory.createEmptyBorder( 0, 0, 10, 0 ) ); + panel.add( m_text, BorderLayout.NORTH ); + panel.add( m_progress, BorderLayout.CENTER ); + panel.setPreferredSize( new Dimension( 360, 50 ) ); + + // show the frame + pane.doLayout(); + m_frame.setSize( 400, 120 ); + m_frame.setResizable( false ); + m_frame.setLocationRelativeTo( parent ); + m_frame.setVisible( true ); + m_frame.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE ); + } + + public void close( ) + { + m_frame.dispose(); + } + + @Override + public void init( int totalWork ) + { + m_text.setText( "Decompiling " + totalWork + " classes..." ); + m_progress.setMinimum( 0 ); + m_progress.setMaximum( totalWork ); + m_progress.setValue( 0 ); + } + + @Override + public void onProgress( int numDone, String message ) + { + m_text.setText( message ); + m_progress.setValue( numDone ); + + // update the frame + m_frame.validate(); + m_frame.repaint(); + } +} -- cgit v1.2.3 From 8fa1741b621644ef84a9395a4c395d4ff3a89207 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 23 Aug 2014 23:43:31 -0400 Subject: moved all classes from the default package into a package called "default" so they can be properly imported by other classes --- src/cuchaz/enigma/Deobfuscator.java | 49 ++++----- src/cuchaz/enigma/TranslatingTypeLoader.java | 19 +++- src/cuchaz/enigma/analysis/JarClassIterator.java | 45 ++++---- src/cuchaz/enigma/analysis/JarIndex.java | 40 ++++--- src/cuchaz/enigma/bytecode/ClassRenamer.java | 115 +++++++++++++++++++++ src/cuchaz/enigma/bytecode/ClassTranslator.java | 64 ++---------- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 6 +- src/cuchaz/enigma/gui/Gui.java | 3 +- src/cuchaz/enigma/gui/GuiController.java | 9 +- src/cuchaz/enigma/gui/GuiTricks.java | 18 ++++ src/cuchaz/enigma/mapping/ClassEntry.java | 25 +++++ .../enigma/mapping/IllegalNameException.java | 22 +++- src/cuchaz/enigma/mapping/NameValidator.java | 8 +- 13 files changed, 298 insertions(+), 125 deletions(-) create mode 100644 src/cuchaz/enigma/bytecode/ClassRenamer.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 5d87ad0a..ee414fa2 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -17,6 +17,8 @@ import java.io.StringWriter; import java.util.List; import java.util.jar.JarFile; +import javassist.bytecode.Descriptor; + import com.strobel.assembler.metadata.MetadataSystem; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.decompiler.DecompilerContext; @@ -118,28 +120,30 @@ public class Deobfuscator public void getSeparatedClasses( List obfClasses, List deobfClasses ) { - for( String obfClassName : m_jarIndex.getObfClassNames() ) + for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() ) { // skip inner classes - if( m_jarIndex.getOuterClass( obfClassName ) != null ) + if( m_jarIndex.getOuterClass( obfClassEntry.getName() ) != null ) { continue; } // separate the classes - ClassMapping classMapping = m_mappings.getClassByObf( obfClassName ); - if( classMapping != null && !classMapping.getObfName().equals( classMapping.getDeobfName() ) ) + ClassEntry deobfClassEntry = deobfuscateEntry( obfClassEntry ); + if( !deobfClassEntry.equals( obfClassEntry ) ) { - deobfClasses.add( classMapping.getDeobfName() ); + // if the class has a mapping, clearly it's deobfuscated + deobfClasses.add( deobfClassEntry.getName() ); } - else if( obfClassName.indexOf( '/' ) >= 0 ) + else if( !obfClassEntry.getPackageName().equals( "default" ) ) { - // this class is in a package and therefore is not obfuscated - deobfClasses.add( obfClassName ); + // also call it deobufscated if it's not in the "default" package + deobfClasses.add( obfClassEntry.getName() ); } else { - obfClasses.add( obfClassName ); + // otherwise, assume it's still obfuscated + obfClasses.add( obfClassEntry.getName() ); } } } @@ -198,7 +202,7 @@ public class Deobfuscator public void writeSources( File dirOut, ProgressListener progress ) throws IOException { - int numClasses = m_jarIndex.getObfClassNames().size(); + int numClasses = m_jarIndex.getObfClassEntries().size(); if( progress != null ) { progress.init( numClasses ); @@ -206,22 +210,22 @@ public class Deobfuscator int i = 0; // DEOBFUSCATE ALL THE THINGS!! @_@ - for( String obfClassName : m_jarIndex.getObfClassNames() ) + for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() ) { // skip inner classes - if( m_jarIndex.getOuterClass( obfClassName ) != null ) + if( obfClassEntry.isInnerClass() ) { continue; } - ClassEntry deobfClassEntry = deobfuscateEntry( new ClassEntry( obfClassName ) ); + ClassEntry deobfClassEntry = deobfuscateEntry( new ClassEntry( obfClassEntry ) ); if( progress != null ) { progress.onProgress( i++, deobfClassEntry.toString() ); } // get the source - String source = getSource( getSourceTree( obfClassName ) ); + String source = getSource( getSourceTree( obfClassEntry.getName() ) ); // write the file File file = new File( dirOut, deobfClassEntry.getName().replace( '.', '/' ) + ".java" ); @@ -284,7 +288,7 @@ public class Deobfuscator { if( obfEntry instanceof ClassEntry ) { - m_renamer.setClassName( (ClassEntry)obfEntry, newName ); + m_renamer.setClassName( (ClassEntry)obfEntry, Descriptor.toJvmName( newName ) ); } else if( obfEntry instanceof FieldEntry ) { @@ -348,21 +352,18 @@ public class Deobfuscator { if( obfEntry instanceof ClassEntry ) { - if( obfEntry.getName().indexOf( '$' ) >= 0 ) + ClassEntry obfClassEntry = (ClassEntry)obfEntry; + if( obfClassEntry.isInnerClass() ) { - String[] parts = obfEntry.getName().split( "\\$" ); - assert( parts.length == 2 ); // not supporting recursively-nested classes - String outerClassName = parts[0]; - String innerClassName = parts[1]; - // both classes must be in the list - return m_jarIndex.getObfClassNames().contains( outerClassName ) - && m_jarIndex.getObfClassNames().contains( innerClassName ); + return m_jarIndex.getObfClassEntries().contains( obfClassEntry.getOuterClassEntry() ) + && m_jarIndex.getObfClassEntries().contains( obfClassEntry.getInnerClassName() ); + // TODO: make sure this works for the inner class!! } else { // class must be in the list - return m_jarIndex.getObfClassNames().contains( obfEntry.getName() ); + return m_jarIndex.getObfClassEntries().contains( obfEntry ); } } else diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index cc863646..e7e941a9 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -13,6 +13,7 @@ package cuchaz.enigma; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -30,6 +31,7 @@ import com.strobel.assembler.metadata.ITypeLoader; import cuchaz.enigma.analysis.BridgeFixer; import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.bytecode.ClassRenamer; import cuchaz.enigma.bytecode.ClassTranslator; import cuchaz.enigma.bytecode.InnerClassWriter; import cuchaz.enigma.bytecode.MethodParameterWriter; @@ -99,22 +101,29 @@ public class TranslatingTypeLoader implements ITypeLoader return null; } - /* DEBUG + // DEBUG if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) { System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); } - */ + // // get the jar entry String classFileName; if( obfClassEntry.isInnerClass() ) { + // use just the inner class simple name for inner classes classFileName = obfClassEntry.getInnerClassName(); } + else if( obfClassEntry.getPackageName().equals( "default" ) ) + { + // use the outer class simple name for classes in the "default" package + classFileName = obfClassEntry.getSimpleName(); + } else { - classFileName = obfClassEntry.getOuterClassName(); + // otherwise, just use the class name (ie for classes in packages) + classFileName = obfClassEntry.getName(); } JarEntry entry = m_jar.getJarEntry( classFileName + ".class" ); if( entry == null ) @@ -147,6 +156,10 @@ public class TranslatingTypeLoader implements ITypeLoader classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); CtClass c = classPool.get( javaClassFileName ); + // we moved a lot of classes out of the default package into the "default" package + // make sure all the class references are consistent + ClassRenamer.moveAllClassesOutOfDefaultPackage( c, "default" ); + // reconstruct inner classes new InnerClassWriter( m_jarIndex ).write( c ); diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java index cf6df805..6c9f1245 100644 --- a/src/cuchaz/enigma/analysis/JarClassIterator.java +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -28,6 +28,7 @@ import javassist.bytecode.Descriptor; import com.beust.jcommander.internal.Lists; import cuchaz.enigma.Constants; +import cuchaz.enigma.mapping.ClassEntry; public class JarClassIterator implements Iterator { @@ -35,14 +36,23 @@ public class JarClassIterator implements Iterator private Iterator m_iter; public JarClassIterator( JarFile jar ) - { - this( jar, getClassEntries( jar ) ); - } - - public JarClassIterator( JarFile jar, List entries ) { m_jar = jar; - m_iter = entries.iterator(); + + // get the jar entries that correspond to classes + List classEntries = Lists.newArrayList(); + Enumeration entries = m_jar.entries(); + while( entries.hasMoreElements() ) + { + JarEntry entry = entries.nextElement(); + + // is this a class file? + if( entry.getName().endsWith( ".class" ) ) + { + classEntries.add( entry ); + } + } + m_iter = classEntries.iterator(); } @Override @@ -79,19 +89,13 @@ public class JarClassIterator implements Iterator } } - // determine the class name (ie chop off the ".class") - String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); - // get a javassist handle for the class + String className = Descriptor.toJavaName( getClassEntry( entry ).getName() ); ClassPool classPool = new ClassPool(); classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); return classPool.get( className ); } - catch( IOException ex ) - { - throw new Error( "Unable to read class: " + entry.getName() ); - } - catch( NotFoundException ex ) + catch( IOException | NotFoundException ex ) { throw new Error( "Unable to load class: " + entry.getName() ); } @@ -103,9 +107,9 @@ public class JarClassIterator implements Iterator throw new UnsupportedOperationException(); } - public static List getClassEntries( JarFile jar ) + public static List getClassEntries( JarFile jar ) { - List classes = Lists.newArrayList(); + List classEntries = Lists.newArrayList(); Enumeration entries = jar.entries(); while( entries.hasMoreElements() ) { @@ -114,10 +118,10 @@ public class JarClassIterator implements Iterator // is this a class file? if( !entry.isDirectory() && entry.getName().endsWith( ".class" ) ) { - classes.add( entry ); + classEntries.add( getClassEntry( entry ) ); } } - return classes; + return classEntries; } public static Iterable classes( final JarFile jar ) @@ -131,4 +135,9 @@ public class JarClassIterator implements Iterator } }; } + + private static ClassEntry getClassEntry( JarEntry entry ) + { + return new ClassEntry( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); + } } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 315a2864..cdaca372 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -17,7 +17,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.jar.JarEntry; import java.util.jar.JarFile; import javassist.CannotCompileException; @@ -35,13 +34,12 @@ import javassist.expr.MethodCall; import javassist.expr.NewExpr; import com.google.common.collect.HashMultimap; -import com.google.common.collect.HashMultiset; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; -import com.google.common.collect.Multiset; import com.google.common.collect.Sets; +import cuchaz.enigma.bytecode.ClassRenamer; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; @@ -53,7 +51,7 @@ import cuchaz.enigma.mapping.Translator; public class JarIndex { - private Set m_obfClassNames; + private Set m_obfClassEntries; private Ancestries m_ancestries; private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; @@ -64,7 +62,7 @@ public class JarIndex public JarIndex( ) { - m_obfClassNames = Sets.newHashSet(); + m_obfClassEntries = Sets.newHashSet(); m_ancestries = new Ancestries(); m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); @@ -77,15 +75,20 @@ public class JarIndex public void indexJar( JarFile jar ) { // pass 1: read the class names - for( JarEntry entry : JarClassIterator.getClassEntries( jar ) ) + for( ClassEntry classEntry : JarClassIterator.getClassEntries( jar ) ) { - String className = entry.getName().substring( 0, entry.getName().length() - 6 ); - m_obfClassNames.add( Descriptor.toJvmName( className ) ); + if( classEntry.isInDefaultPackage() ) + { + // move out of default package + classEntry = new ClassEntry( "default/" + classEntry.getName() ); + } + m_obfClassEntries.add( classEntry ); } // pass 2: index the types, methods for( CtClass c : JarClassIterator.classes( jar ) ) { + fixClass( c ); m_ancestries.addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); for( CtBehavior behavior : c.getDeclaredBehaviors() ) { @@ -96,8 +99,10 @@ public class JarIndex // pass 2: index inner classes and anonymous classes for( CtClass c : JarClassIterator.classes( jar ) ) { + fixClass( c ); + String outerClassName = findOuterClass( c ); - if( outerClassName != null )// /* TEMP */ && false ) + if( outerClassName != null ) { String innerClassName = Descriptor.toJvmName( c.getName() ); m_innerClasses.put( outerClassName, innerClassName ); @@ -127,6 +132,17 @@ public class JarIndex renameClasses( renames ); } + private void fixClass( CtClass c ) + { + ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); + if( classEntry.isInDefaultPackage() ) + { + // move class out of default package + classEntry = new ClassEntry( "default/" + classEntry.getName() ); + ClassRenamer.moveAllClassesOutOfDefaultPackage( c, "default" ); + } + } + private void indexBehavior( CtBehavior behavior ) { // get the method entry @@ -270,7 +286,7 @@ public class JarIndex else if( callerClasses.size() > 1 ) { // TEMP - System.out.println( "WARNING: Illegal class called by more than one class!" + callerClasses ); + System.out.println( "WARNING: Illegal constructor called by more than one class!" + callerClasses ); } } @@ -423,9 +439,9 @@ public class JarIndex return true; } - public Set getObfClassNames( ) + public Set getObfClassEntries( ) { - return m_obfClassNames; + return m_obfClassEntries; } public Ancestries getAncestries( ) diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java new file mode 100644 index 00000000..cba58617 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Map; +import java.util.Set; + +import javassist.ClassMap; +import javassist.CtClass; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.InnerClassesAttribute; + +import com.beust.jcommander.internal.Sets; +import com.google.common.collect.Maps; + +import cuchaz.enigma.mapping.ClassEntry; + +public class ClassRenamer +{ + public static void renameClasses( CtClass c, Map map ) + { + // build the map used by javassist + ClassMap nameMap = new ClassMap(); + for( Map.Entry entry : map.entrySet() ) + { + nameMap.put( entry.getKey().getName(), entry.getValue().getName() ); + } + c.replaceClassName( nameMap ); + + // translate the names in the InnerClasses attribute + ConstPool constants = c.getClassFile().getConstPool(); + InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); + if( attr != null ) + { + for( int i=0; i ATTR: %s,%s,%s", + obfClassEntry, deobfClassEntry, + attr.outerClass( i ), + attr.innerClass( i ), + attr.innerName( i ) + ) ); + */ + } + } + } + + public static Set getAllClassEntries( CtClass c ) + { + // get the classes that javassist knows about + final Set entries = Sets.newHashSet(); + ClassMap map = new ClassMap( ) + { + @Override + public Object get( Object obj ) + { + if( obj instanceof String ) + { + entries.add( new ClassEntry( (String)obj ) ); + } + return null; + } + private static final long serialVersionUID = -202160293602070641L; + }; + c.replaceClassName( map ); + + // also check InnerClassesAttribute + InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); + if( attr != null ) + { + for( int i=0; i map = Maps.newHashMap(); + for( ClassEntry classEntry : ClassRenamer.getAllClassEntries( c ) ) + { + if( classEntry.isInDefaultPackage() ) + { + map.put( classEntry, new ClassEntry( newPackageName + "/" + classEntry.getName() ) ); + } + } + ClassRenamer.renameClasses( c, map ); + } +} diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 9ce06a58..885b45fe 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -10,18 +10,16 @@ ******************************************************************************/ package cuchaz.enigma.bytecode; -import java.util.Set; +import java.util.Map; -import javassist.ClassMap; import javassist.CtBehavior; import javassist.CtClass; import javassist.CtField; import javassist.CtMethod; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; -import javassist.bytecode.InnerClassesAttribute; -import com.beust.jcommander.internal.Sets; +import com.beust.jcommander.internal.Maps; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.FieldEntry; @@ -136,61 +134,11 @@ public class ClassTranslator // translate all the class names referenced in the code // the above code only changed method/field/reference names and types, but not the class names themselves - Set classEntries = getAllClassEntries( c ); - ClassMap map = new ClassMap(); - for( ClassEntry obfClassEntry : classEntries ) + Map map = Maps.newHashMap(); + for( ClassEntry obfClassEntry : ClassRenamer.getAllClassEntries( c ) ) { - map.put( obfClassEntry.getName(), m_translator.translateEntry( obfClassEntry ).getName() ); + map.put( obfClassEntry, m_translator.translateEntry( obfClassEntry ) ); } - c.replaceClassName( map ); - - // translate the names in the InnerClasses attribute - InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); - if( attr != null ) - { - for( int i=0; i ATTR: %s,%s,%s", - obfClassEntry, deobfClassEntry, - attr.outerClass( i ), - attr.innerClass( i ), - attr.innerName( i ) - ) ); - */ - } - } - } - - private Set getAllClassEntries( CtClass c ) - { - final Set entries = Sets.newHashSet(); - ClassMap map = new ClassMap( ) - { - @Override - public Object get( Object obj ) - { - if( obj instanceof String ) - { - entries.add( new ClassEntry( (String)obj ) ); - } - return null; - } - private static final long serialVersionUID = -202160293602070641L; - }; - c.replaceClassName( map ); - return entries; + ClassRenamer.renameClasses( c, map ); } } diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index c412b1aa..2fb5fe0f 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -42,7 +42,7 @@ public class InnerClassWriter else { // this is an inner class, rename it to outer$inner - ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfClassName ); + ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + new ClassEntry( obfClassName ).getSimpleName() ); c.setName( obfClassEntry.getName() ); } @@ -60,8 +60,8 @@ public class InnerClassWriter c.getClassFile().addAttribute( attr ); for( String obfInnerClassName : obfInnerClassNames ) { - // deobfuscate the class names - ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfInnerClassName ); + // get the new inner class name + ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + new ClassEntry( obfInnerClassName ).getSimpleName() ); // here's what the JVM spec says about the InnerClasses attribute // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index febdfd43..4e636064 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -1073,8 +1073,9 @@ public class Gui } catch( IllegalNameException ex ) { - ex.printStackTrace( System.err ); text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) ); + text.setToolTipText( ex.getReason() ); + GuiTricks.showToolTipNow( text ); } return; } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index cf4f002e..90bce520 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -114,16 +114,19 @@ public class GuiController @Override public void run( ) { + ProgressDialog progress = new ProgressDialog( m_gui.getFrame() ); try { - ProgressDialog progress = new ProgressDialog( m_gui.getFrame() ); m_deobfuscator.writeSources( dirOut, progress ); - progress.close(); } - catch( IOException ex ) + catch( Exception ex ) { throw new Error( ex ); } + finally + { + progress.close(); + } } }.start(); } diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java index c79f4329..9b889ef4 100644 --- a/src/cuchaz/enigma/gui/GuiTricks.java +++ b/src/cuchaz/enigma/gui/GuiTricks.java @@ -11,8 +11,11 @@ package cuchaz.enigma.gui; import java.awt.Font; +import java.awt.event.MouseEvent; +import javax.swing.JComponent; import javax.swing.JLabel; +import javax.swing.ToolTipManager; public class GuiTricks { @@ -22,4 +25,19 @@ public class GuiTricks label.setFont( font.deriveFont( font.getStyle() & ~Font.BOLD ) ); return label; } + + public static void showToolTipNow( JComponent component ) + { + // HACKHACK: trick the tooltip manager into showing the tooltip right now + ToolTipManager manager = ToolTipManager.sharedInstance(); + int oldDelay = manager.getInitialDelay(); + manager.setInitialDelay( 0 ); + manager.mouseMoved( new MouseEvent( + component, + MouseEvent.MOUSE_MOVED, + System.currentTimeMillis(), + 0, 0, 0, 0, false + ) ); + manager.setInitialDelay( oldDelay ); + } } diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index dad6da90..fdb7c2c5 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -110,4 +110,29 @@ public class ClassEntry implements Entry, Serializable { return new ClassEntry( getOuterClassName() ); } + + public boolean isInDefaultPackage( ) + { + return m_name.indexOf( '/' ) < 0; + } + + public String getPackageName( ) + { + int pos = m_name.lastIndexOf( '/' ); + if( pos > 0 ) + { + return m_name.substring( 0, pos ); + } + return null; + } + + public String getSimpleName( ) + { + int pos = m_name.lastIndexOf( '/' ); + if( pos > 0 ) + { + return m_name.substring( pos + 1 ); + } + return m_name; + } } diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java index 560e5d93..830f05c4 100644 --- a/src/cuchaz/enigma/mapping/IllegalNameException.java +++ b/src/cuchaz/enigma/mapping/IllegalNameException.java @@ -15,15 +15,35 @@ public class IllegalNameException extends RuntimeException private static final long serialVersionUID = -2279910052561114323L; private String m_name; + private String m_reason; public IllegalNameException( String name ) + { + this( name, null ); + } + + public IllegalNameException( String name, String reason ) { m_name = name; + m_reason = reason; + } + + public String getReason( ) + { + return m_reason; } @Override public String getMessage( ) { - return "Illegal name: " + m_name; + StringBuilder buf = new StringBuilder(); + buf.append( "Illegal name: " ); + buf.append( m_name ); + if( m_reason != null ) + { + buf.append( " because " ); + buf.append( m_reason ); + } + return buf.toString(); } } diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java index a8421fa5..6df893fb 100644 --- a/src/cuchaz/enigma/mapping/NameValidator.java +++ b/src/cuchaz/enigma/mapping/NameValidator.java @@ -59,7 +59,11 @@ public class NameValidator { if( name == null || !ClassPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) { - throw new IllegalNameException( name ); + throw new IllegalNameException( name, "This doesn't look like a legal class name" ); + } + if( new ClassEntry( name ).getPackageName() == null ) + { + throw new IllegalNameException( name, "Classes must be in a package" ); } return Descriptor.toJvmName( name ); } @@ -68,7 +72,7 @@ public class NameValidator { if( name == null || !IdentifierPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) { - throw new IllegalNameException( name ); + throw new IllegalNameException( name, "This doesn't look like a legal identifier" ); } return name; } -- cgit v1.2.3 From 486152b09706d9b64e83c18b508e960ad809f0e0 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 23 Aug 2014 23:47:18 -0400 Subject: MappingsReader will convert old classnames in the default package to the "default" package --- src/cuchaz/enigma/mapping/MappingsReader.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index 4cebb3a4..d64bcaae 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -150,9 +150,22 @@ public class MappingsReader private ClassMapping readClass( Scanner scanner ) { - return new ClassMapping( scanner.next(), scanner.next() ); + return new ClassMapping( + moveOutOfDefaultPackage( scanner.next(), "default" ), + moveOutOfDefaultPackage( scanner.next(), "default" ) + ); } + private String moveOutOfDefaultPackage( String className, String newPackageName ) + { + ClassEntry classEntry = new ClassEntry( className ); + if( classEntry.isInDefaultPackage() ) + { + return newPackageName + "/" + classEntry.getName(); + } + return className; + } + private FieldMapping readField( Scanner scanner ) { return new FieldMapping( scanner.next(), scanner.next() ); -- cgit v1.2.3 From 7c04d2753e21b3b9b7dea53a7511494e8a2804aa Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 24 Aug 2014 00:08:37 -0400 Subject: duh! We can't put classes in a package called "default" "default" is a java reserved word use "none" instead --- src/cuchaz/enigma/Constants.java | 1 + src/cuchaz/enigma/Deobfuscator.java | 6 +++--- src/cuchaz/enigma/Main.java | 2 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 8 ++++---- src/cuchaz/enigma/analysis/JarIndex.java | 7 ++++--- src/cuchaz/enigma/mapping/MappingsReader.java | 5 +++-- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 7ae778dd..1aa9e146 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -18,4 +18,5 @@ public class Constants public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024*1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte + public static final String NonePackage = "none"; } diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index ee414fa2..cfd28429 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -135,9 +135,9 @@ public class Deobfuscator // if the class has a mapping, clearly it's deobfuscated deobfClasses.add( deobfClassEntry.getName() ); } - else if( !obfClassEntry.getPackageName().equals( "default" ) ) + else if( !obfClassEntry.getPackageName().equals( Constants.NonePackage ) ) { - // also call it deobufscated if it's not in the "default" package + // also call it deobufscated if it's not in the none package deobfClasses.add( obfClassEntry.getName() ); } else @@ -213,7 +213,7 @@ public class Deobfuscator for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() ) { // skip inner classes - if( obfClassEntry.isInnerClass() ) + if( m_jarIndex.getOuterClass( obfClassEntry.getName() ) != null ) { continue; } diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index 7d38bd62..c1547193 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -32,7 +32,7 @@ public class Main } // DEBUG - //gui.getController().openEntry( new ClassEntry( "bah$bag" ) ); // bah,bag + //gui.getController().openDeclaration( new ClassEntry( "none/aay" ) ); } private static File getFile( String path ) diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index e7e941a9..162858de 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -115,9 +115,9 @@ public class TranslatingTypeLoader implements ITypeLoader // use just the inner class simple name for inner classes classFileName = obfClassEntry.getInnerClassName(); } - else if( obfClassEntry.getPackageName().equals( "default" ) ) + else if( obfClassEntry.getPackageName().equals( Constants.NonePackage ) ) { - // use the outer class simple name for classes in the "default" package + // use the outer class simple name for classes in the none package classFileName = obfClassEntry.getSimpleName(); } else @@ -156,9 +156,9 @@ public class TranslatingTypeLoader implements ITypeLoader classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); CtClass c = classPool.get( javaClassFileName ); - // we moved a lot of classes out of the default package into the "default" package + // we moved a lot of classes out of the default package into the none package // make sure all the class references are consistent - ClassRenamer.moveAllClassesOutOfDefaultPackage( c, "default" ); + ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); // reconstruct inner classes new InnerClassWriter( m_jarIndex ).write( c ); diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index cdaca372..2c4dec48 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -39,6 +39,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; +import cuchaz.enigma.Constants; import cuchaz.enigma.bytecode.ClassRenamer; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; @@ -80,7 +81,7 @@ public class JarIndex if( classEntry.isInDefaultPackage() ) { // move out of default package - classEntry = new ClassEntry( "default/" + classEntry.getName() ); + classEntry = new ClassEntry( Constants.NonePackage + "/" + classEntry.getName() ); } m_obfClassEntries.add( classEntry ); } @@ -138,8 +139,8 @@ public class JarIndex if( classEntry.isInDefaultPackage() ) { // move class out of default package - classEntry = new ClassEntry( "default/" + classEntry.getName() ); - ClassRenamer.moveAllClassesOutOfDefaultPackage( c, "default" ); + classEntry = new ClassEntry( Constants.NonePackage + "/" + classEntry.getName() ); + ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); } } diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index d64bcaae..5570721f 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -19,6 +19,7 @@ import java.util.Scanner; import com.google.common.collect.Queues; +import cuchaz.enigma.Constants; import cuchaz.enigma.Util; public class MappingsReader @@ -151,8 +152,8 @@ public class MappingsReader private ClassMapping readClass( Scanner scanner ) { return new ClassMapping( - moveOutOfDefaultPackage( scanner.next(), "default" ), - moveOutOfDefaultPackage( scanner.next(), "default" ) + moveOutOfDefaultPackage( scanner.next(), Constants.NonePackage ), + moveOutOfDefaultPackage( scanner.next(), Constants.NonePackage ) ); } -- cgit v1.2.3 From 9c28492b717b866c6b70fe1ef4552eac66857f25 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 24 Aug 2014 16:35:38 -0400 Subject: minor bug fixes --- src/cuchaz/enigma/Deobfuscator.java | 1 + src/cuchaz/enigma/Main.java | 2 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 6 +++++- src/cuchaz/enigma/bytecode/ClassRenamer.java | 4 ++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index cfd28429..0bc4a043 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -73,6 +73,7 @@ public class Deobfuscator // config the decompiler m_settings = DecompilerSettings.javaDefaults(); + m_settings.setForceExplicitImports( true ); // DEBUG //m_settings.setShowSyntheticMembers( true ); diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index c1547193..fe4d6e34 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -32,7 +32,7 @@ public class Main } // DEBUG - //gui.getController().openDeclaration( new ClassEntry( "none/aay" ) ); + //gui.getController().openDeclaration( new ClassEntry( "none/bgl" ) ); } private static File getFile( String path ) diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 162858de..763d767c 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -27,6 +27,7 @@ import javassist.bytecode.Descriptor; import com.beust.jcommander.internal.Maps; import com.strobel.assembler.metadata.Buffer; +import com.strobel.assembler.metadata.ClasspathTypeLoader; import com.strobel.assembler.metadata.ITypeLoader; import cuchaz.enigma.analysis.BridgeFixer; @@ -45,6 +46,7 @@ public class TranslatingTypeLoader implements ITypeLoader private Translator m_obfuscatingTranslator; private Translator m_deobfuscatingTranslator; private Map m_cache; + private ClasspathTypeLoader m_defaultTypeLoader; public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator ) { @@ -53,6 +55,7 @@ public class TranslatingTypeLoader implements ITypeLoader m_obfuscatingTranslator = obfuscatingTranslator; m_deobfuscatingTranslator = deobfuscatingTranslator; m_cache = Maps.newHashMap(); + m_defaultTypeLoader = new ClasspathTypeLoader(); } public void clearCache( ) @@ -77,7 +80,8 @@ public class TranslatingTypeLoader implements ITypeLoader if( data == null ) { - return false; + // chain to default type loader + return m_defaultTypeLoader.tryLoadType( deobfClassName, out ); } // send the class to the decompiler diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index cba58617..f3a8c0ef 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -45,6 +45,10 @@ public class ClassRenamer { ClassEntry inClassEntry = new ClassEntry( Descriptor.toJvmName( attr.innerClass( i ) ) ); ClassEntry outClassEntry = map.get( inClassEntry ); + if( outClassEntry == null ) + { + continue; + } attr.setInnerClassIndex( i, constants.addClassInfo( outClassEntry.getName() ) ); if( attr.outerClassIndex( i ) != 0 ) { -- cgit v1.2.3 From 2e97f24a658aa897313cb7cf92ed2e4b4a986bfc Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 25 Aug 2014 00:04:47 -0400 Subject: fixed issue with bridge methods so source export has fewer compile errors. =) --- src/cuchaz/enigma/Deobfuscator.java | 6 + src/cuchaz/enigma/Main.java | 3 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 2 +- src/cuchaz/enigma/analysis/BridgeFixer.java | 92 -------------- src/cuchaz/enigma/analysis/JarIndex.java | 172 ++++++++++++++++++++++++--- 5 files changed, 163 insertions(+), 112 deletions(-) delete mode 100644 src/cuchaz/enigma/analysis/BridgeFixer.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 0bc4a043..8a516b18 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -219,6 +219,12 @@ public class Deobfuscator continue; } + // TEMP: skip the classes that won't decompile because of a procyon bug + if( obfClassEntry.getName().equals( "none/bgl" ) ) + { + continue; + } + ClassEntry deobfClassEntry = deobfuscateEntry( new ClassEntry( obfClassEntry ) ); if( progress != null ) { diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index fe4d6e34..c69b8907 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -13,6 +13,7 @@ package cuchaz.enigma; import java.io.File; import cuchaz.enigma.gui.Gui; +import cuchaz.enigma.mapping.ClassEntry; public class Main { @@ -32,7 +33,7 @@ public class Main } // DEBUG - //gui.getController().openDeclaration( new ClassEntry( "none/bgl" ) ); + gui.getController().openDeclaration( new ClassEntry( "none/bsp" ) ); } private static File getFile( String path ) diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 763d767c..83f0baa4 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -177,7 +177,7 @@ public class TranslatingTypeLoader implements ITypeLoader assertClassName( c, obfClassEntry ); // do all kinds of deobfuscating transformations on the class - new BridgeFixer().fixBridges( c ); + new BridgeFixer( m_jarIndex ).fixBridges( c ); new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); diff --git a/src/cuchaz/enigma/analysis/BridgeFixer.java b/src/cuchaz/enigma/analysis/BridgeFixer.java deleted file mode 100644 index f13f68f4..00000000 --- a/src/cuchaz/enigma/analysis/BridgeFixer.java +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import java.util.List; - -import javassist.CannotCompileException; -import javassist.CtClass; -import javassist.CtMethod; -import javassist.NotFoundException; -import javassist.bytecode.AccessFlag; -import javassist.expr.ExprEditor; -import javassist.expr.MethodCall; - -import com.beust.jcommander.internal.Lists; - -public class BridgeFixer -{ - public void fixBridges( CtClass c ) - { - // bridge methods are scrubbed and marked as synthetic methods by the obfuscator - // need to figure out which synthetics are bridge methods and restore them - for( CtMethod method : c.getDeclaredMethods() ) - { - // skip non-synthetic methods - if( ( method.getModifiers() & AccessFlag.SYNTHETIC ) == 0 ) - { - continue; - } - - CtMethod bridgedMethod = getBridgedMethod( method ); - if( bridgedMethod != null ) - { - bridgedMethod.setName( method.getName() ); - method.setModifiers( method.getModifiers() | AccessFlag.BRIDGE ); - - // TODO: rename all references to this method? - } - } - } - - private CtMethod getBridgedMethod( CtMethod method ) - { - // bridge methods just call another method, cast it to the return type, and return the result - // let's see if we can detect this scenario - - // get all the called methods - final List methodCalls = Lists.newArrayList(); - try - { - method.instrument( new ExprEditor( ) - { - @Override - public void edit( MethodCall call ) - { - methodCalls.add( call ); - } - } ); - } - catch( CannotCompileException ex ) - { - // this is stupid... we're not even compiling anything - throw new Error( ex ); - } - - // is there just one? - if( methodCalls.size() != 1 ) - { - return null; - } - MethodCall call = methodCalls.get( 0 ); - - try - { - // we have a bridge method! - return call.getMethod(); - } - catch( NotFoundException ex ) - { - // can't find the type? not a bridge method - return null; - } - } -} diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 2c4dec48..cb7508e1 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -24,6 +24,7 @@ import javassist.CtBehavior; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtMethod; +import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import javassist.bytecode.Descriptor; import javassist.bytecode.FieldInfo; @@ -60,6 +61,7 @@ public class JarIndex private Multimap m_innerClasses; private Map m_outerClasses; private Set m_anonymousClasses; + private Map m_bridgeMethods; public JarIndex( ) { @@ -71,11 +73,12 @@ public class JarIndex m_innerClasses = HashMultimap.create(); m_outerClasses = Maps.newHashMap(); m_anonymousClasses = Sets.newHashSet(); + m_bridgeMethods = Maps.newHashMap(); } public void indexJar( JarFile jar ) { - // pass 1: read the class names + // step 1: read the class names for( ClassEntry classEntry : JarClassIterator.getClassEntries( jar ) ) { if( classEntry.isInDefaultPackage() ) @@ -86,7 +89,7 @@ public class JarIndex m_obfClassEntries.add( classEntry ); } - // pass 2: index the types, methods + // step 2: index the types, methods for( CtClass c : JarClassIterator.classes( jar ) ) { fixClass( c ); @@ -97,7 +100,7 @@ public class JarIndex } } - // pass 2: index inner classes and anonymous classes + // step 3: index inner classes and anonymous classes for( CtClass c : JarClassIterator.classes( jar ) ) { fixClass( c ); @@ -124,13 +127,16 @@ public class JarIndex } } - // step 3: update other indicies with inner class info + // step 4: update other indices with inner class info Map renames = Maps.newHashMap(); for( Map.Entry entry : m_outerClasses.entrySet() ) { renames.put( entry.getKey(), entry.getValue() + "$" + entry.getKey() ); } renameClasses( renames ); + + // step 5: update other indices with bridge method info + renameMethods( m_bridgeMethods ); } private void fixClass( CtClass c ) @@ -160,6 +166,18 @@ public class JarIndex // index implementation m_methodImplementations.put( className, methodEntry ); + + // look for bridge methods + CtMethod bridgedMethod = getBridgedMethod( (CtMethod)behavior ); + if( bridgedMethod != null ) + { + MethodEntry bridgedMethodEntry = new MethodEntry( + new ClassEntry( className ), + bridgedMethod.getName(), + bridgedMethod.getSignature() + ); + m_bridgeMethods.put( bridgedMethodEntry, methodEntry ); + } } else if( behavior instanceof CtConstructor ) { @@ -254,6 +272,56 @@ public class JarIndex } } + + private CtMethod getBridgedMethod( CtMethod method ) + { + // bridge methods just call another method, cast it to the return type, and return the result + // let's see if we can detect this scenario + + // skip non-synthetic methods + if( ( method.getModifiers() & AccessFlag.SYNTHETIC ) == 0 ) + { + return null; + } + + // get all the called methods + final List methodCalls = Lists.newArrayList(); + try + { + method.instrument( new ExprEditor( ) + { + @Override + public void edit( MethodCall call ) + { + methodCalls.add( call ); + } + } ); + } + catch( CannotCompileException ex ) + { + // this is stupid... we're not even compiling anything + throw new Error( ex ); + } + + // is there just one? + if( methodCalls.size() != 1 ) + { + return null; + } + MethodCall call = methodCalls.get( 0 ); + + try + { + // we have a bridge method! + return call.getMethod(); + } + catch( NotFoundException ex ) + { + // can't find the type? not a bridge method + return null; + } + } + private String findOuterClass( CtClass c ) { // inner classes: @@ -534,15 +602,27 @@ public class JarIndex return m_anonymousClasses.contains( obfInnerClassName ); } + public MethodEntry getBridgeMethod( MethodEntry methodEntry ) + { + return m_bridgeMethods.get( methodEntry ); + } + private void renameClasses( Map renames ) { m_ancestries.renameClasses( renames ); - renameMultimap( renames, m_methodImplementations ); - renameMultimap( renames, m_behaviorReferences ); - renameMultimap( renames, m_fieldReferences ); + renameClassesInMultimap( renames, m_methodImplementations ); + renameClassesInMultimap( renames, m_behaviorReferences ); + renameClassesInMultimap( renames, m_fieldReferences ); + } + + private void renameMethods( Map renames ) + { + renameMethodsInMultimap( renames, m_methodImplementations ); + renameMethodsInMultimap( renames, m_behaviorReferences ); + renameMethodsInMultimap( renames, m_fieldReferences ); } - private void renameMultimap( Map renames, Multimap map ) + private void renameClassesInMultimap( Map renames, Multimap map ) { // for each key/value pair... Set> entriesToAdd = Sets.newHashSet(); @@ -552,8 +632,8 @@ public class JarIndex Map.Entry entry = iter.next(); iter.remove(); entriesToAdd.add( new AbstractMap.SimpleEntry( - renameThing( renames, entry.getKey() ), - renameThing( renames, entry.getValue() ) + renameClassesInThing( renames, entry.getKey() ), + renameClassesInThing( renames, entry.getValue() ) ) ); } for( Map.Entry entry : entriesToAdd ) @@ -563,7 +643,7 @@ public class JarIndex } @SuppressWarnings( "unchecked" ) - private T renameThing( Map renames, T thing ) + private T renameClassesInThing( Map renames, T thing ) { if( thing instanceof String ) { @@ -576,13 +656,13 @@ public class JarIndex else if( thing instanceof ClassEntry ) { ClassEntry classEntry = (ClassEntry)thing; - return (T)new ClassEntry( renameThing( renames, classEntry.getClassName() ) ); + return (T)new ClassEntry( renameClassesInThing( renames, classEntry.getClassName() ) ); } else if( thing instanceof FieldEntry ) { FieldEntry fieldEntry = (FieldEntry)thing; return (T)new FieldEntry( - renameThing( renames, fieldEntry.getClassEntry() ), + renameClassesInThing( renames, fieldEntry.getClassEntry() ), fieldEntry.getName() ); } @@ -590,7 +670,7 @@ public class JarIndex { ConstructorEntry constructorEntry = (ConstructorEntry)thing; return (T)new ConstructorEntry( - renameThing( renames, constructorEntry.getClassEntry() ), + renameClassesInThing( renames, constructorEntry.getClassEntry() ), constructorEntry.getSignature() ); } @@ -598,7 +678,7 @@ public class JarIndex { MethodEntry methodEntry = (MethodEntry)thing; return (T)new MethodEntry( - renameThing( renames, methodEntry.getClassEntry() ), + renameClassesInThing( renames, methodEntry.getClassEntry() ), methodEntry.getName(), methodEntry.getSignature() ); @@ -607,7 +687,7 @@ public class JarIndex { ArgumentEntry argumentEntry = (ArgumentEntry)thing; return (T)new ArgumentEntry( - renameThing( renames, argumentEntry.getMethodEntry() ), + renameClassesInThing( renames, argumentEntry.getMethodEntry() ), argumentEntry.getIndex(), argumentEntry.getName() ); @@ -615,8 +695,8 @@ public class JarIndex else if( thing instanceof EntryReference ) { EntryReference reference = (EntryReference)thing; - reference.entry = renameThing( renames, reference.entry ); - reference.context = renameThing( renames, reference.context ); + reference.entry = renameClassesInThing( renames, reference.entry ); + reference.context = renameClassesInThing( renames, reference.context ); return thing; } else @@ -626,4 +706,60 @@ public class JarIndex return thing; } + + private void renameMethodsInMultimap( Map renames, Multimap map ) + { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + Iterator> iter = map.entries().iterator(); + while( iter.hasNext() ) + { + Map.Entry entry = iter.next(); + iter.remove(); + entriesToAdd.add( new AbstractMap.SimpleEntry( + renameMethodsInThing( renames, entry.getKey() ), + renameMethodsInThing( renames, entry.getValue() ) + ) ); + } + for( Map.Entry entry : entriesToAdd ) + { + map.put( entry.getKey(), entry.getValue() ); + } + } + + @SuppressWarnings( "unchecked" ) + private T renameMethodsInThing( Map renames, T thing ) + { + if( thing instanceof MethodEntry ) + { + MethodEntry methodEntry = (MethodEntry)thing; + MethodEntry newMethodEntry = renames.get( methodEntry ); + if( newMethodEntry != null ) + { + return (T)new MethodEntry( + methodEntry.getClassEntry(), + newMethodEntry.getName(), + methodEntry.getSignature() + ); + } + return thing; + } + else if( thing instanceof ArgumentEntry ) + { + ArgumentEntry argumentEntry = (ArgumentEntry)thing; + return (T)new ArgumentEntry( + renameMethodsInThing( renames, argumentEntry.getMethodEntry() ), + argumentEntry.getIndex(), + argumentEntry.getName() + ); + } + else if( thing instanceof EntryReference ) + { + EntryReference reference = (EntryReference)thing; + reference.entry = renameMethodsInThing( renames, reference.entry ); + reference.context = renameMethodsInThing( renames, reference.context ); + return thing; + } + return thing; + } } -- cgit v1.2.3 From 5b35c2241ef6872bb76750b581f60d6060e40ee0 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 25 Aug 2014 00:05:18 -0400 Subject: silly mercurial. You should commit this file... --- src/cuchaz/enigma/analysis/BridgeFixer.java | 77 +++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/cuchaz/enigma/analysis/BridgeFixer.java diff --git a/src/cuchaz/enigma/analysis/BridgeFixer.java b/src/cuchaz/enigma/analysis/BridgeFixer.java new file mode 100644 index 00000000..aeaf871a --- /dev/null +++ b/src/cuchaz/enigma/analysis/BridgeFixer.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import javassist.CtClass; +import javassist.CtMethod; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import cuchaz.enigma.bytecode.ConstPoolEditor; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class BridgeFixer +{ + private JarIndex m_index; + + public BridgeFixer( JarIndex index ) + { + m_index = index; + } + + public void fixBridges( CtClass c ) + { + // rename declared methods + for( CtMethod method : c.getDeclaredMethods() ) + { + // get the method entry + MethodEntry methodEntry = new MethodEntry( + new ClassEntry( Descriptor.toJvmName( c.getName() ) ), + method.getName(), + method.getSignature() + ); + MethodEntry bridgeMethodEntry = m_index.getBridgeMethod( methodEntry ); + if( bridgeMethodEntry != null ) + { + // fix this bridged method + method.setName( bridgeMethodEntry.getName() ); + } + } + + // rename method references + // translate all the field and method references in the code by editing the constant pool + ConstPool constants = c.getClassFile().getConstPool(); + ConstPoolEditor editor = new ConstPoolEditor( constants ); + for( int i=1; i +{ + public static class CheckCast + { + public String className; + public MethodEntry prevMethodEntry; + + public CheckCast( String className, MethodEntry prevMethodEntry ) + { + this.className = className; + this.prevMethodEntry = prevMethodEntry; + } + } + + private ConstPool m_constants; + private CodeAttribute m_attribute; + private CodeIterator m_iter; + private CheckCast m_next; + + public CheckCastIterator( CodeAttribute codeAttribute ) + throws BadBytecode + { + m_constants = codeAttribute.getConstPool(); + m_attribute = codeAttribute; + m_iter = m_attribute.iterator(); + + m_next = getNext(); + } + + @Override + public boolean hasNext( ) + { + return m_next != null; + } + + @Override + public CheckCast next( ) + { + CheckCast out = m_next; + try + { + m_next = getNext(); + } + catch( BadBytecode ex ) + { + throw new Error( ex ); + } + return out; + } + + @Override + public void remove( ) + { + throw new UnsupportedOperationException(); + } + + private CheckCast getNext( ) + throws BadBytecode + { + int prevPos = 0; + while( m_iter.hasNext() ) + { + int pos = m_iter.next(); + int opcode = m_iter.byteAt( pos ); + switch( opcode ) + { + case Opcode.CHECKCAST: + + // get the type of this op code (next two bytes are a classinfo index) + MethodEntry prevMethodEntry = getMethodEntry( prevPos ); + if( prevMethodEntry != null ) + { + return new CheckCast( + m_constants.getClassInfo( m_iter.s16bitAt( pos + 1 ) ), + prevMethodEntry + ); + } + break; + } + prevPos = pos; + } + return null; + } + + private MethodEntry getMethodEntry( int pos ) + { + switch( m_iter.byteAt( pos ) ) + { + case Opcode.INVOKEVIRTUAL: + case Opcode.INVOKESTATIC: + case Opcode.INVOKEDYNAMIC: + case Opcode.INVOKESPECIAL: + { + int index = m_iter.s16bitAt( pos + 1 ); + return new MethodEntry( + new ClassEntry( Descriptor.toJvmName( m_constants.getMethodrefClassName( index ) ) ), + m_constants.getMethodrefName( index ), + m_constants.getMethodrefType( index ) + ); + } + + case Opcode.INVOKEINTERFACE: + { + int index = m_iter.s16bitAt( pos + 1 ); + return new MethodEntry( + new ClassEntry( Descriptor.toJvmName( m_constants.getInterfaceMethodrefClassName( index ) ) ), + m_constants.getInterfaceMethodrefName( index ), + m_constants.getInterfaceMethodrefType( index ) + ); + } + } + return null; + } + + public Iterable casts( ) + { + return new Iterable( ) + { + @Override + public Iterator iterator( ) + { + return CheckCastIterator.this; + } + }; + } +} -- cgit v1.2.3 From 5f44aac70f59898197c2a7625b74f901c3b31106 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 26 Aug 2014 00:27:44 -0400 Subject: implemented proper support for interfaces --- src/cuchaz/enigma/TranslatingTypeLoader.java | 5 +- src/cuchaz/enigma/analysis/Ancestries.java | 89 +++++++++++++++++ .../analysis/ClassImplementationsTreeNode.java | 92 +++++++++++++++++ src/cuchaz/enigma/analysis/JarIndex.java | 107 ++++++++++++++++++-- .../analysis/MethodImplementationsTreeNode.java | 111 +++++++++++++++++++++ src/cuchaz/enigma/gui/Gui.java | 104 ++++++++++++++++++- src/cuchaz/enigma/gui/GuiController.java | 25 +++++ src/cuchaz/enigma/mapping/Renamer.java | 21 +--- 8 files changed, 524 insertions(+), 30 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 83f0baa4..1e0e95a2 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -13,7 +13,6 @@ package cuchaz.enigma; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -105,12 +104,12 @@ public class TranslatingTypeLoader implements ITypeLoader return null; } - // DEBUG + /* DEBUG if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) { System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); } - // + */ // get the jar entry String classFileName; diff --git a/src/cuchaz/enigma/analysis/Ancestries.java b/src/cuchaz/enigma/analysis/Ancestries.java index b9d8cbf4..97241084 100644 --- a/src/cuchaz/enigma/analysis/Ancestries.java +++ b/src/cuchaz/enigma/analysis/Ancestries.java @@ -11,24 +11,32 @@ package cuchaz.enigma.analysis; import java.io.Serializable; +import java.util.AbstractMap; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import javassist.bytecode.Descriptor; +import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; public class Ancestries implements Serializable { private static final long serialVersionUID = 738687982126844179L; private Map m_superclasses; + private Multimap m_interfaces; public Ancestries( ) { m_superclasses = Maps.newHashMap(); + m_interfaces = HashMultimap.create(); } public void addSuperclass( String className, String superclassName ) @@ -47,8 +55,25 @@ public class Ancestries implements Serializable } } + public void addInterface( String className, String interfaceName ) + { + className = Descriptor.toJvmName( className ); + interfaceName = Descriptor.toJvmName( interfaceName ); + + if( className.equals( interfaceName ) ) + { + throw new IllegalArgumentException( "Class cannot be its own interface! " + className ); + } + + if( !isJre( className ) && !isJre( interfaceName ) ) + { + m_interfaces.put( className, interfaceName ); + } + } + public void renameClasses( Map renames ) { + // rename superclasses Map newSuperclasses = Maps.newHashMap(); for( Map.Entry entry : m_superclasses.entrySet() ) { @@ -65,6 +90,28 @@ public class Ancestries implements Serializable newSuperclasses.put( subclass, superclass ); } m_superclasses = newSuperclasses; + + // rename interfaces + Set> entriesToAdd = Sets.newHashSet(); + for( Map.Entry entry : m_interfaces.entries() ) + { + String className = renames.get( entry.getKey() ); + if( className == null ) + { + className = entry.getKey(); + } + String interfaceName = renames.get( entry.getValue() ); + if( interfaceName == null ) + { + interfaceName = entry.getValue(); + } + entriesToAdd.add( new AbstractMap.SimpleEntry( className, interfaceName ) ); + } + m_interfaces.clear(); + for( Map.Entry entry : entriesToAdd ) + { + m_interfaces.put( entry.getKey(), entry.getValue() ); + } } public String getSuperclassName( String className ) @@ -86,6 +133,17 @@ public class Ancestries implements Serializable return ancestors; } + public Set getInterfaces( String className ) + { + Set interfaceNames = new HashSet(); + interfaceNames.addAll( m_interfaces.get( className ) ); + for( String ancestor : getAncestry( className ) ) + { + interfaceNames.addAll( m_interfaces.get( ancestor ) ); + } + return interfaceNames; + } + public List getSubclasses( String className ) { // linear search is fast enough for now @@ -102,6 +160,37 @@ public class Ancestries implements Serializable return subclasses; } + public Set getImplementingClasses( String targetInterfaceName ) + { + // linear search is fast enough for now + Set classNames = Sets.newHashSet(); + for( Map.Entry entry : m_interfaces.entries() ) + { + String className = entry.getKey(); + String interfaceName = entry.getValue(); + if( interfaceName.equals( targetInterfaceName ) ) + { + classNames.add( className ); + collectSubclasses( classNames, className ); + } + } + return classNames; + } + + public boolean isInterface( String className ) + { + return m_interfaces.containsValue( className ); + } + + private void collectSubclasses( Set classNames, String className ) + { + for( String subclassName : getSubclasses( className ) ) + { + classNames.add( subclassName ); + collectSubclasses( classNames, subclassName ); + } + } + private boolean isJre( String className ) { return className.startsWith( "java/" ) diff --git a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java new file mode 100644 index 00000000..98648305 --- /dev/null +++ b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class ClassImplementationsTreeNode extends DefaultMutableTreeNode +{ + private static final long serialVersionUID = 3112703459157851912L; + + private Translator m_deobfuscatingTranslator; + private ClassEntry m_entry; + + public ClassImplementationsTreeNode( Translator deobfuscatingTranslator, ClassEntry entry ) + { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + } + + public ClassEntry getClassEntry( ) + { + return m_entry; + } + + public String getDeobfClassName( ) + { + return m_deobfuscatingTranslator.translateClass( m_entry.getClassName() ); + } + + @Override + public String toString( ) + { + String className = getDeobfClassName(); + if( className == null ) + { + className = m_entry.getClassName(); + } + return className; + } + + public void load( Ancestries ancestries ) + { + // get all method implementations + List nodes = Lists.newArrayList(); + for( String implementingClassName : ancestries.getImplementingClasses( m_entry.getClassName() ) ) + { + nodes.add( new ClassImplementationsTreeNode( m_deobfuscatingTranslator, new ClassEntry( implementingClassName ) ) ); + } + + // add them to this node + for( ClassImplementationsTreeNode node : nodes ) + { + this.add( node ); + } + } + + public static ClassImplementationsTreeNode findNode( ClassImplementationsTreeNode node, MethodEntry entry ) + { + // is this the node? + if( node.m_entry.equals( entry ) ) + { + return node; + } + + // recurse + for( int i=0; i 1 ) { - // TEMP System.out.println( "WARNING: Illegal constructor called by more than one class!" + callerClasses ); } } @@ -542,6 +546,13 @@ public class JarIndex return rootNode; } + public ClassImplementationsTreeNode getClassImplementations( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) + { + ClassImplementationsTreeNode node = new ClassImplementationsTreeNode( deobfuscatingTranslator, obfClassEntry ); + node.load( m_ancestries ); + return node; + } + public MethodInheritanceTreeNode getMethodInheritance( Translator deobfuscatingTranslator, MethodEntry obfMethodEntry ) { // travel to the ancestor implementation @@ -577,6 +588,90 @@ public class JarIndex return rootNode; } + public MethodImplementationsTreeNode getMethodImplementations( Translator deobfuscatingTranslator, MethodEntry obfMethodEntry ) + { + MethodEntry interfaceMethodEntry; + + // is this method on an interface? + if( m_ancestries.isInterface( obfMethodEntry.getClassName() ) ) + { + interfaceMethodEntry = obfMethodEntry; + } + else + { + // get the interface class + List methodInterfaces = Lists.newArrayList(); + for( String interfaceName : m_ancestries.getInterfaces( obfMethodEntry.getClassName() ) ) + { + // is this method defined in this interface? + MethodEntry methodInterface = new MethodEntry( + new ClassEntry( interfaceName ), + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + if( isMethodImplemented( methodInterface ) ) + { + methodInterfaces.add( methodInterface ); + } + } + if( methodInterfaces.isEmpty() ) + { + return null; + } + if( methodInterfaces.size() > 1 ) + { + throw new Error( "Too many interfaces define this method! This is not yet supported by Enigma!" ); + } + interfaceMethodEntry = methodInterfaces.get( 0 ); + } + + MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode( deobfuscatingTranslator, interfaceMethodEntry ); + rootNode.load( this ); + return rootNode; + } + + public Set getRelatedMethodImplementations( MethodEntry obfMethodEntry ) + { + Set methodEntries = Sets.newHashSet(); + getRelatedMethodImplementations( methodEntries, getMethodInheritance( null, obfMethodEntry ) ); + return methodEntries; + } + + private void getRelatedMethodImplementations( Set methodEntries, MethodInheritanceTreeNode node ) + { + MethodEntry methodEntry = node.getMethodEntry(); + if( isMethodImplemented( methodEntry ) ) + { + // collect the entry + methodEntries.add( methodEntry ); + } + + // look at interface methods too + getRelatedMethodImplementations( methodEntries, getMethodImplementations( null, methodEntry ) ); + + // recurse + for( int i=0; i methodEntries, MethodImplementationsTreeNode node ) + { + MethodEntry methodEntry = node.getMethodEntry(); + if( isMethodImplemented( methodEntry ) ) + { + // collect the entry + methodEntries.add( methodEntry ); + } + + // recurse + for( int i=0; i> getFieldReferences( FieldEntry fieldEntry ) { return m_fieldReferences.get( fieldEntry ); @@ -626,16 +721,14 @@ public class JarIndex { // for each key/value pair... Set> entriesToAdd = Sets.newHashSet(); - Iterator> iter = map.entries().iterator(); - while( iter.hasNext() ) + for( Map.Entry entry : map.entries() ) { - Map.Entry entry = iter.next(); - iter.remove(); entriesToAdd.add( new AbstractMap.SimpleEntry( renameClassesInThing( renames, entry.getKey() ), renameClassesInThing( renames, entry.getValue() ) ) ); } + map.clear(); for( Map.Entry entry : entriesToAdd ) { map.put( entry.getKey(), entry.getValue() ); @@ -761,5 +854,5 @@ public class JarIndex return thing; } return thing; - } + } } diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java new file mode 100644 index 00000000..b529f3f6 --- /dev/null +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class MethodImplementationsTreeNode extends DefaultMutableTreeNode +{ + private static final long serialVersionUID = 3781080657461899915L; + + private Translator m_deobfuscatingTranslator; + private MethodEntry m_entry; + + public MethodImplementationsTreeNode( Translator deobfuscatingTranslator, MethodEntry entry ) + { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + } + + public MethodEntry getMethodEntry( ) + { + return m_entry; + } + + public String getDeobfClassName( ) + { + return m_deobfuscatingTranslator.translateClass( m_entry.getClassName() ); + } + + public String getDeobfMethodName( ) + { + return m_deobfuscatingTranslator.translate( m_entry ); + } + + @Override + public String toString( ) + { + String className = getDeobfClassName(); + if( className == null ) + { + className = m_entry.getClassName(); + } + + String methodName = getDeobfMethodName(); + if( methodName == null ) + { + methodName = m_entry.getName(); + } + return className + "." + methodName + "()"; + } + + public void load( JarIndex index ) + { + // get all method implementations + List nodes = Lists.newArrayList(); + for( String implementingClassName : index.getAncestries().getImplementingClasses( m_entry.getClassName() ) ) + { + MethodEntry methodEntry = new MethodEntry( + new ClassEntry( implementingClassName ), + m_entry.getName(), + m_entry.getSignature() + ); + if( index.isMethodImplemented( methodEntry ) ) + { + nodes.add( new MethodImplementationsTreeNode( m_deobfuscatingTranslator, methodEntry ) ); + } + } + + // add them to this node + for( MethodImplementationsTreeNode node : nodes ) + { + this.add( node ); + } + } + + public static MethodImplementationsTreeNode findNode( MethodImplementationsTreeNode node, MethodEntry entry ) + { + // is this the node? + if( node.getMethodEntry().equals( entry ) ) + { + return node; + } + + // recurse + for( int i=0; i m_tokens; private JTabbedPane m_tabs; @@ -163,6 +166,7 @@ public class Gui private JMenuItem m_openEntryMenu; private JMenuItem m_openPreviousMenu; private JMenuItem m_showCallsMenu; + private JMenuItem m_showImplementationsMenu; // state private EntryReference m_reference; @@ -285,6 +289,10 @@ public class Gui showInheritance(); break; + case KeyEvent.VK_M: + showImplementations(); + break; + case KeyEvent.VK_N: openDeclaration(); break; @@ -337,6 +345,21 @@ public class Gui popupMenu.add( menu ); m_showInheritanceMenu = menu; } + { + JMenuItem menu = new JMenuItem( "Show Implementations" ); + menu.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + showImplementations(); + } + } ); + menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_M, 0 ) ); + menu.setEnabled( false ); + popupMenu.add( menu ); + m_showImplementationsMenu = menu; + } { JMenuItem menu = new JMenuItem( "Show Calls" ); menu.addActionListener( new ActionListener( ) @@ -428,6 +451,41 @@ public class Gui inheritancePanel.setLayout( new BorderLayout() ); inheritancePanel.add( new JScrollPane( m_inheritanceTree ) ); + // init implementations panel + m_implementationsTree = new JTree(); + m_implementationsTree.setModel( null ); + m_implementationsTree.addMouseListener( new MouseAdapter( ) + { + @Override + public void mouseClicked( MouseEvent event ) + { + if( event.getClickCount() == 2 ) + { + // get the selected node + TreePath path = m_implementationsTree.getSelectionPath(); + if( path == null ) + { + return; + } + + Object node = path.getLastPathComponent(); + if( node instanceof ClassImplementationsTreeNode ) + { + ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode)node; + m_controller.openDeclaration( classNode.getClassEntry() ); + } + else if( node instanceof MethodImplementationsTreeNode ) + { + MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode)node; + m_controller.openDeclaration( methodNode.getMethodEntry() ); + } + } + } + } ); + JPanel implementationsPanel = new JPanel(); + implementationsPanel.setLayout( new BorderLayout() ); + implementationsPanel.add( new JScrollPane( m_implementationsTree ) ); + // init call panel m_callsTree = new JTree(); m_callsTree.setModel( null ); @@ -501,6 +559,7 @@ public class Gui m_tabs = new JTabbedPane(); m_tabs.setPreferredSize( new Dimension( 250, 0 ) ); m_tabs.addTab( "Inheritance", inheritancePanel ); + m_tabs.addTab( "Implementations", implementationsPanel ); m_tabs.addTab( "Call Graph", callPanel ); JSplitPane splitRight = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, m_tabs ); splitRight.setResizeWeight( 1 ); // let the left side take all the slack @@ -1023,6 +1082,7 @@ public class Gui m_renameMenu.setEnabled( isToken ); m_showInheritanceMenu.setEnabled( isClassEntry || isMethodEntry || isConstructorEntry ); + m_showImplementationsMenu.setEnabled( isClassEntry || isMethodEntry ); m_showCallsMenu.setEnabled( isFieldEntry || isMethodEntry || isConstructorEntry ); m_openEntryMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ); m_openPreviousMenu.setEnabled( m_controller.hasPreviousLocation() ); @@ -1097,6 +1157,8 @@ public class Gui return; } + m_inheritanceTree.setModel( null ); + if( m_reference.entry instanceof ClassEntry ) { // get the class inheritance @@ -1124,6 +1186,46 @@ public class Gui redraw(); } + private void showImplementations( ) + { + if( m_reference == null ) + { + return; + } + + m_implementationsTree.setModel( null ); + + if( m_reference.entry instanceof ClassEntry ) + { + // get the class implementations + ClassImplementationsTreeNode node = m_controller.getClassImplementations( (ClassEntry)m_reference.entry ); + if( node != null ) + { + // show the tree at the root + TreePath path = getPathToRoot( node ); + m_implementationsTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) ); + m_implementationsTree.expandPath( path ); + m_implementationsTree.setSelectionRow( m_implementationsTree.getRowForPath( path ) ); + } + } + else if( m_reference.entry instanceof MethodEntry ) + { + // get the method implementations + MethodImplementationsTreeNode node = m_controller.getMethodImplementations( (MethodEntry)m_reference.entry ); + if( node != null ) + { + // show the tree at the root + TreePath path = getPathToRoot( node ); + m_implementationsTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) ); + m_implementationsTree.expandPath( path ); + m_implementationsTree.setSelectionRow( m_implementationsTree.getRowForPath( path ) ); + } + } + + m_tabs.setSelectedIndex( 1 ); + redraw(); + } + private void showCalls( ) { if( m_reference == null ) @@ -1147,7 +1249,7 @@ public class Gui m_callsTree.setModel( new DefaultTreeModel( node ) ); } - m_tabs.setSelectedIndex( 1 ); + m_tabs.setSelectedIndex( 2 ); redraw(); } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 90bce520..bd79e480 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -24,9 +24,11 @@ import com.strobel.decompiler.languages.java.ast.CompilationUnit; import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.FieldReferenceTreeNode; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.Token; @@ -182,6 +184,15 @@ public class GuiController return ClassInheritanceTreeNode.findNode( rootNode, obfClassEntry ); } + public ClassImplementationsTreeNode getClassImplementations( ClassEntry deobfClassEntry ) + { + ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry( deobfClassEntry ); + return m_deobfuscator.getJarIndex().getClassImplementations( + m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + obfClassEntry + ); + } + public MethodInheritanceTreeNode getMethodInheritance( MethodEntry deobfMethodEntry ) { MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry( deobfMethodEntry ); @@ -192,6 +203,20 @@ public class GuiController return MethodInheritanceTreeNode.findNode( rootNode, obfMethodEntry ); } + public MethodImplementationsTreeNode getMethodImplementations( MethodEntry deobfMethodEntry ) + { + MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry( deobfMethodEntry ); + MethodImplementationsTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodImplementations( + m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + obfMethodEntry + ); + if( rootNode == null ) + { + return null; + } + return MethodImplementationsTreeNode.findNode( rootNode, obfMethodEntry ); + } + public FieldReferenceTreeNode getFieldReferences( FieldEntry deobfFieldEntry ) { FieldEntry obfFieldEntry = m_deobfuscator.obfuscateEntry( deobfFieldEntry ); diff --git a/src/cuchaz/enigma/mapping/Renamer.java b/src/cuchaz/enigma/mapping/Renamer.java index 0bb8dc12..79cbd30d 100644 --- a/src/cuchaz/enigma/mapping/Renamer.java +++ b/src/cuchaz/enigma/mapping/Renamer.java @@ -16,7 +16,6 @@ import java.io.OutputStream; import java.util.zip.GZIPOutputStream; import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.analysis.MethodInheritanceTreeNode; public class Renamer { @@ -57,25 +56,9 @@ public class Renamer public void setMethodTreeName( MethodEntry obf, String deobfName ) { - // get the method tree - setMethodTreeName( - m_index.getMethodInheritance( m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ), obf ), - deobfName - ); - } - - private void setMethodTreeName( MethodInheritanceTreeNode node, String deobfName ) - { - if( node.isImplemented() ) - { - // apply the name here - setMethodName( node.getMethodEntry(), deobfName ); - } - - // recurse - for( int i=0; i renames = Maps.newHashMap(); for( Map.Entry entry : m_outerClasses.entrySet() ) { - renames.put( entry.getKey(), entry.getValue() + "$" + entry.getKey() ); + renames.put( entry.getKey(), entry.getValue() + "$" + new ClassEntry( entry.getKey() ).getSimpleName() ); } renameClasses( renames ); diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index fdb7c2c5..f87ddc1d 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -25,12 +25,17 @@ public class ClassEntry implements Entry, Serializable { throw new IllegalArgumentException( "Class name cannot be null!" ); } - if( className.contains( "." ) ) + if( className.indexOf( '.' ) >= 0 ) { throw new IllegalArgumentException( "Class name must be in JVM format. ie, path/to/package/class$inner" ); } m_name = className; + + if( isInnerClass() && getInnerClassName().indexOf( '/' ) >= 0 ) + { + throw new IllegalArgumentException( "Inner cast must not have a package: " + getInnerClassName() ); + } } public ClassEntry( ClassEntry other ) -- cgit v1.2.3 From 310f3f54f4f537c61b8c864467aa63f98af7b1c6 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 26 Aug 2014 23:56:55 -0400 Subject: packaged as v0.4 beta --- build.gradle | 2 +- readme.txt | 2 +- src/cuchaz/enigma/Constants.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index cb676140..77d1524a 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ targetCompatibility = 1.7 group = "com.cuchazinteractive" archivesBaseName = "enigma" -version = "0.3b" +version = "0.4b" sourceSets { diff --git a/readme.txt b/readme.txt index f1ed6d6d..7e81fcb5 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ -Enigma v0.3 beta +Enigma v0.4 beta A tool for deobfuscation of Java bytecode Copyright Jeff Martin, 2014 diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 1aa9e146..6f6ab315 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -14,7 +14,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.3 beta"; + public static final String Version = "0.4 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024*1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte -- cgit v1.2.3 -- cgit v1.2.3 From 88d6d655a022b8125355409bd88535da756e5c99 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 27 Aug 2014 00:57:06 -0400 Subject: add more checking to catch duplicate mappings on load implemented more stable sort order for methods repackaged as v0.4.1beta --- build.gradle | 2 +- readme.txt | 2 +- src/cuchaz/enigma/Constants.java | 2 +- src/cuchaz/enigma/Main.java | 3 +-- src/cuchaz/enigma/mapping/ClassMapping.java | 25 ++++++++++++++++++++++--- src/cuchaz/enigma/mapping/Mappings.java | 9 +++++++++ src/cuchaz/enigma/mapping/MethodMapping.java | 2 +- 7 files changed, 36 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index 77d1524a..6508e982 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ targetCompatibility = 1.7 group = "com.cuchazinteractive" archivesBaseName = "enigma" -version = "0.4b" +version = "0.4.1b" sourceSets { diff --git a/readme.txt b/readme.txt index 7e81fcb5..8e0edaaa 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ -Enigma v0.4 beta +Enigma v0.4.1 beta A tool for deobfuscation of Java bytecode Copyright Jeff Martin, 2014 diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 6f6ab315..e407dbd8 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -14,7 +14,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.4 beta"; + public static final String Version = "0.4.1 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024*1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index c69b8907..bbee9024 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -13,7 +13,6 @@ package cuchaz.enigma; import java.io.File; import cuchaz.enigma.gui.Gui; -import cuchaz.enigma.mapping.ClassEntry; public class Main { @@ -33,7 +32,7 @@ public class Main } // DEBUG - gui.getController().openDeclaration( new ClassEntry( "none/bsp" ) ); + //gui.getController().openDeclaration( new ClassEntry( "none/bsp" ) ); } private static File getFile( String path ) diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index c7f930c6..59365129 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -129,10 +129,18 @@ public class ClassMapping implements Serializable, Comparable protected void addFieldMapping( FieldMapping fieldMapping ) { + if( m_fieldsByObf.containsKey( fieldMapping.getObfName() ) ) + { + throw new Error( "Already have mapping for " + m_obfName + "." + fieldMapping.getObfName() ); + } + if( m_fieldsByDeobf.containsKey( fieldMapping.getDeobfName() ) ) + { + throw new Error( "Already have mapping for " + m_deobfName + "." + fieldMapping.getDeobfName() ); + } m_fieldsByObf.put( fieldMapping.getObfName(), fieldMapping ); m_fieldsByDeobf.put( fieldMapping.getDeobfName(), fieldMapping ); + assert( m_fieldsByObf.size() == m_fieldsByDeobf.size() ); } - public String getObfFieldName( String deobfName ) { @@ -179,8 +187,19 @@ public class ClassMapping implements Serializable, Comparable protected void addMethodMapping( MethodMapping methodMapping ) { - m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping ); - m_methodsByDeobf.put( getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ), methodMapping ); + String obfKey = getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ); + String deobfKey = getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ); + if( m_methodsByObf.containsKey( obfKey ) ) + { + throw new Error( "Already have mapping for " + m_obfName + "." + obfKey ); + } + if( m_methodsByDeobf.containsKey( deobfKey ) ) + { + throw new Error( "Already have mapping for " + m_deobfName + "." + deobfKey ); + } + m_methodsByObf.put( obfKey, methodMapping ); + m_methodsByDeobf.put( deobfKey, methodMapping ); + assert( m_methodsByObf.size() == m_methodsByDeobf.size() ); } public MethodMapping getMethodByObf( String obfName, String signature ) diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index c7cb6a67..f3b8fad1 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -70,8 +70,17 @@ public class Mappings implements Serializable protected void addClassMapping( ClassMapping classMapping ) { + if( m_classesByObf.containsKey( classMapping.getObfName() ) ) + { + throw new Error( "Already have mapping for " + classMapping.getObfName() ); + } + if( m_classesByDeobf.containsKey( classMapping.getDeobfName() ) ) + { + throw new Error( "Already have mapping for " + classMapping.getDeobfName() ); + } m_classesByObf.put( classMapping.getObfName(), classMapping ); m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ); + assert( m_classesByObf.size() == m_classesByDeobf.size() ); } public ClassMapping getClassByObf( ClassEntry entry ) diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index 7857ea7e..6e6bec46 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -137,6 +137,6 @@ public class MethodMapping implements Serializable, Comparable @Override public int compareTo( MethodMapping other ) { - return m_obfName.compareTo( other.m_obfName ); + return ( m_obfName + m_obfSignature ).compareTo( ( other.m_obfName + other.m_obfSignature ) ); } } -- cgit v1.2.3 From 392f63d073f78e0abb488ead6e3f458a7bb0f818 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 28 Aug 2014 00:56:31 -0400 Subject: fixed issue with method signatures in default package repackaged for 0.4.2 beta --- build.gradle | 2 +- readme.txt | 2 +- src/cuchaz/enigma/Constants.java | 2 +- src/cuchaz/enigma/mapping/MappingsReader.java | 30 +++++++++++++++++++++++---- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 6508e982..3cc975db 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ targetCompatibility = 1.7 group = "com.cuchazinteractive" archivesBaseName = "enigma" -version = "0.4.1b" +version = "0.4.2b" sourceSets { diff --git a/readme.txt b/readme.txt index 8e0edaaa..ca4d7a5c 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ -Enigma v0.4.1 beta +Enigma v0.4.2 beta A tool for deobfuscation of Java bytecode Copyright Jeff Martin, 2014 diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index e407dbd8..c8631429 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -14,7 +14,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.4.1 beta"; + public static final String Version = "0.4.2 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024*1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index 5570721f..9f42b42c 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -21,6 +21,7 @@ import com.google.common.collect.Queues; import cuchaz.enigma.Constants; import cuchaz.enigma.Util; +import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class MappingsReader { @@ -152,12 +153,12 @@ public class MappingsReader private ClassMapping readClass( Scanner scanner ) { return new ClassMapping( - moveOutOfDefaultPackage( scanner.next(), Constants.NonePackage ), - moveOutOfDefaultPackage( scanner.next(), Constants.NonePackage ) + moveClassOutOfDefaultPackage( scanner.next(), Constants.NonePackage ), + moveClassOutOfDefaultPackage( scanner.next(), Constants.NonePackage ) ); } - private String moveOutOfDefaultPackage( String className, String newPackageName ) + private String moveClassOutOfDefaultPackage( String className, String newPackageName ) { ClassEntry classEntry = new ClassEntry( className ); if( classEntry.isInDefaultPackage() ) @@ -174,6 +175,27 @@ public class MappingsReader private MethodMapping readMethod( Scanner scanner ) { - return new MethodMapping( scanner.next(), scanner.next(), scanner.next(), scanner.next() ); + return new MethodMapping( + scanner.next(), scanner.next(), + moveSignatureOutOfDefaultPackage( scanner.next(), Constants.NonePackage ), + moveSignatureOutOfDefaultPackage( scanner.next(), Constants.NonePackage ) + ); + } + + private String moveSignatureOutOfDefaultPackage( String signature, final String newPackageName ) + { + return SignatureUpdater.update( signature, new ClassNameUpdater( ) + { + @Override + public String update( String className ) + { + ClassEntry classEntry = new ClassEntry( className ); + if( classEntry.isInDefaultPackage() ) + { + return newPackageName + "/" + className; + } + return className; + } + } ); } } -- cgit v1.2.3 From deb2775d35f5a6d2b464782242e6b30c37279124 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 28 Aug 2014 10:12:24 -0400 Subject: added checks to find buggy mappings --- src/cuchaz/enigma/Deobfuscator.java | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 8a516b18..cc1465a2 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -41,6 +41,7 @@ import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.MethodMapping; import cuchaz.enigma.mapping.Renamer; import cuchaz.enigma.mapping.TranslationDirection; import cuchaz.enigma.mapping.Translator; @@ -101,6 +102,37 @@ public class Deobfuscator { val = new Mappings(); } + + // make sure all the mappings match the classes in the jar + for( ClassMapping classMapping : val.classes() ) + { + ClassEntry classEntry = new ClassEntry( classMapping.getObfName() ); + if( !m_jarIndex.getObfClassEntries().contains( classEntry ) ) + { + throw new Error( "Class " + classEntry + " not found in Jar!" ); + } + + // and method implementations + for( MethodMapping methodMapping : classMapping.methods() ) + { + if( methodMapping.getObfName().startsWith( "<" ) ) + { + // skip constructors and static initializers + continue; + } + + MethodEntry methodEntry = new MethodEntry( + classEntry, + methodMapping.getObfName(), + methodMapping.getObfSignature() + ); + if( !m_jarIndex.isMethodImplemented( methodEntry ) ) + { + throw new Error( "Method " + methodEntry + " not found in Jar!" ); + } + } + } + m_mappings = val; m_renamer = new Renamer( m_jarIndex, m_mappings ); -- cgit v1.2.3 From 3301d00ab1df7a0f88985d143787f9f3c2283e38 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 28 Aug 2014 10:43:48 -0400 Subject: added crash reporter --- src/cuchaz/enigma/gui/CrashDialog.java | 107 +++++++++++++++++++++++++++++++++ src/cuchaz/enigma/gui/Gui.java | 22 +++++-- 2 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 src/cuchaz/enigma/gui/CrashDialog.java diff --git a/src/cuchaz/enigma/gui/CrashDialog.java b/src/cuchaz/enigma/gui/CrashDialog.java new file mode 100644 index 00000000..501080ec --- /dev/null +++ b/src/cuchaz/enigma/gui/CrashDialog.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.WindowConstants; + +import cuchaz.enigma.Constants; + +public class CrashDialog +{ + private static CrashDialog m_instance = null; + + private JFrame m_frame; + private JTextArea m_text; + + private CrashDialog( JFrame parent ) + { + // init frame + m_frame = new JFrame( Constants.Name + " - Crash Report" ); + final Container pane = m_frame.getContentPane(); + pane.setLayout( new BorderLayout() ); + + JLabel label = new JLabel( Constants.Name + " has crashed! =(" ); + label.setBorder( BorderFactory.createEmptyBorder( 10, 10, 10, 10 ) ); + pane.add( label, BorderLayout.NORTH ); + + // report panel + m_text = new JTextArea(); + m_text.setTabSize( 2 ); + pane.add( new JScrollPane( m_text ), BorderLayout.CENTER ); + + // buttons panel + JPanel buttonsPanel = new JPanel(); + FlowLayout buttonsLayout = new FlowLayout(); + buttonsLayout.setAlignment( FlowLayout.RIGHT ); + buttonsPanel.setLayout( buttonsLayout ); + JButton ignoreButton = new JButton( "Ignore" ); + ignoreButton.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + // close (hide) the dialog + m_frame.setVisible( false ); + } + } ); + buttonsPanel.add( ignoreButton ); + JButton exitButton = new JButton( "Exit" ); + exitButton.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + // exit enigma + System.exit( 1 ); + } + } ); + buttonsPanel.add( exitButton ); + pane.add( buttonsPanel, BorderLayout.SOUTH ); + + // show the frame + m_frame.setSize( 600, 400 ); + m_frame.setLocationRelativeTo( parent ); + m_frame.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE ); + } + + public static void init( JFrame parent ) + { + m_instance = new CrashDialog( parent ); + } + + public static void show( Throwable ex ) + { + // get the error report + StringWriter buf = new StringWriter(); + ex.printStackTrace( new PrintWriter( buf ) ); + String report = buf.toString(); + + // show it! + m_instance.m_text.setText( report ); + m_instance.m_frame.doLayout(); + m_instance.m_frame.setVisible( true ); + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 3dcb4e24..e357382a 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -28,6 +28,7 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; +import java.lang.Thread.UncaughtExceptionHandler; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -176,6 +177,22 @@ public class Gui public Gui( ) { + // init frame + m_frame = new JFrame( Constants.Name ); + final Container pane = m_frame.getContentPane(); + pane.setLayout( new BorderLayout() ); + + // install a global exception handler to the event thread + CrashDialog.init( m_frame ); + Thread.setDefaultUncaughtExceptionHandler( new UncaughtExceptionHandler( ) + { + @Override + public void uncaughtException( Thread thread, Throwable ex ) + { + CrashDialog.show( ex ); + } + } ); + m_controller = new GuiController( this ); // init file choosers @@ -184,11 +201,6 @@ public class Gui m_exportFileChooser = new JFileChooser(); m_exportFileChooser.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY ); - // init frame - m_frame = new JFrame( Constants.Name ); - final Container pane = m_frame.getContentPane(); - pane.setLayout( new BorderLayout() ); - // init obfuscated classes list m_obfClasses = new JList(); m_obfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); -- cgit v1.2.3 From c1d64214212b43b43ea6a69a3b88eda8ac0e5006 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 28 Aug 2014 17:14:38 -0400 Subject: fixed minor spelling error --- src/cuchaz/enigma/gui/Gui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index e357382a..3ee8ade0 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -696,7 +696,7 @@ public class Gui m_saveMappingsAsMenu = item; } { - JMenuItem item = new JMenuItem( "Close Mapppings" ); + JMenuItem item = new JMenuItem( "Close Mappings" ); menu.add( item ); item.addActionListener( new ActionListener( ) { -- cgit v1.2.3 From 029f65d110279288f4cad7fb7cfaa33efd0f207d Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 28 Aug 2014 19:34:17 -0400 Subject: Show public/protected/private access on field/method/constructor references --- src/cuchaz/enigma/analysis/Access.java | 51 ++++++++++++++++++++++ .../enigma/analysis/BehaviorReferenceTreeNode.java | 8 ++-- .../enigma/analysis/FieldReferenceTreeNode.java | 12 ++--- src/cuchaz/enigma/analysis/JarIndex.java | 31 +++++++++++-- src/cuchaz/enigma/gui/Gui.java | 1 + 5 files changed, 92 insertions(+), 11 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/Access.java diff --git a/src/cuchaz/enigma/analysis/Access.java b/src/cuchaz/enigma/analysis/Access.java new file mode 100644 index 00000000..e35bb21b --- /dev/null +++ b/src/cuchaz/enigma/analysis/Access.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.lang.reflect.Modifier; + +import javassist.CtBehavior; +import javassist.CtField; + +public enum Access +{ + Public, + Protected, + Private; + + public static Access get( CtBehavior behavior ) + { + return get( behavior.getModifiers() ); + } + + public static Access get( CtField field ) + { + return get( field.getModifiers() ); + } + + public static Access get( int modifiers ) + { + if( Modifier.isPublic( modifiers ) ) + { + return Public; + } + else if( Modifier.isProtected( modifiers ) ) + { + return Protected; + } + else if( Modifier.isPrivate( modifiers ) ) + { + return Private; + } + // assume public by default + return Public; + } +} \ No newline at end of file diff --git a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java index 0f7e7f71..20f1d472 100644 --- a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java @@ -28,6 +28,7 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements private Translator m_deobfuscatingTranslator; private BehaviorEntry m_entry; private EntryReference m_reference; + private Access m_access; public BehaviorReferenceTreeNode( Translator deobfuscatingTranslator, BehaviorEntry entry ) { @@ -36,11 +37,12 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements m_reference = null; } - public BehaviorReferenceTreeNode( Translator deobfuscatingTranslator, EntryReference reference ) + public BehaviorReferenceTreeNode( Translator deobfuscatingTranslator, EntryReference reference, Access access ) { m_deobfuscatingTranslator = deobfuscatingTranslator; m_entry = reference.entry; m_reference = reference; + m_access = access; } @Override @@ -60,7 +62,7 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements { if( m_reference != null ) { - return m_deobfuscatingTranslator.translateEntry( m_reference.context ).toString(); + return String.format( "%s (%s)", m_deobfuscatingTranslator.translateEntry( m_reference.context ), m_access ); } return m_deobfuscatingTranslator.translateEntry( m_entry ).toString(); } @@ -70,7 +72,7 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements // get all the child nodes for( EntryReference reference : index.getBehaviorReferences( m_entry ) ) { - add( new BehaviorReferenceTreeNode( m_deobfuscatingTranslator, reference ) ); + add( new BehaviorReferenceTreeNode( m_deobfuscatingTranslator, reference, index.getAccess( m_entry ) ) ); } if( recurse && children != null ) diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java index 94d0da7e..2652f64a 100644 --- a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -23,7 +23,8 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re private Translator m_deobfuscatingTranslator; private FieldEntry m_entry; private EntryReference m_reference; - + private Access m_access; + public FieldReferenceTreeNode( Translator deobfuscatingTranslator, FieldEntry entry ) { m_deobfuscatingTranslator = deobfuscatingTranslator; @@ -31,11 +32,12 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re m_reference = null; } - private FieldReferenceTreeNode( Translator deobfuscatingTranslator, EntryReference reference ) + private FieldReferenceTreeNode( Translator deobfuscatingTranslator, EntryReference reference, Access access ) { m_deobfuscatingTranslator = deobfuscatingTranslator; m_entry = reference.entry; m_reference = reference; + m_access = access; } @Override @@ -55,7 +57,7 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re { if( m_reference != null ) { - return m_deobfuscatingTranslator.translateEntry( m_reference.context ).toString(); + return String.format( "%s (%s)", m_deobfuscatingTranslator.translateEntry( m_reference.context ), m_access ); } return m_deobfuscatingTranslator.translateEntry( m_entry ).toString(); } @@ -67,14 +69,14 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re { for( EntryReference reference : index.getFieldReferences( m_entry ) ) { - add( new FieldReferenceTreeNode( m_deobfuscatingTranslator, reference ) ); + add( new FieldReferenceTreeNode( m_deobfuscatingTranslator, reference, index.getAccess( m_entry ) ) ); } } else { for( EntryReference reference : index.getBehaviorReferences( m_reference.context ) ) { - add( new BehaviorReferenceTreeNode( m_deobfuscatingTranslator, reference ) ); + add( new BehaviorReferenceTreeNode( m_deobfuscatingTranslator, reference, index.getAccess( m_reference.context ) ) ); } } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 947453ef..deacf16d 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -23,6 +23,7 @@ import javassist.CannotCompileException; import javassist.CtBehavior; import javassist.CtClass; import javassist.CtConstructor; +import javassist.CtField; import javassist.CtMethod; import javassist.NotFoundException; import javassist.bytecode.AccessFlag; @@ -55,6 +56,7 @@ public class JarIndex { private Set m_obfClassEntries; private Ancestries m_ancestries; + private Map m_access; private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; private Multimap> m_fieldReferences; @@ -67,6 +69,7 @@ public class JarIndex { m_obfClassEntries = Sets.newHashSet(); m_ancestries = new Ancestries(); + m_access = Maps.newHashMap(); m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); m_fieldReferences = HashMultimap.create(); @@ -89,7 +92,24 @@ public class JarIndex m_obfClassEntries.add( classEntry ); } - // step 2: index the types, methods + // step 2: index method/field access + for( CtClass c : JarClassIterator.classes( jar ) ) + { + fixClass( c ); + ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); + for( CtField field : c.getDeclaredFields() ) + { + FieldEntry fieldEntry = new FieldEntry( classEntry, field.getName() ); + m_access.put( fieldEntry, Access.get( field ) ); + } + for( CtBehavior behavior : c.getDeclaredBehaviors() ) + { + MethodEntry methodEntry = new MethodEntry( classEntry, behavior.getName(), behavior.getSignature() ); + m_access.put( methodEntry, Access.get( behavior ) ); + } + } + + // step 3: index the types, methods for( CtClass c : JarClassIterator.classes( jar ) ) { fixClass( c ); @@ -105,7 +125,7 @@ public class JarIndex } } - // step 3: index inner classes and anonymous classes + // step 4: index inner classes and anonymous classes for( CtClass c : JarClassIterator.classes( jar ) ) { fixClass( c ); @@ -132,7 +152,7 @@ public class JarIndex } } - // step 4: update other indices with inner class info + // step 5: update other indices with inner class info Map renames = Maps.newHashMap(); for( Map.Entry entry : m_outerClasses.entrySet() ) { @@ -522,6 +542,11 @@ public class JarIndex return m_ancestries; } + public Access getAccess( Entry entry ) + { + return m_access.get( entry ); + } + public boolean isMethodImplemented( MethodEntry methodEntry ) { Collection implementations = m_methodImplementations.get( methodEntry.getClassName() ); diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 3ee8ade0..ec0f842a 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -189,6 +189,7 @@ public class Gui @Override public void uncaughtException( Thread thread, Throwable ex ) { + ex.printStackTrace( System.err ); CrashDialog.show( ex ); } } ); -- cgit v1.2.3 From 81767097df4a119489ae8cbd9c5d8265f54daf7b Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 29 Aug 2014 01:18:10 -0400 Subject: started on mapping converter tool so we can update to newer Minecraft jars --- src/cuchaz/enigma/Util.java | 6 + src/cuchaz/enigma/convert/ClassIdentity.java | 255 +++++++++++++++++++++++++++ src/cuchaz/enigma/convert/ClassMapper.java | 73 ++++++++ 3 files changed, 334 insertions(+) create mode 100644 src/cuchaz/enigma/convert/ClassIdentity.java create mode 100644 src/cuchaz/enigma/convert/ClassMapper.java diff --git a/src/cuchaz/enigma/Util.java b/src/cuchaz/enigma/Util.java index 3686ef02..678de546 100644 --- a/src/cuchaz/enigma/Util.java +++ b/src/cuchaz/enigma/Util.java @@ -19,6 +19,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; +import java.util.Arrays; import java.util.jar.JarFile; import javassist.CannotCompileException; @@ -31,6 +32,11 @@ import com.google.common.io.CharStreams; public class Util { public static int combineHashesOrdered( Object ... objs ) + { + return combineHashesOrdered( Arrays.asList( objs ) ); + } + + public static int combineHashesOrdered( Iterable objs ) { final int prime = 67; int result = 1; diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java new file mode 100644 index 00000000..aecf7fcb --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -0,0 +1,255 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.CtMethod; +import javassist.bytecode.BadBytecode; +import javassist.bytecode.CodeIterator; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.Opcode; + +import com.beust.jcommander.internal.Maps; +import com.beust.jcommander.internal.Sets; +import com.google.common.collect.Lists; + +import cuchaz.enigma.Util; +import cuchaz.enigma.bytecode.ConstPoolEditor; +import cuchaz.enigma.bytecode.InfoType; +import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.SignatureUpdater; +import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; + +public class ClassIdentity +{ + private ClassEntry m_classEntry; + private Set m_fields; + private Set m_methods; + private Set m_constructors; + private String m_staticInitializer; + + public ClassIdentity( CtClass c ) + { + m_classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); + m_fields = Sets.newHashSet(); + for( CtField field : c.getDeclaredFields() ) + { + m_fields.add( scrubSignature( scrubSignature( field.getSignature() ) ) ); + } + m_methods = Sets.newHashSet(); + for( CtMethod method : c.getDeclaredMethods() ) + { + m_methods.add( scrubSignature( method.getSignature() ) + "0x" + getBehaviorSignature( method ) ); + } + m_constructors = Sets.newHashSet(); + for( CtConstructor constructor : c.getDeclaredConstructors() ) + { + m_constructors.add( scrubSignature( constructor.getSignature() ) + "0x" + getBehaviorSignature( constructor ) ); + } + m_staticInitializer = ""; + if( c.getClassInitializer() != null ) + { + m_staticInitializer = getBehaviorSignature( c.getClassInitializer() ); + } + } + + public ClassEntry getClassEntry( ) + { + return m_classEntry; + } + + @Override + public String toString( ) + { + StringBuilder buf = new StringBuilder(); + buf.append( "class: " ); + buf.append( hashCode() ); + buf.append( "\n" ); + for( String field : m_fields ) + { + buf.append( "\tfield " ); + buf.append( field ); + buf.append( "\n" ); + } + for( String method : m_methods ) + { + buf.append( "\tmethod " ); + buf.append( method ); + buf.append( "\n" ); + } + for( String constructor : m_constructors ) + { + buf.append( "\tconstructor " ); + buf.append( constructor ); + buf.append( "\n" ); + } + if( m_staticInitializer.length() > 0 ) + { + buf.append( "\tinitializer " ); + buf.append( m_staticInitializer ); + buf.append( "\n" ); + } + return buf.toString(); + } + + private String scrubSignature( String signature ) + { + return SignatureUpdater.update( signature, new ClassNameUpdater( ) + { + private Map m_classNames = Maps.newHashMap(); + + @Override + public String update( String className ) + { + // does the class have a package? + if( className.indexOf( '/' ) >= 0 ) + { + return className; + } + + if( !m_classNames.containsKey( className ) ) + { + m_classNames.put( className, getNewClassName() ); + } + return m_classNames.get( className ); + } + + private String getNewClassName( ) + { + return String.format( "C%03d", m_classNames.size() ); + } + } ); + } + + private String getBehaviorSignature( CtBehavior behavior ) + { + try + { + // does this method have an implementation? + if( behavior.getMethodInfo().getCodeAttribute() == null ) + { + return "(none)"; + } + + // compute the hash from the opcodes + ConstPool constants = behavior.getMethodInfo().getConstPool(); + ConstPoolEditor editor = new ConstPoolEditor( constants ); + MessageDigest digest = MessageDigest.getInstance( "MD5" ); + CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); + while( iter.hasNext() ) + { + int pos = iter.next(); + + // update the hash with the opcode + int opcode = iter.byteAt( pos ); + digest.update( (byte)opcode ); + + // is there a constant value here? + int constIndex = -1; + switch( opcode ) + { + case Opcode.LDC: + constIndex = iter.byteAt( pos + 1 ); + break; + + case Opcode.LDC_W: + constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 ); + break; + + case Opcode.LDC2_W: + constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 ); + break; + } + + if( constIndex >= 0 ) + { + // update the hash with the constant value + ConstInfoAccessor item = editor.getItem( constIndex ); + if( item.getType() == InfoType.StringInfo ) + { + String val = constants.getStringInfo( constIndex ); + try + { + digest.update( val.getBytes( "UTF8" ) ); + } + catch( UnsupportedEncodingException ex ) + { + throw new Error( ex ); + } + } + } + } + + // convert the hash to a hex string + return toHex( digest.digest() ); + } + catch( BadBytecode | NoSuchAlgorithmException ex ) + { + throw new Error( ex ); + } + } + + private String toHex( byte[] bytes ) + { + // function taken from: + // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java + final char[] hexArray = "0123456789ABCDEF".toCharArray(); + char[] hexChars = new char[bytes.length * 2]; + for( int j = 0; j < bytes.length; j++ ) + { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String( hexChars ); + } + + @Override + public boolean equals( Object other ) + { + if( other instanceof ClassIdentity ) + { + return equals( (ClassIdentity)other ); + } + return false; + } + + public boolean equals( ClassIdentity other ) + { + return m_fields.equals( other.m_fields ) + && m_methods.equals( other.m_methods ) + && m_constructors.equals( other.m_constructors ) + && m_staticInitializer.equals( other.m_staticInitializer ); + } + + @Override + public int hashCode( ) + { + List objs = Lists.newArrayList(); + objs.addAll( m_fields ); + objs.addAll( m_methods ); + objs.addAll( m_constructors ); + objs.add( m_staticInitializer ); + return Util.combineHashesOrdered( objs ); + } +} diff --git a/src/cuchaz/enigma/convert/ClassMapper.java b/src/cuchaz/enigma/convert/ClassMapper.java new file mode 100644 index 00000000..a0d5a3f1 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassMapper.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.io.File; +import java.io.IOException; +import java.util.jar.JarFile; + +import javassist.CtClass; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; + +import cuchaz.enigma.analysis.JarClassIterator; + +public class ClassMapper +{ + public static void main( String[] args ) + throws IOException + { + // TEMP + JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); + JarFile toJar = new JarFile( new File( "input/1.8-pre2.jar" ) ); + + new ClassMapper( fromJar, toJar ); + } + + public ClassMapper( JarFile a, JarFile b ) + { + int numAClasses = JarClassIterator.getClassEntries( a ).size(); + int numBClasses = JarClassIterator.getClassEntries( b ).size(); + + // TEMP + System.out.println( "A classes: " + numAClasses ); + System.out.println( "B classes: " + numBClasses ); + + // compute the a classes + Multiset aclasses = HashMultiset.create(); + for( CtClass c : JarClassIterator.classes( a ) ) + { + ClassIdentity aclass = new ClassIdentity( c ); + aclasses.add( aclass ); + } + + int numMatches = 0; + + // match the b classes to the a classes + for( CtClass c : JarClassIterator.classes( b ) ) + { + ClassIdentity bclass = new ClassIdentity( c ); + if( aclasses.contains( bclass ) ) + { + numMatches++; + } + + // TEMP + //System.out.println( bclass ); + } + + // TEMP + System.out.println( String.format( "Class matches: %d/%d (missing %d)", + numMatches, aclasses.size(), aclasses.size() - numMatches + ) ); + } +} -- cgit v1.2.3 From 5ba8046f37b6998625a6ef4d61879950dbccfc4e Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 29 Aug 2014 15:41:30 -0400 Subject: working on version conversion --- src/cuchaz/enigma/Main.java | 2 +- src/cuchaz/enigma/convert/ClassMapper.java | 111 +++++++++++++++++++++-------- 2 files changed, 82 insertions(+), 31 deletions(-) diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index bbee9024..bbd734c6 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -32,7 +32,7 @@ public class Main } // DEBUG - //gui.getController().openDeclaration( new ClassEntry( "none/bsp" ) ); + //gui.getController().openDeclaration( new ClassEntry( "none/bub" ) ); } private static File getFile( String path ) diff --git a/src/cuchaz/enigma/convert/ClassMapper.java b/src/cuchaz/enigma/convert/ClassMapper.java index a0d5a3f1..5a16a1cb 100644 --- a/src/cuchaz/enigma/convert/ClassMapper.java +++ b/src/cuchaz/enigma/convert/ClassMapper.java @@ -12,17 +12,31 @@ package cuchaz.enigma.convert; import java.io.File; import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; import java.util.jar.JarFile; import javassist.CtClass; -import com.google.common.collect.HashMultiset; -import com.google.common.collect.Multiset; +import com.beust.jcommander.internal.Lists; +import com.beust.jcommander.internal.Maps; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; import cuchaz.enigma.analysis.JarClassIterator; +import cuchaz.enigma.mapping.ClassEntry; public class ClassMapper { + private int m_numSourceClasses; + private int m_numDestClasses; + private Multimap m_sourceClasses; + private Multimap m_destClasses; + private List m_unmatchedSourceClasses; + private List m_unmatchedDestClasses; + private Map m_sourceKeyIndex; + public static void main( String[] args ) throws IOException { @@ -30,44 +44,81 @@ public class ClassMapper JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); JarFile toJar = new JarFile( new File( "input/1.8-pre2.jar" ) ); - new ClassMapper( fromJar, toJar ); + ClassMapper mapper = new ClassMapper( fromJar, toJar ); + System.out.println( String.format( "Mapped %d/%d source classes (%d unmatched) to %d/%d dest classes (%d unmatched)", + mapper.m_sourceClasses.size(), mapper.m_numSourceClasses, mapper.m_unmatchedSourceClasses.size(), + mapper.m_destClasses.size(), mapper.m_numDestClasses, mapper.m_unmatchedDestClasses.size() + ) ); } - public ClassMapper( JarFile a, JarFile b ) + public ClassMapper( JarFile sourceJar, JarFile destJar ) { - int numAClasses = JarClassIterator.getClassEntries( a ).size(); - int numBClasses = JarClassIterator.getClassEntries( b ).size(); - - // TEMP - System.out.println( "A classes: " + numAClasses ); - System.out.println( "B classes: " + numBClasses ); + m_numSourceClasses = JarClassIterator.getClassEntries( sourceJar ).size(); + m_numDestClasses = JarClassIterator.getClassEntries( destJar ).size(); - // compute the a classes - Multiset aclasses = HashMultiset.create(); - for( CtClass c : JarClassIterator.classes( a ) ) + // compute identities for the source classes + m_sourceClasses = ArrayListMultimap.create(); + m_sourceKeyIndex = Maps.newHashMap(); + for( CtClass c : JarClassIterator.classes( sourceJar ) ) { - ClassIdentity aclass = new ClassIdentity( c ); - aclasses.add( aclass ); + ClassIdentity sourceClass = new ClassIdentity( c ); + m_sourceClasses.put( sourceClass, sourceClass ); + m_sourceKeyIndex.put( sourceClass.getClassEntry(), sourceClass ); } - int numMatches = 0; - - // match the b classes to the a classes - for( CtClass c : JarClassIterator.classes( b ) ) + // match the dest classes to the source classes + m_destClasses = ArrayListMultimap.create(); + m_unmatchedDestClasses = Lists.newArrayList(); + for( CtClass c : JarClassIterator.classes( destJar ) ) { - ClassIdentity bclass = new ClassIdentity( c ); - if( aclasses.contains( bclass ) ) + ClassIdentity destClass = new ClassIdentity( c ); + Collection matchedSourceClasses = m_sourceClasses.get( destClass ); + if( matchedSourceClasses.isEmpty() ) + { + // unmatched dest class + m_unmatchedDestClasses.add( destClass ); + } + else { - numMatches++; + ClassIdentity sourceClass = matchedSourceClasses.iterator().next(); + m_destClasses.put( sourceClass, destClass ); } - - // TEMP - //System.out.println( bclass ); } - - // TEMP - System.out.println( String.format( "Class matches: %d/%d (missing %d)", - numMatches, aclasses.size(), aclasses.size() - numMatches - ) ); + + // get unmatched source classes + m_unmatchedSourceClasses = Lists.newArrayList(); + for( ClassIdentity sourceClass : m_sourceClasses.keySet() ) + { + Collection matchedSourceClasses = m_sourceClasses.get( sourceClass ); + Collection matchedDestClasses = m_destClasses.get( sourceClass ); + if( matchedDestClasses.isEmpty() ) + { + m_unmatchedSourceClasses.add( sourceClass ); + } + else if( matchedDestClasses.size() > 1 ) + { + // warn about identity collisions + System.err.println( String.format( "WARNING: identity collision:\n\tSource: %s\n\t Dest: %s", + getClassEntries( matchedSourceClasses ), + getClassEntries( matchedDestClasses ) + ) ); + } + } + } + + public Map.Entry,Collection> getMapping( ClassEntry sourceEntry ) + { + // TODO + return null; + } + + private Collection getClassEntries( Collection classes ) + { + List entries = Lists.newArrayList(); + for( ClassIdentity c : classes ) + { + entries.add( c.getClassEntry() ); + } + return entries; } } -- cgit v1.2.3 From e43fac9f55cfeebacd869352bfb090b7d8d063c1 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 30 Aug 2014 11:41:17 -0400 Subject: got a decent class matcher working --- src/cuchaz/enigma/TranslatingTypeLoader.java | 27 +++ src/cuchaz/enigma/analysis/JarClassIterator.java | 72 ++++--- src/cuchaz/enigma/analysis/JarIndex.java | 34 +-- src/cuchaz/enigma/bytecode/ClassRenamer.java | 35 ++++ src/cuchaz/enigma/convert/ClassIdentity.java | 255 ++++++++++++++++++++--- src/cuchaz/enigma/convert/ClassMapper.java | 202 +++++++++++------- src/cuchaz/enigma/convert/ClassMatching.java | 184 ++++++++++++++++ src/cuchaz/enigma/convert/ClassNamer.java | 75 +++++++ src/cuchaz/enigma/mapping/Translator.java | 9 + 9 files changed, 748 insertions(+), 145 deletions(-) create mode 100644 src/cuchaz/enigma/convert/ClassMatching.java create mode 100644 src/cuchaz/enigma/convert/ClassNamer.java diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 1e0e95a2..e70093eb 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -47,6 +47,11 @@ public class TranslatingTypeLoader implements ITypeLoader private Map m_cache; private ClasspathTypeLoader m_defaultTypeLoader; + public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex ) + { + this( jar, jarIndex, new Translator(), new Translator() ); + } + public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator ) { m_jar = jar; @@ -90,6 +95,28 @@ public class TranslatingTypeLoader implements ITypeLoader return true; } + public CtClass loadClass( String deobfClassName ) + { + byte[] data = loadType( deobfClassName ); + if( data == null ) + { + return null; + } + + // return a javassist handle for the class + String javaClassFileName = Descriptor.toJavaName( deobfClassName ); + ClassPool classPool = new ClassPool(); + classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, data ) ); + try + { + return classPool.get( javaClassFileName ); + } + catch( NotFoundException ex ) + { + throw new Error( ex ); + } + } + private byte[] loadType( String deobfClassName ) { // what class file should we actually load? diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java index 6c9f1245..10ae8052 100644 --- a/src/cuchaz/enigma/analysis/JarClassIterator.java +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -67,33 +67,7 @@ public class JarClassIterator implements Iterator JarEntry entry = m_iter.next(); try { - // read the class into a buffer - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - byte[] buf = new byte[Constants.KiB]; - int totalNumBytesRead = 0; - InputStream in = m_jar.getInputStream( entry ); - while( in.available() > 0 ) - { - int numBytesRead = in.read( buf ); - if( numBytesRead < 0 ) - { - break; - } - bos.write( buf, 0, numBytesRead ); - - // sanity checking - totalNumBytesRead += numBytesRead; - if( totalNumBytesRead > Constants.MiB ) - { - throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" ); - } - } - - // get a javassist handle for the class - String className = Descriptor.toJavaName( getClassEntry( entry ).getName() ); - ClassPool classPool = new ClassPool(); - classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); - return classPool.get( className ); + return getClass( m_jar, entry ); } catch( IOException | NotFoundException ex ) { @@ -136,6 +110,50 @@ public class JarClassIterator implements Iterator }; } + public static CtClass getClass( JarFile jar, ClassEntry classEntry ) + { + try + { + return getClass( jar, new JarEntry( classEntry.getName() + ".class" ) ); + } + catch( IOException | NotFoundException ex ) + { + throw new Error( "Unable to load class: " + classEntry.getName() ); + } + } + + private static CtClass getClass( JarFile jar, JarEntry entry ) + throws IOException, NotFoundException + { + // read the class into a buffer + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buf = new byte[Constants.KiB]; + int totalNumBytesRead = 0; + InputStream in = jar.getInputStream( entry ); + while( in.available() > 0 ) + { + int numBytesRead = in.read( buf ); + if( numBytesRead < 0 ) + { + break; + } + bos.write( buf, 0, numBytesRead ); + + // sanity checking + totalNumBytesRead += numBytesRead; + if( totalNumBytesRead > Constants.MiB ) + { + throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" ); + } + } + + // get a javassist handle for the class + String className = Descriptor.toJavaName( getClassEntry( entry ).getName() ); + ClassPool classPool = new ClassPool(); + classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); + return classPool.get( className ); + } + private static ClassEntry getClassEntry( JarEntry entry ) { return new ClassEntry( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index deacf16d..b479b69e 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -95,7 +95,7 @@ public class JarIndex // step 2: index method/field access for( CtClass c : JarClassIterator.classes( jar ) ) { - fixClass( c ); + ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); for( CtField field : c.getDeclaredFields() ) { @@ -112,7 +112,7 @@ public class JarIndex // step 3: index the types, methods for( CtClass c : JarClassIterator.classes( jar ) ) { - fixClass( c ); + ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); String className = Descriptor.toJvmName( c.getName() ); m_ancestries.addSuperclass( className, Descriptor.toJvmName( c.getClassFile().getSuperclass() ) ); for( String interfaceName : c.getClassFile().getInterfaces() ) @@ -128,8 +128,7 @@ public class JarIndex // step 4: index inner classes and anonymous classes for( CtClass c : JarClassIterator.classes( jar ) ) { - fixClass( c ); - + ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); String outerClassName = findOuterClass( c ); if( outerClassName != null ) { @@ -164,17 +163,6 @@ public class JarIndex renameMethods( m_bridgeMethods ); } - private void fixClass( CtClass c ) - { - ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); - if( classEntry.isInDefaultPackage() ) - { - // move class out of default package - classEntry = new ClassEntry( Constants.NonePackage + "/" + classEntry.getName() ); - ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); - } - } - private void indexBehavior( CtBehavior behavior ) { // get the method entry @@ -729,6 +717,22 @@ public class JarIndex private void renameClasses( Map renames ) { + // rename class entries + Set obfClassEntries = Sets.newHashSet(); + for( ClassEntry classEntry : m_obfClassEntries ) + { + if( renames.containsKey( classEntry.getName() ) ) + { + obfClassEntries.add( new ClassEntry( renames.get( classEntry.getName() ) ) ); + } + else + { + obfClassEntries.add( classEntry ); + } + } + m_obfClassEntries = obfClassEntries; + + // rename others m_ancestries.renameClasses( renames ); renameClassesInMultimap( renames, m_methodImplementations ); renameClassesInMultimap( renames, m_behaviorReferences ); diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index f3a8c0ef..efe22a18 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -14,6 +14,7 @@ import java.util.Map; import java.util.Set; import javassist.ClassMap; +import javassist.CtBehavior; import javassist.CtClass; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; @@ -23,6 +24,8 @@ import com.beust.jcommander.internal.Sets; import com.google.common.collect.Maps; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.SignatureUpdater; +import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class ClassRenamer { @@ -115,5 +118,37 @@ public class ClassRenamer } } ClassRenamer.renameClasses( c, map ); + + // TEMP + for( ClassEntry classEntry : ClassRenamer.getAllClassEntries( c ) ) + { + if( classEntry.isInDefaultPackage() ) + { + throw new Error( "!!! " + classEntry ); + } + } + + // TEMP + for( CtBehavior behavior : c.getDeclaredBehaviors() ) + { + if( behavior.getSignature() == null ) + { + continue; + } + + SignatureUpdater.update( behavior.getSignature(), new ClassNameUpdater( ) + { + @Override + public String update( String className ) + { + ClassEntry classEntry = new ClassEntry( className ); + if( classEntry.isInDefaultPackage() ) + { + throw new Error( "!!! " + className ); + } + return className; + } + } ); + } } } diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index aecf7fcb..0a3a4497 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -13,10 +13,12 @@ package cuchaz.enigma.convert; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.Set; +import javassist.CannotCompileException; import javassist.CtBehavior; import javassist.CtClass; import javassist.CtConstructor; @@ -27,34 +29,58 @@ import javassist.bytecode.CodeIterator; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.Opcode; +import javassist.expr.ConstructorCall; +import javassist.expr.ExprEditor; +import javassist.expr.FieldAccess; +import javassist.expr.MethodCall; +import javassist.expr.NewExpr; import com.beust.jcommander.internal.Maps; import com.beust.jcommander.internal.Sets; import com.google.common.collect.Lists; +import cuchaz.enigma.Constants; import cuchaz.enigma.Util; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.bytecode.ConstPoolEditor; import cuchaz.enigma.bytecode.InfoType; import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; +import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; +import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.SignatureUpdater; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class ClassIdentity { private ClassEntry m_classEntry; + private SidedClassNamer m_namer; private Set m_fields; private Set m_methods; private Set m_constructors; private String m_staticInitializer; + private String m_extends; + private Set m_implements; + private Set m_implementations; + private Set m_references; - public ClassIdentity( CtClass c ) + public ClassIdentity( CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences ) { + m_namer = namer; + + // stuff from the bytecode + m_classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); m_fields = Sets.newHashSet(); for( CtField field : c.getDeclaredFields() ) { - m_fields.add( scrubSignature( scrubSignature( field.getSignature() ) ) ); + m_fields.add( scrubSignature( field.getSignature() ) ); } m_methods = Sets.newHashSet(); for( CtMethod method : c.getDeclaredMethods() ) @@ -71,6 +97,73 @@ public class ClassIdentity { m_staticInitializer = getBehaviorSignature( c.getClassInitializer() ); } + m_extends = ""; + if( c.getClassFile().getSuperclass() != null ) + { + m_extends = scrubClassName( c.getClassFile().getSuperclass() ); + } + m_implements = Sets.newHashSet(); + for( String interfaceName : c.getClassFile().getInterfaces() ) + { + m_implements.add( scrubClassName( interfaceName ) ); + } + + // stuff from the jar index + + m_implementations = Sets.newHashSet(); + @SuppressWarnings( "unchecked" ) + Enumeration implementations = index.getClassImplementations( null, m_classEntry ).children(); + while( implementations.hasMoreElements() ) + { + ClassImplementationsTreeNode node = implementations.nextElement(); + m_implementations.add( scrubClassName( node.getClassEntry().getName() ) ); + } + + m_references = Sets.newHashSet(); + if( useReferences ) + { + for( CtField field : c.getDeclaredFields() ) + { + FieldEntry fieldEntry = new FieldEntry( m_classEntry, field.getName() ); + for( EntryReference reference : index.getFieldReferences( fieldEntry ) ) + { + addReference( reference ); + } + } + for( CtMethod method : c.getDeclaredMethods() ) + { + MethodEntry methodEntry = new MethodEntry( m_classEntry, method.getName(), method.getSignature() ); + for( EntryReference reference : index.getBehaviorReferences( methodEntry ) ) + { + addReference( reference ); + } + } + for( CtConstructor constructor : c.getDeclaredConstructors() ) + { + ConstructorEntry constructorEntry = new ConstructorEntry( m_classEntry, constructor.getSignature() ); + for( EntryReference reference : index.getBehaviorReferences( constructorEntry ) ) + { + addReference( reference ); + } + } + } + } + + private void addReference( EntryReference reference ) + { + if( reference.context.getSignature() != null ) + { + m_references.add( String.format( "%s_%s", + scrubClassName( reference.context.getClassName() ), + scrubSignature( reference.context.getSignature() ) + ) ); + } + else + { + m_references.add( String.format( "%s_", + scrubClassName( reference.context.getClassName() ) + ) ); + } } public ClassEntry getClassEntry( ) @@ -109,9 +202,38 @@ public class ClassIdentity buf.append( m_staticInitializer ); buf.append( "\n" ); } + if( m_extends.length() > 0 ) + { + buf.append( "\textends " ); + buf.append( m_extends ); + buf.append( "\n" ); + } + for( String interfaceName : m_implements ) + { + buf.append( "\timplements " ); + buf.append( interfaceName ); + buf.append( "\n" ); + } + for( String implementation : m_implementations ) + { + buf.append( "\timplemented by " ); + buf.append( implementation ); + buf.append( "\n" ); + } + for( String reference : m_references ) + { + buf.append( "\treference " ); + buf.append( reference ); + buf.append( "\n" ); + } return buf.toString(); } + private String scrubClassName( String className ) + { + return scrubSignature( "L" + Descriptor.toJvmName( className ) + ";" ); + } + private String scrubSignature( String signature ) { return SignatureUpdater.update( signature, new ClassNameUpdater( ) @@ -121,12 +243,30 @@ public class ClassIdentity @Override public String update( String className ) { - // does the class have a package? - if( className.indexOf( '/' ) >= 0 ) + // classes not in the none package can be passed through + ClassEntry classEntry = new ClassEntry( className ); + if( !classEntry.getPackageName().equals( Constants.NonePackage ) ) { return className; } + // is this class ourself? + if( className.equals( m_classEntry.getName() ) ) + { + return "CSelf"; + } + + // try the namer + if( m_namer != null ) + { + String newName = m_namer.getName( className ); + if( newName != null ) + { + return newName; + } + } + + // otherwise, use local naming if( !m_classNames.containsKey( className ) ) { m_classNames.put( className, getNewClassName() ); @@ -141,6 +281,11 @@ public class ClassIdentity } ); } + private boolean isClassMatchedUniquely( String className ) + { + return m_namer != null && m_namer.getName( Descriptor.toJvmName( className ) ) != null; + } + private String getBehaviorSignature( CtBehavior behavior ) { try @@ -153,8 +298,7 @@ public class ClassIdentity // compute the hash from the opcodes ConstPool constants = behavior.getMethodInfo().getConstPool(); - ConstPoolEditor editor = new ConstPoolEditor( constants ); - MessageDigest digest = MessageDigest.getInstance( "MD5" ); + final MessageDigest digest = MessageDigest.getInstance( "MD5" ); CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); while( iter.hasNext() ) { @@ -164,46 +308,91 @@ public class ClassIdentity int opcode = iter.byteAt( pos ); digest.update( (byte)opcode ); - // is there a constant value here? - int constIndex = -1; switch( opcode ) { case Opcode.LDC: - constIndex = iter.byteAt( pos + 1 ); + { + int constIndex = iter.byteAt( pos + 1 ); + updateHashWithConstant( digest, constants, constIndex ); + } break; case Opcode.LDC_W: - constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 ); - break; - case Opcode.LDC2_W: - constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 ); + { + int constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 ); + updateHashWithConstant( digest, constants, constIndex ); + } break; } + } + + // update hash with method and field accesses + behavior.instrument( new ExprEditor( ) + { + @Override + public void edit( MethodCall call ) + { + updateHashWithString( digest, scrubClassName( call.getClassName() ) ); + updateHashWithString( digest, scrubSignature( call.getSignature() ) ); + if( isClassMatchedUniquely( call.getClassName() ) ) + { + updateHashWithString( digest, call.getMethodName() ); + } + } - if( constIndex >= 0 ) + @Override + public void edit( FieldAccess access ) { - // update the hash with the constant value - ConstInfoAccessor item = editor.getItem( constIndex ); - if( item.getType() == InfoType.StringInfo ) + updateHashWithString( digest, scrubClassName( access.getClassName() ) ); + updateHashWithString( digest, scrubSignature( access.getSignature() ) ); + if( isClassMatchedUniquely( access.getClassName() ) ) { - String val = constants.getStringInfo( constIndex ); - try - { - digest.update( val.getBytes( "UTF8" ) ); - } - catch( UnsupportedEncodingException ex ) - { - throw new Error( ex ); - } + updateHashWithString( digest, access.getFieldName() ); } } - } + + @Override + public void edit( ConstructorCall call ) + { + updateHashWithString( digest, scrubClassName( call.getClassName() ) ); + updateHashWithString( digest, scrubSignature( call.getSignature() ) ); + } + + @Override + public void edit( NewExpr expr ) + { + updateHashWithString( digest, scrubClassName( expr.getClassName() ) ); + } + } ); // convert the hash to a hex string return toHex( digest.digest() ); } - catch( BadBytecode | NoSuchAlgorithmException ex ) + catch( BadBytecode | NoSuchAlgorithmException | CannotCompileException ex ) + { + throw new Error( ex ); + } + } + + private void updateHashWithConstant( MessageDigest digest, ConstPool constants, int index ) + { + ConstPoolEditor editor = new ConstPoolEditor( constants ); + ConstInfoAccessor item = editor.getItem( index ); + if( item.getType() == InfoType.StringInfo ) + { + updateHashWithString( digest, constants.getStringInfo( index ) ); + } + // TODO: other constants + } + + private void updateHashWithString( MessageDigest digest, String val ) + { + try + { + digest.update( val.getBytes( "UTF8" ) ); + } + catch( UnsupportedEncodingException ex ) { throw new Error( ex ); } @@ -239,7 +428,11 @@ public class ClassIdentity return m_fields.equals( other.m_fields ) && m_methods.equals( other.m_methods ) && m_constructors.equals( other.m_constructors ) - && m_staticInitializer.equals( other.m_staticInitializer ); + && m_staticInitializer.equals( other.m_staticInitializer ) + && m_extends.equals( other.m_extends ) + && m_implements.equals( other.m_implements ) + && m_implementations.equals( other.m_implementations ) + && m_references.equals( other.m_references ); } @Override @@ -250,6 +443,10 @@ public class ClassIdentity objs.addAll( m_methods ); objs.addAll( m_constructors ); objs.add( m_staticInitializer ); + objs.add( m_extends ); + objs.addAll( m_implements ); + objs.addAll( m_implementations ); + objs.addAll( m_references ); return Util.combineHashesOrdered( objs ); } } diff --git a/src/cuchaz/enigma/convert/ClassMapper.java b/src/cuchaz/enigma/convert/ClassMapper.java index 5a16a1cb..fe48c505 100644 --- a/src/cuchaz/enigma/convert/ClassMapper.java +++ b/src/cuchaz/enigma/convert/ClassMapper.java @@ -12,31 +12,20 @@ package cuchaz.enigma.convert; import java.io.File; import java.io.IOException; -import java.util.Collection; +import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.jar.JarFile; import javassist.CtClass; - -import com.beust.jcommander.internal.Lists; -import com.beust.jcommander.internal.Maps; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; - -import cuchaz.enigma.analysis.JarClassIterator; +import cuchaz.enigma.TranslatingTypeLoader; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; import cuchaz.enigma.mapping.ClassEntry; public class ClassMapper { - private int m_numSourceClasses; - private int m_numDestClasses; - private Multimap m_sourceClasses; - private Multimap m_destClasses; - private List m_unmatchedSourceClasses; - private List m_unmatchedDestClasses; - private Map m_sourceKeyIndex; - public static void main( String[] args ) throws IOException { @@ -44,81 +33,146 @@ public class ClassMapper JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); JarFile toJar = new JarFile( new File( "input/1.8-pre2.jar" ) ); - ClassMapper mapper = new ClassMapper( fromJar, toJar ); - System.out.println( String.format( "Mapped %d/%d source classes (%d unmatched) to %d/%d dest classes (%d unmatched)", - mapper.m_sourceClasses.size(), mapper.m_numSourceClasses, mapper.m_unmatchedSourceClasses.size(), - mapper.m_destClasses.size(), mapper.m_numDestClasses, mapper.m_unmatchedDestClasses.size() - ) ); + // compute the matching + ClassMatching matching = ClassMapper.computeMatching( fromJar, toJar ); + + // TODO: use the matching to convert the mappings } - public ClassMapper( JarFile sourceJar, JarFile destJar ) + public static ClassMatching computeMatching( JarFile sourceJar, JarFile destJar ) { - m_numSourceClasses = JarClassIterator.getClassEntries( sourceJar ).size(); - m_numDestClasses = JarClassIterator.getClassEntries( destJar ).size(); + // index jars + System.out.println( "Indexing source jar..." ); + JarIndex sourceIndex = new JarIndex(); + sourceIndex.indexJar( sourceJar ); + System.out.println( "Indexing dest jar..." ); + JarIndex destIndex = new JarIndex(); + destIndex.indexJar( destJar ); - // compute identities for the source classes - m_sourceClasses = ArrayListMultimap.create(); - m_sourceKeyIndex = Maps.newHashMap(); - for( CtClass c : JarClassIterator.classes( sourceJar ) ) - { - ClassIdentity sourceClass = new ClassIdentity( c ); - m_sourceClasses.put( sourceClass, sourceClass ); - m_sourceKeyIndex.put( sourceClass.getClassEntry(), sourceClass ); - } + System.out.println( "Computing matching..." ); - // match the dest classes to the source classes - m_destClasses = ArrayListMultimap.create(); - m_unmatchedDestClasses = Lists.newArrayList(); - for( CtClass c : JarClassIterator.classes( destJar ) ) + TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader( sourceJar, sourceIndex ); + TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex ); + + ClassMatching matching = null; + for( boolean useReferences : Arrays.asList( false, true ) ) { - ClassIdentity destClass = new ClassIdentity( c ); - Collection matchedSourceClasses = m_sourceClasses.get( destClass ); - if( matchedSourceClasses.isEmpty() ) - { - // unmatched dest class - m_unmatchedDestClasses.add( destClass ); - } - else + int numMatches = 0; + do { - ClassIdentity sourceClass = matchedSourceClasses.iterator().next(); - m_destClasses.put( sourceClass, destClass ); + SidedClassNamer sourceNamer = null; + SidedClassNamer destNamer = null; + if( matching != null ) + { + // build a class namer + ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); + sourceNamer = namer.getSourceNamer(); + destNamer = namer.getDestNamer(); + + // note the number of matches + numMatches = matching.getUniqueMatches().size(); + } + + // get the entries left to match + Set sourceClassEntries = sourceIndex.getObfClassEntries(); + Set destClassEntries = destIndex.getObfClassEntries(); + if( matching != null ) + { + sourceClassEntries.clear(); + destClassEntries.clear(); + for( Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet() ) + { + for( ClassIdentity c : entry.getKey() ) + { + sourceClassEntries.add( c.getClassEntry() ); + matching.removeSource( c ); + } + for( ClassIdentity c : entry.getValue() ) + { + destClassEntries.add( c.getClassEntry() ); + matching.removeDest( c ); + } + } + for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) + { + sourceClassEntries.add( c.getClassEntry() ); + matching.removeSource( c ); + } + for( ClassIdentity c : matching.getUnmatchedDestClasses() ) + { + destClassEntries.add( c.getClassEntry() ); + matching.removeDest( c ); + } + } + else + { + matching = new ClassMatching(); + } + + // compute a matching for the classes + for( ClassEntry classEntry : sourceClassEntries ) + { + CtClass c = sourceLoader.loadClass( classEntry.getName() ); + ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences ); + matching.addSource( sourceClass ); + } + for( ClassEntry classEntry : destClassEntries ) + { + CtClass c = destLoader.loadClass( classEntry.getName() ); + ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences ); + matching.matchDestClass( destClass ); + } + + // TEMP + System.out.println( matching ); } + while( matching.getUniqueMatches().size() - numMatches > 0 ); } - - // get unmatched source classes - m_unmatchedSourceClasses = Lists.newArrayList(); - for( ClassIdentity sourceClass : m_sourceClasses.keySet() ) + + /* DEBUG: show some ambiguous matches + List,List>> ambiguousMatches = new ArrayList,List>>( matching.getAmbiguousMatches().entrySet() ); + Collections.sort( ambiguousMatches, new Comparator,List>>( ) { - Collection matchedSourceClasses = m_sourceClasses.get( sourceClass ); - Collection matchedDestClasses = m_destClasses.get( sourceClass ); - if( matchedDestClasses.isEmpty() ) + @Override + public int compare( Map.Entry,List> a, Map.Entry,List> b ) { - m_unmatchedSourceClasses.add( sourceClass ); + String aName = a.getKey().get( 0 ).getClassEntry().getName(); + String bName = b.getKey().get( 0 ).getClassEntry().getName(); + return aName.compareTo( bName ); } - else if( matchedDestClasses.size() > 1 ) + } ); + for( Map.Entry,List> entry : ambiguousMatches ) + { + for( ClassIdentity c : entry.getKey() ) { - // warn about identity collisions - System.err.println( String.format( "WARNING: identity collision:\n\tSource: %s\n\t Dest: %s", - getClassEntries( matchedSourceClasses ), - getClassEntries( matchedDestClasses ) - ) ); + System.out.print( c.getClassEntry().getName() + " " ); } + System.out.println(); + } + Map.Entry,List> entry = ambiguousMatches.get( 7 ); + for( ClassIdentity c : entry.getKey() ) + { + System.out.println( c ); } + for( ClassIdentity c : entry.getKey() ) + { + System.out.println( decompile( sourceLoader, c.getClassEntry() ) ); + } + */ + + return matching; } - public Map.Entry,Collection> getMapping( ClassEntry sourceEntry ) + /* DEBUG + private static String decompile( TranslatingTypeLoader loader, ClassEntry classEntry ) { - // TODO - return null; - } - - private Collection getClassEntries( Collection classes ) - { - List entries = Lists.newArrayList(); - for( ClassIdentity c : classes ) - { - entries.add( c.getClassEntry() ); - } - return entries; + PlainTextOutput output = new PlainTextOutput(); + DecompilerSettings settings = DecompilerSettings.javaDefaults(); + settings.setForceExplicitImports( true ); + settings.setShowSyntheticMembers( true ); + settings.setTypeLoader( loader ); + Decompiler.decompile( classEntry.getName(), output, settings ); + return output.toString(); } + */ } diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java new file mode 100644 index 00000000..fea84386 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import com.beust.jcommander.internal.Lists; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Multimap; + +public class ClassMatching +{ + private Multimap m_sourceClasses; + private Multimap m_matchedDestClasses; + private List m_unmatchedDestClasses; + + public ClassMatching( ) + { + m_sourceClasses = ArrayListMultimap.create(); + m_matchedDestClasses = ArrayListMultimap.create(); + m_unmatchedDestClasses = Lists.newArrayList(); + } + + public void addSource( ClassIdentity c ) + { + m_sourceClasses.put( c, c ); + } + + public void matchDestClass( ClassIdentity destClass ) + { + Collection matchedSourceClasses = m_sourceClasses.get( destClass ); + if( matchedSourceClasses.isEmpty() ) + { + // no match + m_unmatchedDestClasses.add( destClass ); + } + else + { + // found a match + m_matchedDestClasses.put( destClass, destClass ); + + // DEBUG + ClassIdentity sourceClass = matchedSourceClasses.iterator().next(); + assert( sourceClass.hashCode() == destClass.hashCode() ); + assert( sourceClass.equals( destClass ) ); + } + } + + public void removeSource( ClassIdentity sourceClass ) + { + m_sourceClasses.remove( sourceClass, sourceClass ); + } + + public void removeDest( ClassIdentity destClass ) + { + m_matchedDestClasses.remove( destClass, destClass ); + m_unmatchedDestClasses.remove( destClass ); + } + + public List getSourceClasses( ) + { + return new ArrayList( m_sourceClasses.values() ); + } + + public List getDestClasses( ) + { + List classes = Lists.newArrayList(); + classes.addAll( m_matchedDestClasses.values() ); + classes.addAll( m_unmatchedDestClasses ); + return classes; + } + + public BiMap getUniqueMatches( ) + { + BiMap uniqueMatches = HashBiMap.create(); + for( ClassIdentity sourceClass : m_sourceClasses.keySet() ) + { + Collection matchedSourceClasses = m_sourceClasses.get( sourceClass ); + Collection matchedDestClasses = m_matchedDestClasses.get( sourceClass ); + if( matchedSourceClasses.size() == 1 && matchedDestClasses.size() == 1 ) + { + ClassIdentity matchedSourceClass = matchedSourceClasses.iterator().next(); + ClassIdentity matchedDestClass = matchedSourceClasses.iterator().next(); + uniqueMatches.put( matchedSourceClass, matchedDestClass ); + } + } + return uniqueMatches; + } + + public BiMap,List> getAmbiguousMatches( ) + { + BiMap,List> ambiguousMatches = HashBiMap.create(); + for( ClassIdentity sourceClass : m_sourceClasses.keySet() ) + { + Collection matchedSourceClasses = m_sourceClasses.get( sourceClass ); + Collection matchedDestClasses = m_matchedDestClasses.get( sourceClass ); + if( matchedSourceClasses.size() > 1 && matchedDestClasses.size() > 1 ) + { + ambiguousMatches.put( + new ArrayList( matchedSourceClasses ), + new ArrayList( matchedDestClasses ) + ); + } + } + return ambiguousMatches; + } + + public int getNumAmbiguousSourceMatches( ) + { + int num = 0; + for( Map.Entry,List> entry : getAmbiguousMatches().entrySet() ) + { + num += entry.getKey().size(); + } + return num; + } + + public int getNumAmbiguousDestMatches( ) + { + int num = 0; + for( Map.Entry,List> entry : getAmbiguousMatches().entrySet() ) + { + num += entry.getValue().size(); + } + return num; + } + + public List getUnmatchedSourceClasses( ) + { + List classes = Lists.newArrayList(); + for( ClassIdentity sourceClass : getSourceClasses() ) + { + if( m_matchedDestClasses.get( sourceClass ).isEmpty() ) + { + classes.add( sourceClass ); + } + } + return classes; + } + + public List getUnmatchedDestClasses( ) + { + return new ArrayList( m_unmatchedDestClasses ); + } + + @Override + public String toString( ) + { + StringBuilder buf = new StringBuilder(); + + buf.append( "Source classes: " ); + buf.append( getSourceClasses().size() ); + buf.append( "\n\tUnique: " ); + buf.append( getUniqueMatches().size() ); + buf.append( "\n\tAmbiguous: " ); + buf.append( getNumAmbiguousSourceMatches() ); + buf.append( "\n\tUnmatched: " ); + buf.append( getUnmatchedSourceClasses().size() ); + + buf.append( "\nDest classes: " ); + buf.append( getDestClasses().size() ); + buf.append( "\n\tUnique: " ); + buf.append( getUniqueMatches().size() ); + buf.append( "\n\tAmbiguous: " ); + buf.append( getNumAmbiguousDestMatches() ); + buf.append( "\n\tUnmatched: " ); + buf.append( getUnmatchedDestClasses().size() ); + + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/convert/ClassNamer.java b/src/cuchaz/enigma/convert/ClassNamer.java new file mode 100644 index 00000000..1cd96657 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassNamer.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.util.Map; + +import com.beust.jcommander.internal.Maps; +import com.google.common.collect.BiMap; + +public class ClassNamer +{ + public interface SidedClassNamer + { + String getName( String name ); + } + + private Map m_sourceNames; + private Map m_destNames; + + public ClassNamer( BiMap mappings ) + { + // convert the identity mappings to name maps + m_sourceNames = Maps.newHashMap(); + m_destNames = Maps.newHashMap(); + int i = 0; + for( Map.Entry entry : mappings.entrySet() ) + { + String name = String.format( "M%04d", i++ ); + m_sourceNames.put( entry.getKey().getClassEntry().getName(), name ); + m_destNames.put( entry.getValue().getClassEntry().getName(), name ); + } + } + + public String getSourceName( String name ) + { + return m_sourceNames.get( name ); + } + + public String getDestName( String name ) + { + return m_destNames.get( name ); + } + + public SidedClassNamer getSourceNamer( ) + { + return new SidedClassNamer( ) + { + @Override + public String getName( String name ) + { + return getSourceName( name ); + } + }; + } + + public SidedClassNamer getDestNamer( ) + { + return new SidedClassNamer( ) + { + @Override + public String getName( String name ) + { + return getDestName( name ); + } + }; + } +} diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index a671c275..23bf0951 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -14,6 +14,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import com.beust.jcommander.internal.Maps; + import cuchaz.enigma.analysis.Ancestries; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; @@ -23,6 +25,13 @@ public class Translator public Map m_classes; private Ancestries m_ancestries; + public Translator( ) + { + m_direction = null; + m_classes = Maps.newHashMap(); + m_ancestries = new Ancestries(); + } + protected Translator( TranslationDirection direction, Map classes, Ancestries ancestries ) { m_direction = direction; -- cgit v1.2.3 From 63172120a39a315e29bc38ea6634741797b3dcab Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 30 Aug 2014 14:14:54 -0400 Subject: finished class matching for now, need to work on class member matching --- src/cuchaz/enigma/convert/ClassIdentity.java | 40 ++++- src/cuchaz/enigma/convert/ClassMapper.java | 229 ++++++++++++++++++++------- src/cuchaz/enigma/convert/ClassMatching.java | 33 ++++ src/cuchaz/enigma/mapping/ClassMapping.java | 29 ++++ src/cuchaz/enigma/mapping/Mappings.java | 25 +++ src/cuchaz/enigma/mapping/MethodMapping.java | 20 +++ 6 files changed, 319 insertions(+), 57 deletions(-) diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index 0a3a4497..980f31f5 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -60,6 +60,7 @@ import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class ClassIdentity { private ClassEntry m_classEntry; + private String m_rawName; private SidedClassNamer m_namer; private Set m_fields; private Set m_methods; @@ -70,13 +71,18 @@ public class ClassIdentity private Set m_implementations; private Set m_references; - public ClassIdentity( CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences ) + public ClassIdentity( CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences, boolean useRawNames ) { m_namer = namer; // stuff from the bytecode m_classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); + m_rawName = ""; + if( useRawNames ) + { + m_rawName = m_classEntry.getName(); + } m_fields = Sets.newHashSet(); for( CtField field : c.getDeclaredFields() ) { @@ -176,8 +182,16 @@ public class ClassIdentity { StringBuilder buf = new StringBuilder(); buf.append( "class: " ); + buf.append( m_classEntry.getName() ); + buf.append( " " ); buf.append( hashCode() ); buf.append( "\n" ); + if( m_rawName.length() > 0 ) + { + buf.append( "\traw name: " ); + buf.append( m_rawName ); + buf.append( "\n" ); + } for( String field : m_fields ) { buf.append( "\tfield " ); @@ -425,7 +439,8 @@ public class ClassIdentity public boolean equals( ClassIdentity other ) { - return m_fields.equals( other.m_fields ) + return m_rawName.equals( other.m_rawName ) + && m_fields.equals( other.m_fields ) && m_methods.equals( other.m_methods ) && m_constructors.equals( other.m_constructors ) && m_staticInitializer.equals( other.m_staticInitializer ) @@ -439,6 +454,7 @@ public class ClassIdentity public int hashCode( ) { List objs = Lists.newArrayList(); + objs.add( m_rawName ); objs.addAll( m_fields ); objs.addAll( m_methods ); objs.addAll( m_constructors ); @@ -449,4 +465,24 @@ public class ClassIdentity objs.addAll( m_references ); return Util.combineHashesOrdered( objs ); } + + public int getMatchScore( ClassIdentity other ) + { + return getNumMatches( m_fields, other.m_fields ) + + getNumMatches( m_methods, other.m_methods ) + + getNumMatches( m_constructors, other.m_constructors ); + } + + private int getNumMatches( Set a, Set b ) + { + int numMatches = 0; + for( String val : a ) + { + if( b.contains( val ) ) + { + numMatches++; + } + } + return numMatches; + } } diff --git a/src/cuchaz/enigma/convert/ClassMapper.java b/src/cuchaz/enigma/convert/ClassMapper.java index fe48c505..fd6ab922 100644 --- a/src/cuchaz/enigma/convert/ClassMapper.java +++ b/src/cuchaz/enigma/convert/ClassMapper.java @@ -11,32 +11,130 @@ package cuchaz.enigma.convert; import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import java.util.jar.JarFile; import javassist.CtClass; + +import com.beust.jcommander.internal.Sets; +import com.google.common.collect.Maps; + +import cuchaz.enigma.Constants; import cuchaz.enigma.TranslatingTypeLoader; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.MappingParseException; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.MappingsWriter; public class ClassMapper { public static void main( String[] args ) - throws IOException + throws IOException, MappingParseException { // TEMP JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); - JarFile toJar = new JarFile( new File( "input/1.8-pre2.jar" ) ); + JarFile toJar = new JarFile( new File( "input/1.8-pre3.jar" ) ); + File inMappingsFile = new File( "../minecraft-mappings/1.8-pre.mappings" ); + File outMappingsFile = new File( "../minecraft-mappings/1.8-pre3.mappings" ); // compute the matching ClassMatching matching = ClassMapper.computeMatching( fromJar, toJar ); - // TODO: use the matching to convert the mappings + // use the matching to convert the mappings + Mappings mappings = new MappingsReader().read( new FileReader( inMappingsFile ) ); + Map>> conversionMap = matching.getConversionMap(); + Map finalConversion = Maps.newHashMap(); + Set unmatchedSourceClasses = Sets.newHashSet(); + for( ClassMapping classMapping : mappings.classes() ) + { + // is there a match for this class? + Map.Entry> entry = conversionMap.get( classMapping.getObfName() ); + ClassIdentity sourceClass = entry.getKey(); + List matches = entry.getValue(); + + if( matches.isEmpty() ) + { + // no match! =( + System.out.println( "No exact match for source class " + classMapping.getObfName() ); + + // find the closest classes + TreeMap scoredMatches = Maps.newTreeMap( Collections.reverseOrder() ); + for( ClassIdentity c : matching.getUnmatchedDestClasses() ) + { + scoredMatches.put( sourceClass.getMatchScore( c ), c ); + } + Iterator> iter = scoredMatches.entrySet().iterator(); + for( int i=0; i<10 && iter.hasNext(); i++ ) + { + Map.Entry score = iter.next(); + System.out.println( String.format( "\tScore: %3d %s", score.getKey(), score.getValue().getClassEntry().getName() ) ); + } + + // does the best match have a non-zero score and the same name? + Map.Entry bestMatch = scoredMatches.firstEntry(); + if( bestMatch.getKey() > 0 && bestMatch.getValue().getClassEntry().equals( sourceClass.getClassEntry() ) ) + { + // use it + System.out.println( "\tAutomatically choosing likely match: " + bestMatch.getValue().getClassEntry().getName() ); + addFinalConversion( finalConversion, sourceClass, bestMatch.getValue() ); + } + else + { + unmatchedSourceClasses.add( classMapping.getObfName() ); + } + } + if( matches.size() == 1 ) + { + // unique match! We're good to go! + addFinalConversion( finalConversion, sourceClass, matches.get( 0 ) ); + } + else if( matches.size() > 1 ) + { + // too many matches! =( + unmatchedSourceClasses.add( classMapping.getObfName() ); + } + } + + // remove (and warn about) unmatched classes + if( !unmatchedSourceClasses.isEmpty() ) + { + System.err.println( "WARNING: there were unmatched classes!" ); + for( String className : unmatchedSourceClasses ) + { + System.err.println( "\t" + className ); + mappings.removeClassByObfName( className ); + } + System.err.println( "Mappings for these classes have been removed." ); + } + + // show the class name changes + for( Map.Entry entry : finalConversion.entrySet() ) + { + if( !entry.getKey().equals( entry.getValue() ) ) + { + System.out.println( String.format( "Class change: %s -> %s", entry.getKey(), entry.getValue() ) ); + } + } + + // do the final conversion + mappings.renameObfClasses( finalConversion ); + FileWriter writer = new FileWriter( outMappingsFile ); + new MappingsWriter().write( writer, mappings ); + writer.close(); + System.out.println( "Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath() ); } public static ClassMatching computeMatching( JarFile sourceJar, JarFile destJar ) @@ -55,78 +153,81 @@ public class ClassMapper TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex ); ClassMatching matching = null; - for( boolean useReferences : Arrays.asList( false, true ) ) + for( boolean useRawNames : Arrays.asList( false, true ) ) { - int numMatches = 0; - do + for( boolean useReferences : Arrays.asList( false, true ) ) { - SidedClassNamer sourceNamer = null; - SidedClassNamer destNamer = null; - if( matching != null ) + int numMatches = 0; + do { - // build a class namer - ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); - sourceNamer = namer.getSourceNamer(); - destNamer = namer.getDestNamer(); + SidedClassNamer sourceNamer = null; + SidedClassNamer destNamer = null; + if( matching != null ) + { + // build a class namer + ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); + sourceNamer = namer.getSourceNamer(); + destNamer = namer.getDestNamer(); + + // note the number of matches + numMatches = matching.getUniqueMatches().size(); + } - // note the number of matches - numMatches = matching.getUniqueMatches().size(); - } - - // get the entries left to match - Set sourceClassEntries = sourceIndex.getObfClassEntries(); - Set destClassEntries = destIndex.getObfClassEntries(); - if( matching != null ) - { - sourceClassEntries.clear(); - destClassEntries.clear(); - for( Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet() ) + // get the entries left to match + Set sourceClassEntries = sourceIndex.getObfClassEntries(); + Set destClassEntries = destIndex.getObfClassEntries(); + if( matching != null ) { - for( ClassIdentity c : entry.getKey() ) + sourceClassEntries.clear(); + destClassEntries.clear(); + for( Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet() ) + { + for( ClassIdentity c : entry.getKey() ) + { + sourceClassEntries.add( c.getClassEntry() ); + matching.removeSource( c ); + } + for( ClassIdentity c : entry.getValue() ) + { + destClassEntries.add( c.getClassEntry() ); + matching.removeDest( c ); + } + } + for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) { sourceClassEntries.add( c.getClassEntry() ); matching.removeSource( c ); } - for( ClassIdentity c : entry.getValue() ) + for( ClassIdentity c : matching.getUnmatchedDestClasses() ) { destClassEntries.add( c.getClassEntry() ); matching.removeDest( c ); } } - for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) + else { - sourceClassEntries.add( c.getClassEntry() ); - matching.removeSource( c ); + matching = new ClassMatching(); } - for( ClassIdentity c : matching.getUnmatchedDestClasses() ) + + // compute a matching for the classes + for( ClassEntry classEntry : sourceClassEntries ) { - destClassEntries.add( c.getClassEntry() ); - matching.removeDest( c ); + CtClass c = sourceLoader.loadClass( classEntry.getName() ); + ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences, useRawNames ); + matching.addSource( sourceClass ); } + for( ClassEntry classEntry : destClassEntries ) + { + CtClass c = destLoader.loadClass( classEntry.getName() ); + ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences, useRawNames ); + matching.matchDestClass( destClass ); + } + + // TEMP + System.out.println( matching ); } - else - { - matching = new ClassMatching(); - } - - // compute a matching for the classes - for( ClassEntry classEntry : sourceClassEntries ) - { - CtClass c = sourceLoader.loadClass( classEntry.getName() ); - ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences ); - matching.addSource( sourceClass ); - } - for( ClassEntry classEntry : destClassEntries ) - { - CtClass c = destLoader.loadClass( classEntry.getName() ); - ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences ); - matching.matchDestClass( destClass ); - } - - // TEMP - System.out.println( matching ); + while( matching.getUniqueMatches().size() - numMatches > 0 ); } - while( matching.getUniqueMatches().size() - numMatches > 0 ); } /* DEBUG: show some ambiguous matches @@ -163,6 +264,24 @@ public class ClassMapper return matching; } + private static void addFinalConversion( Map finalConversion, ClassIdentity sourceClass, ClassIdentity destClass ) + { + // flatten inner classes since these are all obf classes in the none package + String sourceClassName = sourceClass.getClassEntry().getName(); + if( sourceClass.getClassEntry().isInnerClass() ) + { + sourceClassName = Constants.NonePackage + "/" + sourceClass.getClassEntry().getInnerClassName(); + } + + String destClassName = destClass.getClassEntry().getName(); + if( destClass.getClassEntry().isInnerClass() ) + { + destClassName = Constants.NonePackage + "/" + destClass.getClassEntry().getInnerClassName(); + } + + finalConversion.put( sourceClassName, destClassName ); + } + /* DEBUG private static String decompile( TranslatingTypeLoader loader, ClassEntry classEntry ) { diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java index fea84386..4e9fe398 100644 --- a/src/cuchaz/enigma/convert/ClassMatching.java +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -10,12 +10,15 @@ ******************************************************************************/ package cuchaz.enigma.convert; +import java.util.AbstractMap; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import com.beust.jcommander.internal.Lists; +import com.beust.jcommander.internal.Maps; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; @@ -156,6 +159,36 @@ public class ClassMatching return new ArrayList( m_unmatchedDestClasses ); } + public Map>> getConversionMap( ) + { + Map>> conversion = Maps.newHashMap(); + for( Map.Entry entry : getUniqueMatches().entrySet() ) + { + conversion.put( + entry.getKey().getClassEntry().getName(), + new AbstractMap.SimpleEntry>( entry.getKey(), Arrays.asList( entry.getValue() ) ) + ); + } + for( Map.Entry,List> entry : getAmbiguousMatches().entrySet() ) + { + for( ClassIdentity sourceClass : entry.getKey() ) + { + conversion.put( + sourceClass.getClassEntry().getName(), + new AbstractMap.SimpleEntry>( sourceClass, entry.getValue() ) + ); + } + } + for( ClassIdentity sourceClass : getUnmatchedSourceClasses() ) + { + conversion.put( + sourceClass.getClassEntry().getName(), + new AbstractMap.SimpleEntry>( sourceClass, new ArrayList() ) + ); + } + return conversion; + } + @Override public String toString( ) { diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 59365129..095cb385 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -11,6 +11,7 @@ package cuchaz.enigma.mapping; import java.io.Serializable; +import java.util.ArrayList; import java.util.Map; import com.google.common.collect.Maps; @@ -299,4 +300,32 @@ public class ClassMapping implements Serializable, Comparable { return m_obfName.compareTo( other.m_obfName ); } + + public void renameObfClasses( Map nameMap ) + { + // rename self + { + String newName = nameMap.get( m_obfName ); + if( newName != null ) + { + m_obfName = newName; + } + } + + // rename inner classes + for( ClassMapping classMapping : new ArrayList( m_innerClassesByObf.values() ) ) + { + m_innerClassesByObf.remove( classMapping.getObfName() ); + classMapping.renameObfClasses( nameMap ); + m_innerClassesByObf.put( classMapping.getObfName(), classMapping ); + } + + // rename method signatures + for( MethodMapping methodMapping : new ArrayList( m_methodsByObf.values() ) ) + { + m_methodsByObf.remove( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ) ); + methodMapping.renameObfClasses( nameMap ); + m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping ); + } + } } diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index f3b8fad1..70bea25f 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.Serializable; +import java.util.ArrayList; import java.util.Map; import java.util.zip.GZIPInputStream; @@ -136,4 +137,28 @@ public class Mappings implements Serializable } return buf.toString(); } + + public void renameObfClasses( Map nameMap ) + { + for( ClassMapping classMapping : new ArrayList( m_classesByObf.values() ) ) + { + String newName = nameMap.get( classMapping.getObfName() ); + if( newName != null ) + { + m_classesByObf.remove( classMapping.getObfName() ); + classMapping.renameObfClasses( nameMap ); + m_classesByObf.put( classMapping.getObfName(), classMapping ); + } + } + } + + public void removeClassByObfName( String obfName ) + { + ClassMapping classMapping = m_classesByObf.get( obfName ); + if( classMapping != null ) + { + m_classesByObf.remove( classMapping.getObfName() ); + m_classesByDeobf.remove( classMapping.getDeobfName() ); + } + } } diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index 6e6bec46..fe4e29b2 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -14,6 +14,8 @@ import java.io.Serializable; import java.util.Map; import java.util.TreeMap; +import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; + public class MethodMapping implements Serializable, Comparable { private static final long serialVersionUID = -4409570216084263978L; @@ -139,4 +141,22 @@ public class MethodMapping implements Serializable, Comparable { return ( m_obfName + m_obfSignature ).compareTo( ( other.m_obfName + other.m_obfSignature ) ); } + + public void renameObfClasses( final Map nameMap ) + { + // rename obf classes in the signature + m_obfSignature = SignatureUpdater.update( m_obfSignature, new ClassNameUpdater( ) + { + @Override + public String update( String className ) + { + String newName = nameMap.get( className ); + if( newName != null ) + { + return newName; + } + return className; + } + } ); + } } -- cgit v1.2.3 From 59c592673635e989fd0785d41d51d7c3dd17cc0b Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 30 Aug 2014 16:31:31 -0400 Subject: debugging class matcher... almost got it! --- src/cuchaz/enigma/Deobfuscator.java | 2 +- src/cuchaz/enigma/analysis/JarIndex.java | 57 ++-- src/cuchaz/enigma/convert/ClassIdentity.java | 40 +-- src/cuchaz/enigma/convert/ClassMapper.java | 297 -------------------- src/cuchaz/enigma/convert/ClassMatcher.java | 398 +++++++++++++++++++++++++++ src/cuchaz/enigma/convert/ClassMatching.java | 2 +- src/cuchaz/enigma/mapping/Mappings.java | 7 + 7 files changed, 461 insertions(+), 342 deletions(-) delete mode 100644 src/cuchaz/enigma/convert/ClassMapper.java create mode 100644 src/cuchaz/enigma/convert/ClassMatcher.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index cc1465a2..9a78f38d 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -70,7 +70,7 @@ public class Deobfuscator // build the jar index m_jarIndex = new JarIndex(); - m_jarIndex.indexJar( m_jar ); + m_jarIndex.indexJar( m_jar, true ); // config the decompiler m_settings = DecompilerSettings.javaDefaults(); diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index b479b69e..4279dedd 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -79,7 +79,7 @@ public class JarIndex m_bridgeMethods = Maps.newHashMap(); } - public void indexJar( JarFile jar ) + public void indexJar( JarFile jar, boolean buildInnerClasses ) { // step 1: read the class names for( ClassEntry classEntry : JarClassIterator.getClassEntries( jar ) ) @@ -125,40 +125,43 @@ public class JarIndex } } - // step 4: index inner classes and anonymous classes - for( CtClass c : JarClassIterator.classes( jar ) ) + if( buildInnerClasses ) { - ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); - String outerClassName = findOuterClass( c ); - if( outerClassName != null ) + // step 4: index inner classes and anonymous classes + for( CtClass c : JarClassIterator.classes( jar ) ) { - String innerClassName = Descriptor.toJvmName( c.getName() ); - m_innerClasses.put( outerClassName, innerClassName ); - m_outerClasses.put( innerClassName, outerClassName ); - - if( isAnonymousClass( c, outerClassName ) ) + ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); + String outerClassName = findOuterClass( c ); + if( outerClassName != null ) { - m_anonymousClasses.add( innerClassName ); + String innerClassName = Descriptor.toJvmName( c.getName() ); + m_innerClasses.put( outerClassName, innerClassName ); + m_outerClasses.put( innerClassName, outerClassName ); - // DEBUG - //System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); - } - else - { - // DEBUG - //System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); + if( isAnonymousClass( c, outerClassName ) ) + { + m_anonymousClasses.add( innerClassName ); + + // DEBUG + //System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); + } + else + { + // DEBUG + //System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); + } } } + + // step 5: update other indices with inner class info + Map renames = Maps.newHashMap(); + for( Map.Entry entry : m_outerClasses.entrySet() ) + { + renames.put( entry.getKey(), entry.getValue() + "$" + new ClassEntry( entry.getKey() ).getSimpleName() ); + } + renameClasses( renames ); } - // step 5: update other indices with inner class info - Map renames = Maps.newHashMap(); - for( Map.Entry entry : m_outerClasses.entrySet() ) - { - renames.put( entry.getKey(), entry.getValue() + "$" + new ClassEntry( entry.getKey() ).getSimpleName() ); - } - renameClasses( renames ); - // step 5: update other indices with bridge method info renameMethods( m_bridgeMethods ); } diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index 980f31f5..8de71288 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -16,7 +16,6 @@ import java.security.NoSuchAlgorithmException; import java.util.Enumeration; import java.util.List; import java.util.Map; -import java.util.Set; import javassist.CannotCompileException; import javassist.CtBehavior; @@ -36,8 +35,9 @@ import javassist.expr.MethodCall; import javassist.expr.NewExpr; import com.beust.jcommander.internal.Maps; -import com.beust.jcommander.internal.Sets; +import com.google.common.collect.HashMultiset; import com.google.common.collect.Lists; +import com.google.common.collect.Multiset; import cuchaz.enigma.Constants; import cuchaz.enigma.Util; @@ -62,14 +62,14 @@ public class ClassIdentity private ClassEntry m_classEntry; private String m_rawName; private SidedClassNamer m_namer; - private Set m_fields; - private Set m_methods; - private Set m_constructors; + private Multiset m_fields; + private Multiset m_methods; + private Multiset m_constructors; private String m_staticInitializer; private String m_extends; - private Set m_implements; - private Set m_implementations; - private Set m_references; + private Multiset m_implements; + private Multiset m_implementations; + private Multiset m_references; public ClassIdentity( CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences, boolean useRawNames ) { @@ -83,17 +83,17 @@ public class ClassIdentity { m_rawName = m_classEntry.getName(); } - m_fields = Sets.newHashSet(); + m_fields = HashMultiset.create(); for( CtField field : c.getDeclaredFields() ) { m_fields.add( scrubSignature( field.getSignature() ) ); } - m_methods = Sets.newHashSet(); + m_methods = HashMultiset.create(); for( CtMethod method : c.getDeclaredMethods() ) { m_methods.add( scrubSignature( method.getSignature() ) + "0x" + getBehaviorSignature( method ) ); } - m_constructors = Sets.newHashSet(); + m_constructors = HashMultiset.create(); for( CtConstructor constructor : c.getDeclaredConstructors() ) { m_constructors.add( scrubSignature( constructor.getSignature() ) + "0x" + getBehaviorSignature( constructor ) ); @@ -108,7 +108,7 @@ public class ClassIdentity { m_extends = scrubClassName( c.getClassFile().getSuperclass() ); } - m_implements = Sets.newHashSet(); + m_implements = HashMultiset.create(); for( String interfaceName : c.getClassFile().getInterfaces() ) { m_implements.add( scrubClassName( interfaceName ) ); @@ -116,7 +116,7 @@ public class ClassIdentity // stuff from the jar index - m_implementations = Sets.newHashSet(); + m_implementations = HashMultiset.create(); @SuppressWarnings( "unchecked" ) Enumeration implementations = index.getClassImplementations( null, m_classEntry ).children(); while( implementations.hasMoreElements() ) @@ -125,7 +125,7 @@ public class ClassIdentity m_implementations.add( scrubClassName( node.getClassEntry().getName() ) ); } - m_references = Sets.newHashSet(); + m_references = HashMultiset.create(); if( useReferences ) { for( CtField field : c.getDeclaredFields() ) @@ -154,7 +154,7 @@ public class ClassIdentity } } } - + private void addReference( EntryReference reference ) { if( reference.context.getSignature() != null ) @@ -473,7 +473,15 @@ public class ClassIdentity + getNumMatches( m_constructors, other.m_constructors ); } - private int getNumMatches( Set a, Set b ) + public boolean matches( CtClass c ) + { + // just compare declaration counts + return m_fields.size() == c.getDeclaredFields().length + && m_methods.size() == c.getDeclaredMethods().length + && m_constructors.size() == c.getDeclaredConstructors().length; + } + + private int getNumMatches( Multiset a, Multiset b ) { int numMatches = 0; for( String val : a ) diff --git a/src/cuchaz/enigma/convert/ClassMapper.java b/src/cuchaz/enigma/convert/ClassMapper.java deleted file mode 100644 index fd6ab922..00000000 --- a/src/cuchaz/enigma/convert/ClassMapper.java +++ /dev/null @@ -1,297 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.convert; - -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.jar.JarFile; - -import javassist.CtClass; - -import com.beust.jcommander.internal.Sets; -import com.google.common.collect.Maps; - -import cuchaz.enigma.Constants; -import cuchaz.enigma.TranslatingTypeLoader; -import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.ClassMapping; -import cuchaz.enigma.mapping.MappingParseException; -import cuchaz.enigma.mapping.Mappings; -import cuchaz.enigma.mapping.MappingsReader; -import cuchaz.enigma.mapping.MappingsWriter; - -public class ClassMapper -{ - public static void main( String[] args ) - throws IOException, MappingParseException - { - // TEMP - JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); - JarFile toJar = new JarFile( new File( "input/1.8-pre3.jar" ) ); - File inMappingsFile = new File( "../minecraft-mappings/1.8-pre.mappings" ); - File outMappingsFile = new File( "../minecraft-mappings/1.8-pre3.mappings" ); - - // compute the matching - ClassMatching matching = ClassMapper.computeMatching( fromJar, toJar ); - - // use the matching to convert the mappings - Mappings mappings = new MappingsReader().read( new FileReader( inMappingsFile ) ); - Map>> conversionMap = matching.getConversionMap(); - Map finalConversion = Maps.newHashMap(); - Set unmatchedSourceClasses = Sets.newHashSet(); - for( ClassMapping classMapping : mappings.classes() ) - { - // is there a match for this class? - Map.Entry> entry = conversionMap.get( classMapping.getObfName() ); - ClassIdentity sourceClass = entry.getKey(); - List matches = entry.getValue(); - - if( matches.isEmpty() ) - { - // no match! =( - System.out.println( "No exact match for source class " + classMapping.getObfName() ); - - // find the closest classes - TreeMap scoredMatches = Maps.newTreeMap( Collections.reverseOrder() ); - for( ClassIdentity c : matching.getUnmatchedDestClasses() ) - { - scoredMatches.put( sourceClass.getMatchScore( c ), c ); - } - Iterator> iter = scoredMatches.entrySet().iterator(); - for( int i=0; i<10 && iter.hasNext(); i++ ) - { - Map.Entry score = iter.next(); - System.out.println( String.format( "\tScore: %3d %s", score.getKey(), score.getValue().getClassEntry().getName() ) ); - } - - // does the best match have a non-zero score and the same name? - Map.Entry bestMatch = scoredMatches.firstEntry(); - if( bestMatch.getKey() > 0 && bestMatch.getValue().getClassEntry().equals( sourceClass.getClassEntry() ) ) - { - // use it - System.out.println( "\tAutomatically choosing likely match: " + bestMatch.getValue().getClassEntry().getName() ); - addFinalConversion( finalConversion, sourceClass, bestMatch.getValue() ); - } - else - { - unmatchedSourceClasses.add( classMapping.getObfName() ); - } - } - if( matches.size() == 1 ) - { - // unique match! We're good to go! - addFinalConversion( finalConversion, sourceClass, matches.get( 0 ) ); - } - else if( matches.size() > 1 ) - { - // too many matches! =( - unmatchedSourceClasses.add( classMapping.getObfName() ); - } - } - - // remove (and warn about) unmatched classes - if( !unmatchedSourceClasses.isEmpty() ) - { - System.err.println( "WARNING: there were unmatched classes!" ); - for( String className : unmatchedSourceClasses ) - { - System.err.println( "\t" + className ); - mappings.removeClassByObfName( className ); - } - System.err.println( "Mappings for these classes have been removed." ); - } - - // show the class name changes - for( Map.Entry entry : finalConversion.entrySet() ) - { - if( !entry.getKey().equals( entry.getValue() ) ) - { - System.out.println( String.format( "Class change: %s -> %s", entry.getKey(), entry.getValue() ) ); - } - } - - // do the final conversion - mappings.renameObfClasses( finalConversion ); - FileWriter writer = new FileWriter( outMappingsFile ); - new MappingsWriter().write( writer, mappings ); - writer.close(); - System.out.println( "Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath() ); - } - - public static ClassMatching computeMatching( JarFile sourceJar, JarFile destJar ) - { - // index jars - System.out.println( "Indexing source jar..." ); - JarIndex sourceIndex = new JarIndex(); - sourceIndex.indexJar( sourceJar ); - System.out.println( "Indexing dest jar..." ); - JarIndex destIndex = new JarIndex(); - destIndex.indexJar( destJar ); - - System.out.println( "Computing matching..." ); - - TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader( sourceJar, sourceIndex ); - TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex ); - - ClassMatching matching = null; - for( boolean useRawNames : Arrays.asList( false, true ) ) - { - for( boolean useReferences : Arrays.asList( false, true ) ) - { - int numMatches = 0; - do - { - SidedClassNamer sourceNamer = null; - SidedClassNamer destNamer = null; - if( matching != null ) - { - // build a class namer - ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); - sourceNamer = namer.getSourceNamer(); - destNamer = namer.getDestNamer(); - - // note the number of matches - numMatches = matching.getUniqueMatches().size(); - } - - // get the entries left to match - Set sourceClassEntries = sourceIndex.getObfClassEntries(); - Set destClassEntries = destIndex.getObfClassEntries(); - if( matching != null ) - { - sourceClassEntries.clear(); - destClassEntries.clear(); - for( Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet() ) - { - for( ClassIdentity c : entry.getKey() ) - { - sourceClassEntries.add( c.getClassEntry() ); - matching.removeSource( c ); - } - for( ClassIdentity c : entry.getValue() ) - { - destClassEntries.add( c.getClassEntry() ); - matching.removeDest( c ); - } - } - for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) - { - sourceClassEntries.add( c.getClassEntry() ); - matching.removeSource( c ); - } - for( ClassIdentity c : matching.getUnmatchedDestClasses() ) - { - destClassEntries.add( c.getClassEntry() ); - matching.removeDest( c ); - } - } - else - { - matching = new ClassMatching(); - } - - // compute a matching for the classes - for( ClassEntry classEntry : sourceClassEntries ) - { - CtClass c = sourceLoader.loadClass( classEntry.getName() ); - ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences, useRawNames ); - matching.addSource( sourceClass ); - } - for( ClassEntry classEntry : destClassEntries ) - { - CtClass c = destLoader.loadClass( classEntry.getName() ); - ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences, useRawNames ); - matching.matchDestClass( destClass ); - } - - // TEMP - System.out.println( matching ); - } - while( matching.getUniqueMatches().size() - numMatches > 0 ); - } - } - - /* DEBUG: show some ambiguous matches - List,List>> ambiguousMatches = new ArrayList,List>>( matching.getAmbiguousMatches().entrySet() ); - Collections.sort( ambiguousMatches, new Comparator,List>>( ) - { - @Override - public int compare( Map.Entry,List> a, Map.Entry,List> b ) - { - String aName = a.getKey().get( 0 ).getClassEntry().getName(); - String bName = b.getKey().get( 0 ).getClassEntry().getName(); - return aName.compareTo( bName ); - } - } ); - for( Map.Entry,List> entry : ambiguousMatches ) - { - for( ClassIdentity c : entry.getKey() ) - { - System.out.print( c.getClassEntry().getName() + " " ); - } - System.out.println(); - } - Map.Entry,List> entry = ambiguousMatches.get( 7 ); - for( ClassIdentity c : entry.getKey() ) - { - System.out.println( c ); - } - for( ClassIdentity c : entry.getKey() ) - { - System.out.println( decompile( sourceLoader, c.getClassEntry() ) ); - } - */ - - return matching; - } - - private static void addFinalConversion( Map finalConversion, ClassIdentity sourceClass, ClassIdentity destClass ) - { - // flatten inner classes since these are all obf classes in the none package - String sourceClassName = sourceClass.getClassEntry().getName(); - if( sourceClass.getClassEntry().isInnerClass() ) - { - sourceClassName = Constants.NonePackage + "/" + sourceClass.getClassEntry().getInnerClassName(); - } - - String destClassName = destClass.getClassEntry().getName(); - if( destClass.getClassEntry().isInnerClass() ) - { - destClassName = Constants.NonePackage + "/" + destClass.getClassEntry().getInnerClassName(); - } - - finalConversion.put( sourceClassName, destClassName ); - } - - /* DEBUG - private static String decompile( TranslatingTypeLoader loader, ClassEntry classEntry ) - { - PlainTextOutput output = new PlainTextOutput(); - DecompilerSettings settings = DecompilerSettings.javaDefaults(); - settings.setForceExplicitImports( true ); - settings.setShowSyntheticMembers( true ); - settings.setTypeLoader( loader ); - Decompiler.decompile( classEntry.getName(), output, settings ); - return output.toString(); - } - */ -} diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java new file mode 100644 index 00000000..ac07a5bd --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -0,0 +1,398 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.jar.JarFile; + +import javassist.CtBehavior; +import javassist.CtClass; + +import com.beust.jcommander.internal.Lists; +import com.beust.jcommander.internal.Sets; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Maps; + +import cuchaz.enigma.TranslatingTypeLoader; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.MappingParseException; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.MappingsWriter; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.MethodMapping; + +public class ClassMatcher +{ + public static void main( String[] args ) + throws IOException, MappingParseException + { + // TEMP + JarFile sourceJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); + JarFile destJar = new JarFile( new File( "input/1.8-pre2.jar" ) ); + File inMappingsFile = new File( "../minecraft-mappings/1.8-pre.mappings" ); + File outMappingsFile = new File( "../minecraft-mappings/1.8-pre2.mappings" ); + + // do the conversion + Mappings mappings = new MappingsReader().read( new FileReader( inMappingsFile ) ); + convertMappings( sourceJar, destJar, mappings ); + + // write out the convert mappings + FileWriter writer = new FileWriter( outMappingsFile ); + new MappingsWriter().write( writer, mappings ); + writer.close(); + System.out.println( "Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath() ); + } + + private static void convertMappings( JarFile sourceJar, JarFile destJar, Mappings mappings ) + { + // index jars + System.out.println( "Indexing source jar..." ); + JarIndex sourceIndex = new JarIndex(); + sourceIndex.indexJar( sourceJar, false ); + System.out.println( "Indexing dest jar..." ); + JarIndex destIndex = new JarIndex(); + destIndex.indexJar( destJar, false ); + TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader( sourceJar, sourceIndex ); + TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex ); + + // compute the matching + ClassMatching matching = ClassMatcher.computeMatching( sourceIndex, sourceLoader, destIndex, destLoader ); + + // start the class conversion map with the unique and ambiguous matchings + Map>> conversionMap = matching.getConversionMap(); + + // probabilistically match the unmatched source classes + for( ClassIdentity sourceClass : new ArrayList( matching.getUnmatchedSourceClasses() ) ) + { + System.out.println( "No exact match for source class " + sourceClass.getClassEntry() ); + + // find the closest classes + TreeMap scoredMatches = Maps.newTreeMap( Collections.reverseOrder() ); + for( ClassIdentity c : matching.getUnmatchedDestClasses() ) + { + scoredMatches.put( sourceClass.getMatchScore( c ), c ); + } + Iterator> iter = scoredMatches.entrySet().iterator(); + for( int i=0; i<10 && iter.hasNext(); i++ ) + { + Map.Entry score = iter.next(); + System.out.println( String.format( "\tScore: %3d %s", score.getKey(), score.getValue().getClassEntry().getName() ) ); + } + + // does the best match have a non-zero score and the same name? + Map.Entry bestMatch = scoredMatches.firstEntry(); + if( bestMatch.getKey() > 0 && bestMatch.getValue().getClassEntry().equals( sourceClass.getClassEntry() ) ) + { + // use it + System.out.println( "\tAutomatically choosing likely match: " + bestMatch.getValue().getClassEntry().getName() ); + conversionMap.put( + sourceClass.getClassEntry().getName(), + new AbstractMap.SimpleEntry>( sourceClass, Arrays.asList( bestMatch.getValue() ) ) + ); + } + } + + // use the matching to convert the mappings + BiMap classConversion = HashBiMap.create(); + Set unmatchedSourceClasses = Sets.newHashSet(); + for( String className : mappings.getAllObfClassNames() ) + { + // is there a match for this class? + Map.Entry> entry = conversionMap.get( className ); + ClassIdentity sourceClass = entry.getKey(); + List matches = entry.getValue(); + + if( matches.isEmpty() ) + { + // no match! =( + unmatchedSourceClasses.add( className ); + } + else if( matches.size() == 1 ) + { + // unique match! We're good to go! + classConversion.put( + sourceClass.getClassEntry().getName(), + matches.get( 0 ).getClassEntry().getName() + ); + } + else if( matches.size() > 1 ) + { + // too many matches! =( + unmatchedSourceClasses.add( className ); + } + } + + // remove (and warn about) unmatched classes + if( !unmatchedSourceClasses.isEmpty() ) + { + System.err.println( "WARNING: there were unmatched classes!" ); + for( String className : unmatchedSourceClasses ) + { + System.err.println( "\t" + className ); + mappings.removeClassByObfName( className ); + } + System.err.println( "Mappings for these classes have been removed." ); + } + + // show the class name changes + for( Map.Entry entry : classConversion.entrySet() ) + { + if( !entry.getKey().equals( entry.getValue() ) ) + { + System.out.println( String.format( "Class change: %s -> %s", entry.getKey(), entry.getValue() ) ); + /* DEBUG + System.out.println( String.format( "\n%s\n%s", + new ClassIdentity( sourceLoader.loadClass( entry.getKey() ), null, sourceIndex, false, false ), + new ClassIdentity( destLoader.loadClass( entry.getValue() ), null, destIndex, false, false ) + ) ); + */ + } + } + + // TEMP: show some classes + for( String className : Arrays.asList( "none/em", "none/ej", "none/en" ) ) + { + System.out.println( String.format( "check: %s -> %s", className, classConversion.get( className ) ) ); + } + + // convert the classes + mappings.renameObfClasses( classConversion ); + + // look for method matches + System.out.println( "Matching methods..." ); + for( ClassMapping classMapping : mappings.classes() ) + { + ClassEntry classEntry = new ClassEntry( classMapping.getObfName() ); + for( MethodMapping methodMapping : classMapping.methods() ) + { + // skip constructors + if( methodMapping.getObfName().equals( "" ) ) + { + continue; + } + + MethodEntry methodEntry = new MethodEntry( + classEntry, + methodMapping.getObfName(), + methodMapping.getObfSignature() + ); + if( !destIndex.isMethodImplemented( methodEntry ) ) + { + System.err.println( "WARNING: method doesn't match: " + methodEntry ); + + // show the available methods + System.err.println( "\tAvailable dest methods:" ); + CtClass c = destLoader.loadClass( classMapping.getObfName() ); + for( CtBehavior behavior : c.getDeclaredBehaviors() ) + { + MethodEntry declaredMethodEntry = new MethodEntry( + new ClassEntry( classMapping.getObfName() ), + behavior.getName(), + behavior.getSignature() + ); + System.err.println( "\t\t" + declaredMethodEntry ); + } + + System.err.println( "\tAvailable source methods:" ); + c = sourceLoader.loadClass( classConversion.inverse().get( classMapping.getObfName() ) ); + for( CtBehavior behavior : c.getDeclaredBehaviors() ) + { + MethodEntry declaredMethodEntry = new MethodEntry( + new ClassEntry( classMapping.getObfName() ), + behavior.getName(), + behavior.getSignature() + ); + System.err.println( "\t\t" + declaredMethodEntry ); + } + } + } + } + } + + public static ClassMatching computeMatching( JarIndex sourceIndex, TranslatingTypeLoader sourceLoader, JarIndex destIndex, TranslatingTypeLoader destLoader ) + { + System.out.println( "Matching classes..." ); + ClassMatching matching = null; + for( boolean useRawNames : Arrays.asList( false/*, true*/ ) ) + { + for( boolean useReferences : Arrays.asList( false, true ) ) + { + int numMatches = 0; + do + { + SidedClassNamer sourceNamer = null; + SidedClassNamer destNamer = null; + if( matching != null ) + { + // build a class namer + ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); + sourceNamer = namer.getSourceNamer(); + destNamer = namer.getDestNamer(); + + // note the number of matches + numMatches = matching.getUniqueMatches().size(); + } + + // get the entries left to match + Set sourceClassEntries = sourceIndex.getObfClassEntries(); + Set destClassEntries = destIndex.getObfClassEntries(); + if( matching != null ) + { + sourceClassEntries.clear(); + destClassEntries.clear(); + for( Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet() ) + { + for( ClassIdentity c : entry.getKey() ) + { + sourceClassEntries.add( c.getClassEntry() ); + matching.removeSource( c ); + } + for( ClassIdentity c : entry.getValue() ) + { + destClassEntries.add( c.getClassEntry() ); + matching.removeDest( c ); + } + } + for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) + { + sourceClassEntries.add( c.getClassEntry() ); + matching.removeSource( c ); + } + for( ClassIdentity c : matching.getUnmatchedDestClasses() ) + { + destClassEntries.add( c.getClassEntry() ); + matching.removeDest( c ); + } + } + else + { + matching = new ClassMatching(); + } + + // compute a matching for the classes + for( ClassEntry classEntry : sourceClassEntries ) + { + CtClass c = sourceLoader.loadClass( classEntry.getName() ); + ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences, useRawNames ); + matching.addSource( sourceClass ); + } + for( ClassEntry classEntry : destClassEntries ) + { + CtClass c = destLoader.loadClass( classEntry.getName() ); + ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences, useRawNames ); + matching.matchDestClass( destClass ); + } + + // TEMP + System.out.println( matching ); + } + while( matching.getUniqueMatches().size() - numMatches > 0 ); + } + } + + // DEBUG: check the class matches + System.out.println( "Checking class matches..." ); + for( Map.Entry entry : matching.getUniqueMatches().entrySet() ) + { + // check source + ClassIdentity sourceClass = entry.getKey(); + CtClass sourceC = sourceLoader.loadClass( sourceClass.getClassEntry().getName() ); + assert( sourceC != null ) + : "Unable to load source class " + sourceClass.getClassEntry(); + assert( sourceClass.matches( sourceC ) ) + : "Source " + sourceClass + " doesn't match " + new ClassIdentity( sourceC, null, sourceIndex, false, false ); + + // check dest + ClassIdentity destClass = entry.getValue(); + CtClass destC = destLoader.loadClass( destClass.getClassEntry().getName() ); + assert( destC != null ) + : "Unable to load dest class " + destClass.getClassEntry(); + assert( destClass.matches( destC ) ) + : "Dest " + destClass + " doesn't match " + new ClassIdentity( destC, null, destIndex, false, false ); + } + + // warn about the ambiguous matchings + List,List>> ambiguousMatches = new ArrayList,List>>( matching.getAmbiguousMatches().entrySet() ); + Collections.sort( ambiguousMatches, new Comparator,List>>( ) + { + @Override + public int compare( Map.Entry,List> a, Map.Entry,List> b ) + { + String aName = a.getKey().get( 0 ).getClassEntry().getName(); + String bName = b.getKey().get( 0 ).getClassEntry().getName(); + return aName.compareTo( bName ); + } + } ); + for( Map.Entry,List> entry : ambiguousMatches ) + { + System.out.println( "Ambiguous matching:" ); + System.out.println( "\tSource: " + getClassNames( entry.getKey() ) ); + System.out.println( "\tDest: " + getClassNames( entry.getValue() ) ); + } + + /* DEBUG + Map.Entry,List> entry = ambiguousMatches.get( 7 ); + for( ClassIdentity c : entry.getKey() ) + { + System.out.println( c ); + } + for( ClassIdentity c : entry.getKey() ) + { + System.out.println( decompile( sourceLoader, c.getClassEntry() ) ); + } + */ + + return matching; + } + + private static List getClassNames( Collection classes ) + { + List out = Lists.newArrayList(); + for( ClassIdentity c : classes ) + { + out.add( c.getClassEntry().getName() ); + } + Collections.sort( out ); + return out; + } + + /* DEBUG + private static String decompile( TranslatingTypeLoader loader, ClassEntry classEntry ) + { + PlainTextOutput output = new PlainTextOutput(); + DecompilerSettings settings = DecompilerSettings.javaDefaults(); + settings.setForceExplicitImports( true ); + settings.setShowSyntheticMembers( true ); + settings.setTypeLoader( loader ); + Decompiler.decompile( classEntry.getName(), output, settings ); + return output.toString(); + } + */ +} diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java index 4e9fe398..ef5a7d8a 100644 --- a/src/cuchaz/enigma/convert/ClassMatching.java +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -96,7 +96,7 @@ public class ClassMatching if( matchedSourceClasses.size() == 1 && matchedDestClasses.size() == 1 ) { ClassIdentity matchedSourceClass = matchedSourceClasses.iterator().next(); - ClassIdentity matchedDestClass = matchedSourceClasses.iterator().next(); + ClassIdentity matchedDestClass = matchedDestClasses.iterator().next(); uniqueMatches.put( matchedSourceClass, matchedDestClass ); } } diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 70bea25f..c92f8de0 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -15,6 +15,7 @@ import java.io.InputStream; import java.io.ObjectInputStream; import java.io.Serializable; import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.zip.GZIPInputStream; @@ -161,4 +162,10 @@ public class Mappings implements Serializable m_classesByDeobf.remove( classMapping.getDeobfName() ); } } + + public List getAllObfClassNames( ) + { + // TODO: implement this + return null; + } } -- cgit v1.2.3 From d3fc0b55515e81ae1b10fa16129f05b0241271f0 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 31 Aug 2014 14:41:24 -0400 Subject: fixed lots of bugs in the mappings converter. It's finally ready. =) --- src/cuchaz/enigma/convert/ClassIdentity.java | 23 +-- src/cuchaz/enigma/convert/ClassMatcher.java | 260 ++++++++++++++++----------- src/cuchaz/enigma/convert/ClassMatching.java | 26 +-- src/cuchaz/enigma/mapping/Mappings.java | 29 ++- 4 files changed, 194 insertions(+), 144 deletions(-) diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index 8de71288..bd2824b3 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -60,7 +60,6 @@ import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class ClassIdentity { private ClassEntry m_classEntry; - private String m_rawName; private SidedClassNamer m_namer; private Multiset m_fields; private Multiset m_methods; @@ -71,18 +70,13 @@ public class ClassIdentity private Multiset m_implementations; private Multiset m_references; - public ClassIdentity( CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences, boolean useRawNames ) + public ClassIdentity( CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences ) { m_namer = namer; // stuff from the bytecode m_classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); - m_rawName = ""; - if( useRawNames ) - { - m_rawName = m_classEntry.getName(); - } m_fields = HashMultiset.create(); for( CtField field : c.getDeclaredFields() ) { @@ -186,12 +180,6 @@ public class ClassIdentity buf.append( " " ); buf.append( hashCode() ); buf.append( "\n" ); - if( m_rawName.length() > 0 ) - { - buf.append( "\traw name: " ); - buf.append( m_rawName ); - buf.append( "\n" ); - } for( String field : m_fields ) { buf.append( "\tfield " ); @@ -439,8 +427,7 @@ public class ClassIdentity public boolean equals( ClassIdentity other ) { - return m_rawName.equals( other.m_rawName ) - && m_fields.equals( other.m_fields ) + return m_fields.equals( other.m_fields ) && m_methods.equals( other.m_methods ) && m_constructors.equals( other.m_constructors ) && m_staticInitializer.equals( other.m_staticInitializer ) @@ -454,7 +441,6 @@ public class ClassIdentity public int hashCode( ) { List objs = Lists.newArrayList(); - objs.add( m_rawName ); objs.addAll( m_fields ); objs.addAll( m_methods ); objs.addAll( m_constructors ); @@ -473,6 +459,11 @@ public class ClassIdentity + getNumMatches( m_constructors, other.m_constructors ); } + public int getMaxMatchScore( ) + { + return m_fields.size() + m_methods.size() + m_constructors.size(); + } + public boolean matches( CtClass c ) { // just compare declaration counts diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index ac07a5bd..b551da25 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -14,17 +14,14 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeMap; import java.util.jar.JarFile; import javassist.CtBehavior; @@ -32,9 +29,11 @@ import javassist.CtClass; import com.beust.jcommander.internal.Lists; import com.beust.jcommander.internal.Sets; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; import cuchaz.enigma.TranslatingTypeLoader; import cuchaz.enigma.analysis.JarIndex; @@ -55,22 +54,28 @@ public class ClassMatcher { // TEMP JarFile sourceJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); - JarFile destJar = new JarFile( new File( "input/1.8-pre2.jar" ) ); + JarFile destJar = new JarFile( new File( "input/1.8-pre3.jar" ) ); File inMappingsFile = new File( "../minecraft-mappings/1.8-pre.mappings" ); - File outMappingsFile = new File( "../minecraft-mappings/1.8-pre2.mappings" ); + File outMappingsFile = new File( "../minecraft-mappings/1.8-pre3.mappings" ); + + // define a matching to use when the automated system cannot find a match + Map fallbackMatching = Maps.newHashMap(); + fallbackMatching.put( "none/ayb", "none/ayb" ); + fallbackMatching.put( "none/ayd", "none/ayd" ); + fallbackMatching.put( "none/bgk", "none/bgk" ); // do the conversion Mappings mappings = new MappingsReader().read( new FileReader( inMappingsFile ) ); - convertMappings( sourceJar, destJar, mappings ); + convertMappings( sourceJar, destJar, mappings, fallbackMatching ); - // write out the convert mappings + // write out the converted mappings FileWriter writer = new FileWriter( outMappingsFile ); new MappingsWriter().write( writer, mappings ); writer.close(); System.out.println( "Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath() ); } - private static void convertMappings( JarFile sourceJar, JarFile destJar, Mappings mappings ) + private static void convertMappings( JarFile sourceJar, JarFile destJar, Mappings mappings, Map fallbackMatching ) { // index jars System.out.println( "Indexing source jar..." ); @@ -83,58 +88,77 @@ public class ClassMatcher TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex ); // compute the matching - ClassMatching matching = ClassMatcher.computeMatching( sourceIndex, sourceLoader, destIndex, destLoader ); + ClassMatching matching = computeMatching( sourceIndex, sourceLoader, destIndex, destLoader ); // start the class conversion map with the unique and ambiguous matchings Map>> conversionMap = matching.getConversionMap(); - // probabilistically match the unmatched source classes - for( ClassIdentity sourceClass : new ArrayList( matching.getUnmatchedSourceClasses() ) ) + // get all the obf class names used in the mappings + Set usedClassNames = mappings.getAllObfClassNames(); + Set allClassNames = Sets.newHashSet(); + for( ClassEntry classEntry : sourceIndex.getObfClassEntries() ) + { + allClassNames.add( classEntry.getName() ); + } + usedClassNames.retainAll( allClassNames ); + + // probabilistically match the non-uniquely-matched source classes + for( Map.Entry> entry : conversionMap.values() ) { + ClassIdentity sourceClass = entry.getKey(); + List destClasses = entry.getValue(); + + // skip classes that are uniquely matched + if( destClasses.size() == 1 ) + { + continue; + } + + // skip classes that aren't used in the mappings + if( !usedClassNames.contains( sourceClass.getClassEntry().getName() ) ) + { + continue; + } + System.out.println( "No exact match for source class " + sourceClass.getClassEntry() ); // find the closest classes - TreeMap scoredMatches = Maps.newTreeMap( Collections.reverseOrder() ); - for( ClassIdentity c : matching.getUnmatchedDestClasses() ) + Multimap scoredMatches = ArrayListMultimap.create(); + for( ClassIdentity c : destClasses ) { scoredMatches.put( sourceClass.getMatchScore( c ), c ); } - Iterator> iter = scoredMatches.entrySet().iterator(); - for( int i=0; i<10 && iter.hasNext(); i++ ) - { - Map.Entry score = iter.next(); - System.out.println( String.format( "\tScore: %3d %s", score.getKey(), score.getValue().getClassEntry().getName() ) ); - } + List scores = new ArrayList( scoredMatches.keySet() ); + Collections.sort( scores, Collections.reverseOrder() ); + printScoredMatches( sourceClass.getMaxMatchScore(), scores, scoredMatches ); // does the best match have a non-zero score and the same name? - Map.Entry bestMatch = scoredMatches.firstEntry(); - if( bestMatch.getKey() > 0 && bestMatch.getValue().getClassEntry().equals( sourceClass.getClassEntry() ) ) + int bestScore = scores.get( 0 ); + Collection bestMatches = scoredMatches.get( bestScore ); + if( bestScore > 0 && bestMatches.size() == 1 ) { - // use it - System.out.println( "\tAutomatically choosing likely match: " + bestMatch.getValue().getClassEntry().getName() ); - conversionMap.put( - sourceClass.getClassEntry().getName(), - new AbstractMap.SimpleEntry>( sourceClass, Arrays.asList( bestMatch.getValue() ) ) - ); + ClassIdentity bestMatch = bestMatches.iterator().next(); + if( bestMatch.getClassEntry().equals( sourceClass.getClassEntry() ) ) + { + // use it + System.out.println( "\tAutomatically choosing likely match: " + bestMatch.getClassEntry().getName() ); + destClasses.clear(); + destClasses.add( bestMatch ); + } } } // use the matching to convert the mappings BiMap classConversion = HashBiMap.create(); Set unmatchedSourceClasses = Sets.newHashSet(); - for( String className : mappings.getAllObfClassNames() ) + for( String className : usedClassNames ) { // is there a match for this class? Map.Entry> entry = conversionMap.get( className ); ClassIdentity sourceClass = entry.getKey(); List matches = entry.getValue(); - if( matches.isEmpty() ) - { - // no match! =( - unmatchedSourceClasses.add( className ); - } - else if( matches.size() == 1 ) + if( matches.size() == 1 ) { // unique match! We're good to go! classConversion.put( @@ -142,10 +166,21 @@ public class ClassMatcher matches.get( 0 ).getClassEntry().getName() ); } - else if( matches.size() > 1 ) + else { - // too many matches! =( - unmatchedSourceClasses.add( className ); + // no match, check the fallback matching + String fallbackMatch = fallbackMatching.get( className ); + if( fallbackMatch != null ) + { + classConversion.put( + sourceClass.getClassEntry().getName(), + fallbackMatch + ); + } + else + { + unmatchedSourceClasses.add( className ); + } } } @@ -176,17 +211,11 @@ public class ClassMatcher } } - // TEMP: show some classes - for( String className : Arrays.asList( "none/em", "none/ej", "none/en" ) ) - { - System.out.println( String.format( "check: %s -> %s", className, classConversion.get( className ) ) ); - } - - // convert the classes + // convert the mappings mappings.renameObfClasses( classConversion ); - // look for method matches - System.out.println( "Matching methods..." ); + // check the method matches + System.out.println( "Checking methods..." ); for( ClassMapping classMapping : mappings.classes() ) { ClassEntry classEntry = new ClassEntry( classMapping.getObfName() ); @@ -234,91 +263,93 @@ public class ClassMatcher } } } + + System.out.println( "Done!" ); } public static ClassMatching computeMatching( JarIndex sourceIndex, TranslatingTypeLoader sourceLoader, JarIndex destIndex, TranslatingTypeLoader destLoader ) { System.out.println( "Matching classes..." ); ClassMatching matching = null; - for( boolean useRawNames : Arrays.asList( false/*, true*/ ) ) + for( boolean useReferences : Arrays.asList( false, true ) ) { - for( boolean useReferences : Arrays.asList( false, true ) ) + int numMatches = 0; + do { - int numMatches = 0; - do + SidedClassNamer sourceNamer = null; + SidedClassNamer destNamer = null; + if( matching != null ) { - SidedClassNamer sourceNamer = null; - SidedClassNamer destNamer = null; - if( matching != null ) - { - // build a class namer - ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); - sourceNamer = namer.getSourceNamer(); - destNamer = namer.getDestNamer(); - - // note the number of matches - numMatches = matching.getUniqueMatches().size(); - } + // build a class namer + ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); + sourceNamer = namer.getSourceNamer(); + destNamer = namer.getDestNamer(); - // get the entries left to match - Set sourceClassEntries = sourceIndex.getObfClassEntries(); - Set destClassEntries = destIndex.getObfClassEntries(); - if( matching != null ) + // note the number of matches + numMatches = matching.getUniqueMatches().size(); + } + + // get the entries left to match + Set sourceClassEntries = Sets.newHashSet(); + Set destClassEntries = Sets.newHashSet(); + if( matching == null ) + { + sourceClassEntries.addAll( sourceIndex.getObfClassEntries() ); + destClassEntries.addAll( destIndex.getObfClassEntries() ); + matching = new ClassMatching(); + } + else + { + for( Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet() ) { - sourceClassEntries.clear(); - destClassEntries.clear(); - for( Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet() ) - { - for( ClassIdentity c : entry.getKey() ) - { - sourceClassEntries.add( c.getClassEntry() ); - matching.removeSource( c ); - } - for( ClassIdentity c : entry.getValue() ) - { - destClassEntries.add( c.getClassEntry() ); - matching.removeDest( c ); - } - } - for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) + for( ClassIdentity c : entry.getKey() ) { sourceClassEntries.add( c.getClassEntry() ); matching.removeSource( c ); } - for( ClassIdentity c : matching.getUnmatchedDestClasses() ) + for( ClassIdentity c : entry.getValue() ) { destClassEntries.add( c.getClassEntry() ); matching.removeDest( c ); } } - else + for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) { - matching = new ClassMatching(); + sourceClassEntries.add( c.getClassEntry() ); + matching.removeSource( c ); } - - // compute a matching for the classes - for( ClassEntry classEntry : sourceClassEntries ) - { - CtClass c = sourceLoader.loadClass( classEntry.getName() ); - ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences, useRawNames ); - matching.addSource( sourceClass ); - } - for( ClassEntry classEntry : destClassEntries ) + for( ClassIdentity c : matching.getUnmatchedDestClasses() ) { - CtClass c = destLoader.loadClass( classEntry.getName() ); - ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences, useRawNames ); - matching.matchDestClass( destClass ); + destClassEntries.add( c.getClassEntry() ); + matching.removeDest( c ); } - - // TEMP - System.out.println( matching ); } - while( matching.getUniqueMatches().size() - numMatches > 0 ); + + // compute a matching for the classes + for( ClassEntry classEntry : sourceClassEntries ) + { + CtClass c = sourceLoader.loadClass( classEntry.getName() ); + ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences ); + matching.addSource( sourceClass ); + } + for( ClassEntry classEntry : destClassEntries ) + { + CtClass c = destLoader.loadClass( classEntry.getName() ); + ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences ); + matching.matchDestClass( destClass ); + } + + // TEMP + System.out.println( matching ); } + while( matching.getUniqueMatches().size() - numMatches > 0 ); } - // DEBUG: check the class matches + // check the class matches System.out.println( "Checking class matches..." ); + ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); + SidedClassNamer sourceNamer = namer.getSourceNamer(); + SidedClassNamer destNamer = namer.getDestNamer(); for( Map.Entry entry : matching.getUniqueMatches().entrySet() ) { // check source @@ -327,7 +358,7 @@ public class ClassMatcher assert( sourceC != null ) : "Unable to load source class " + sourceClass.getClassEntry(); assert( sourceClass.matches( sourceC ) ) - : "Source " + sourceClass + " doesn't match " + new ClassIdentity( sourceC, null, sourceIndex, false, false ); + : "Source " + sourceClass + " doesn't match " + new ClassIdentity( sourceC, sourceNamer, sourceIndex, false ); // check dest ClassIdentity destClass = entry.getValue(); @@ -335,7 +366,7 @@ public class ClassMatcher assert( destC != null ) : "Unable to load dest class " + destClass.getClassEntry(); assert( destClass.matches( destC ) ) - : "Dest " + destClass + " doesn't match " + new ClassIdentity( destC, null, destIndex, false, false ); + : "Dest " + destClass + " doesn't match " + new ClassIdentity( destC, destNamer, destIndex, false ); } // warn about the ambiguous matchings @@ -372,6 +403,27 @@ public class ClassMatcher return matching; } + private static void printScoredMatches( int maxScore, List scores, Multimap scoredMatches ) + { + int numScoredMatchesShown = 0; + for( int score : scores ) + { + for( ClassIdentity scoredMatch : scoredMatches.get( score ) ) + { + System.out.println( String.format( "\tScore: %3d %3.0f%% %s", + score, + 100.0*score/maxScore, + scoredMatch.getClassEntry().getName() + ) ); + + if( numScoredMatchesShown++ > 10 ) + { + return; + } + } + } + } + private static List getClassNames( Collection classes ) { List out = Lists.newArrayList(); diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java index ef5a7d8a..6ce8c887 100644 --- a/src/cuchaz/enigma/convert/ClassMatching.java +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -183,7 +183,7 @@ public class ClassMatching { conversion.put( sourceClass.getClassEntry().getName(), - new AbstractMap.SimpleEntry>( sourceClass, new ArrayList() ) + new AbstractMap.SimpleEntry>( sourceClass, getUnmatchedDestClasses() ) ); } return conversion; @@ -193,25 +193,11 @@ public class ClassMatching public String toString( ) { StringBuilder buf = new StringBuilder(); - - buf.append( "Source classes: " ); - buf.append( getSourceClasses().size() ); - buf.append( "\n\tUnique: " ); - buf.append( getUniqueMatches().size() ); - buf.append( "\n\tAmbiguous: " ); - buf.append( getNumAmbiguousSourceMatches() ); - buf.append( "\n\tUnmatched: " ); - buf.append( getUnmatchedSourceClasses().size() ); - - buf.append( "\nDest classes: " ); - buf.append( getDestClasses().size() ); - buf.append( "\n\tUnique: " ); - buf.append( getUniqueMatches().size() ); - buf.append( "\n\tAmbiguous: " ); - buf.append( getNumAmbiguousDestMatches() ); - buf.append( "\n\tUnmatched: " ); - buf.append( getUnmatchedDestClasses().size() ); - + buf.append( String.format( "%12s%8s%8s\n", "", "Source", "Dest" ) ); + buf.append( String.format( "%12s%8d%8d\n", "Classes", getSourceClasses().size(), getDestClasses().size() ) ); + buf.append( String.format( "%12s%8d%8d\n", "Unique", getUniqueMatches().size(), getUniqueMatches().size() ) ); + buf.append( String.format( "%12s%8d%8d\n", "Ambiguous", getNumAmbiguousSourceMatches(), getNumAmbiguousDestMatches() ) ); + buf.append( String.format( "%12s%8d%8d\n", "Unmatched", getUnmatchedSourceClasses().size(), getUnmatchedDestClasses().size() ) ); return buf.toString(); } } diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index c92f8de0..4b47d160 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -15,15 +15,17 @@ import java.io.InputStream; import java.io.ObjectInputStream; import java.io.Serializable; import java.util.ArrayList; -import java.util.List; import java.util.Map; +import java.util.Set; import java.util.zip.GZIPInputStream; +import com.beust.jcommander.internal.Sets; import com.google.common.collect.Maps; import cuchaz.enigma.Util; import cuchaz.enigma.analysis.Ancestries; import cuchaz.enigma.analysis.DeobfuscatedAncestries; +import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class Mappings implements Serializable { @@ -163,9 +165,28 @@ public class Mappings implements Serializable } } - public List getAllObfClassNames( ) + public Set getAllObfClassNames( ) { - // TODO: implement this - return null; + final Set classNames = Sets.newHashSet(); + for( ClassMapping classMapping : classes() ) + { + // add the class name + classNames.add( classMapping.getObfName() ); + + // add classes from method signatures + for( MethodMapping methodMapping : classMapping.methods() ) + { + SignatureUpdater.update( methodMapping.getObfSignature(), new ClassNameUpdater( ) + { + @Override + public String update( String className ) + { + classNames.add( className ); + return className; + } + } ); + } + } + return classNames; } } -- cgit v1.2.3 From 4e9c52d5fc5d23e8a77857e712654596203acb31 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 31 Aug 2014 16:14:45 -0400 Subject: fixed mapping conversion bug with class rename order --- src/cuchaz/enigma/convert/ClassMatcher.java | 83 ++++++++++++++++++++-------- src/cuchaz/enigma/convert/ClassMatching.java | 2 +- src/cuchaz/enigma/mapping/ClassMapping.java | 36 +++++++----- src/cuchaz/enigma/mapping/Mappings.java | 28 +++------- src/cuchaz/enigma/mapping/MethodMapping.java | 16 ++++-- 5 files changed, 103 insertions(+), 62 deletions(-) diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index b551da25..0821bd3a 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -19,6 +19,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -89,9 +91,7 @@ public class ClassMatcher // compute the matching ClassMatching matching = computeMatching( sourceIndex, sourceLoader, destIndex, destLoader ); - - // start the class conversion map with the unique and ambiguous matchings - Map>> conversionMap = matching.getConversionMap(); + Map>> matchingIndex = matching.getIndex(); // get all the obf class names used in the mappings Set usedClassNames = mappings.getAllObfClassNames(); @@ -101,9 +101,10 @@ public class ClassMatcher allClassNames.add( classEntry.getName() ); } usedClassNames.retainAll( allClassNames ); + System.out.println( "Used " + usedClassNames.size() + " classes in the mappings" ); // probabilistically match the non-uniquely-matched source classes - for( Map.Entry> entry : conversionMap.values() ) + for( Map.Entry> entry : matchingIndex.values() ) { ClassIdentity sourceClass = entry.getKey(); List destClasses = entry.getValue(); @@ -148,20 +149,20 @@ public class ClassMatcher } } - // use the matching to convert the mappings - BiMap classConversion = HashBiMap.create(); - Set unmatchedSourceClasses = Sets.newHashSet(); + // group the matching into unique and non-unique matches + BiMap matchedClassNames = HashBiMap.create(); + Set unmatchedSourceClassNames = Sets.newHashSet(); for( String className : usedClassNames ) { // is there a match for this class? - Map.Entry> entry = conversionMap.get( className ); + Map.Entry> entry = matchingIndex.get( className ); ClassIdentity sourceClass = entry.getKey(); List matches = entry.getValue(); if( matches.size() == 1 ) { // unique match! We're good to go! - classConversion.put( + matchedClassNames.put( sourceClass.getClassEntry().getName(), matches.get( 0 ).getClassEntry().getName() ); @@ -172,35 +173,36 @@ public class ClassMatcher String fallbackMatch = fallbackMatching.get( className ); if( fallbackMatch != null ) { - classConversion.put( + matchedClassNames.put( sourceClass.getClassEntry().getName(), fallbackMatch ); } else { - unmatchedSourceClasses.add( className ); + unmatchedSourceClassNames.add( className ); } } } - // remove (and warn about) unmatched classes - if( !unmatchedSourceClasses.isEmpty() ) + // report unmatched classes + if( !unmatchedSourceClassNames.isEmpty() ) { - System.err.println( "WARNING: there were unmatched classes!" ); - for( String className : unmatchedSourceClasses ) + System.err.println( "ERROR: there were unmatched classes!" ); + for( String className : unmatchedSourceClassNames ) { System.err.println( "\t" + className ); - mappings.removeClassByObfName( className ); } - System.err.println( "Mappings for these classes have been removed." ); + return; } - // show the class name changes - for( Map.Entry entry : classConversion.entrySet() ) + // get the class name changes from the matched class names + Map classChanges = Maps.newHashMap(); + for( Map.Entry entry : matchedClassNames.entrySet() ) { if( !entry.getKey().equals( entry.getValue() ) ) { + classChanges.put( entry.getKey(), entry.getValue() ); System.out.println( String.format( "Class change: %s -> %s", entry.getKey(), entry.getValue() ) ); /* DEBUG System.out.println( String.format( "\n%s\n%s", @@ -211,8 +213,45 @@ public class ClassMatcher } } - // convert the mappings - mappings.renameObfClasses( classConversion ); + // sort the changes so classes are renamed in the correct order + // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b + LinkedHashMap orderedClassChanges = Maps.newLinkedHashMap(); + int numChangesLeft = classChanges.size(); + while( !classChanges.isEmpty() ) + { + Iterator> iter = classChanges.entrySet().iterator(); + while( iter.hasNext() ) + { + Map.Entry entry = iter.next(); + if( classChanges.get( entry.getValue() ) == null ) + { + orderedClassChanges.put( entry.getKey(), entry.getValue() ); + iter.remove(); + } + } + + // did we remove any changes? + if( numChangesLeft - classChanges.size() > 0 ) + { + // keep going + numChangesLeft = classChanges.size(); + } + else + { + // can't sort anymore. There must be a loop + break; + } + } + if( classChanges.size() > 0 ) + { + throw new Error( String.format( "Unable to sort %d/%d class changes!", classChanges.size(), matchedClassNames.size() ) ); + } + + // convert the mappings in the correct class order + for( Map.Entry entry : orderedClassChanges.entrySet() ) + { + mappings.renameObfClass( entry.getKey(), entry.getValue() ); + } // check the method matches System.out.println( "Checking methods..." ); @@ -250,7 +289,7 @@ public class ClassMatcher } System.err.println( "\tAvailable source methods:" ); - c = sourceLoader.loadClass( classConversion.inverse().get( classMapping.getObfName() ) ); + c = sourceLoader.loadClass( matchedClassNames.inverse().get( classMapping.getObfName() ) ); for( CtBehavior behavior : c.getDeclaredBehaviors() ) { MethodEntry declaredMethodEntry = new MethodEntry( diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java index 6ce8c887..5511902d 100644 --- a/src/cuchaz/enigma/convert/ClassMatching.java +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -159,7 +159,7 @@ public class ClassMatching return new ArrayList( m_unmatchedDestClasses ); } - public Map>> getConversionMap( ) + public Map>> getIndex( ) { Map>> conversion = Maps.newHashMap(); for( Map.Entry entry : getUniqueMatches().entrySet() ) diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 095cb385..1219e7ca 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -301,31 +301,37 @@ public class ClassMapping implements Serializable, Comparable return m_obfName.compareTo( other.m_obfName ); } - public void renameObfClasses( Map nameMap ) + public boolean renameObfClass( String oldObfClassName, String newObfClassName ) { - // rename self + // rename inner classes + for( ClassMapping innerClassMapping : new ArrayList( m_innerClassesByObf.values() ) ) { - String newName = nameMap.get( m_obfName ); - if( newName != null ) + if( innerClassMapping.renameObfClass( oldObfClassName, newObfClassName ) ) { - m_obfName = newName; + m_innerClassesByObf.remove( oldObfClassName ); + m_innerClassesByObf.put( newObfClassName, innerClassMapping ); + assert( m_innerClassesByObf.size() == m_innerClassesByDeobf.size() ); } } - // rename inner classes - for( ClassMapping classMapping : new ArrayList( m_innerClassesByObf.values() ) ) + // rename method signatures + for( MethodMapping methodMapping : new ArrayList( m_methodsByObf.values() ) ) { - m_innerClassesByObf.remove( classMapping.getObfName() ); - classMapping.renameObfClasses( nameMap ); - m_innerClassesByObf.put( classMapping.getObfName(), classMapping ); + String oldMethodKey = getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ); + if( methodMapping.renameObfClass( oldObfClassName, newObfClassName ) ) + { + m_methodsByObf.remove( oldMethodKey ); + m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping ); + assert( m_methodsByObf.size() == m_methodsByDeobf.size() ); + } } - // rename method signatures - for( MethodMapping methodMapping : new ArrayList( m_methodsByObf.values() ) ) + if( m_obfName.equals( oldObfClassName ) ) { - m_methodsByObf.remove( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ) ); - methodMapping.renameObfClasses( nameMap ); - m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping ); + // rename this class + m_obfName = newObfClassName; + return true; } + return false; } } diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 4b47d160..0b03abb0 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -15,6 +15,7 @@ import java.io.InputStream; import java.io.ObjectInputStream; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; @@ -66,7 +67,7 @@ public class Mappings implements Serializable } } - public Iterable classes( ) + public Collection classes( ) { assert( m_classesByObf.size() == m_classesByDeobf.size() ); return m_classesByObf.values(); @@ -141,30 +142,19 @@ public class Mappings implements Serializable return buf.toString(); } - public void renameObfClasses( Map nameMap ) + public void renameObfClass( String oldObfName, String newObfName ) { - for( ClassMapping classMapping : new ArrayList( m_classesByObf.values() ) ) + for( ClassMapping classMapping : new ArrayList( classes() ) ) { - String newName = nameMap.get( classMapping.getObfName() ); - if( newName != null ) + if( classMapping.renameObfClass( oldObfName, newObfName ) ) { - m_classesByObf.remove( classMapping.getObfName() ); - classMapping.renameObfClasses( nameMap ); - m_classesByObf.put( classMapping.getObfName(), classMapping ); + m_classesByObf.remove( oldObfName ); + m_classesByObf.put( newObfName, classMapping ); + assert( m_classesByObf.size() == m_classesByDeobf.size() ); } } } - - public void removeClassByObfName( String obfName ) - { - ClassMapping classMapping = m_classesByObf.get( obfName ); - if( classMapping != null ) - { - m_classesByObf.remove( classMapping.getObfName() ); - m_classesByDeobf.remove( classMapping.getDeobfName() ); - } - } - + public Set getAllObfClassNames( ) { final Set classNames = Sets.newHashSet(); diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index fe4e29b2..b0f7ba84 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -142,21 +142,27 @@ public class MethodMapping implements Serializable, Comparable return ( m_obfName + m_obfSignature ).compareTo( ( other.m_obfName + other.m_obfSignature ) ); } - public void renameObfClasses( final Map nameMap ) + public boolean renameObfClass( final String oldObfClassName, final String newObfClassName ) { // rename obf classes in the signature - m_obfSignature = SignatureUpdater.update( m_obfSignature, new ClassNameUpdater( ) + String newSignature = SignatureUpdater.update( m_obfSignature, new ClassNameUpdater( ) { @Override public String update( String className ) { - String newName = nameMap.get( className ); - if( newName != null ) + if( className.equals( oldObfClassName ) ) { - return newName; + return newObfClassName; } return className; } } ); + + if( newSignature != m_obfSignature ) + { + m_obfSignature = newSignature; + return true; + } + return false; } } -- cgit v1.2.3 From 5ba0b71cae99d99a4ef359ebccbb97ceda9c5083 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 31 Aug 2014 17:53:05 -0400 Subject: added simple loading screen for jars --- src/cuchaz/enigma/gui/Gui.java | 53 +++++++++++++++++++++++++------- src/cuchaz/enigma/gui/GuiController.java | 5 +-- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index ec0f842a..46395ac2 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -146,6 +146,8 @@ public class Gui private JList m_obfClasses; private JList m_deobfClasses; private JEditorPane m_editor; + private JPanel m_classesPanel; + private JSplitPane m_splitClasses; private JPanel m_infoPanel; private ObfuscatedHighlightPainter m_obfuscatedHighlightPainter; private DeobfuscatedHighlightPainter m_deobfuscatedHighlightPainter; @@ -262,6 +264,12 @@ public class Gui deobfPanel.add( new JLabel( "De-obfuscated Classes" ), BorderLayout.NORTH ); deobfPanel.add( deobfScroller, BorderLayout.CENTER ); + // set up classes panel (don't add the splitter yet) + m_splitClasses = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); + m_classesPanel = new JPanel(); + m_classesPanel.setLayout( new BorderLayout() ); + m_classesPanel.setPreferredSize( new Dimension( 250, 0 ) ); + // init info panel m_infoPanel = new JPanel(); m_infoPanel.setLayout( new GridLayout( 4, 1, 0, 0 ) ); @@ -563,8 +571,6 @@ public class Gui callPanel.resetToPreferredSizes(); // layout controls - JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); - splitLeft.setPreferredSize( new Dimension( 250, 0 ) ); JPanel centerPanel = new JPanel(); centerPanel.setLayout( new BorderLayout() ); centerPanel.add( m_infoPanel, BorderLayout.NORTH ); @@ -577,7 +583,7 @@ public class Gui JSplitPane splitRight = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, m_tabs ); splitRight.setResizeWeight( 1 ); // let the left side take all the slack splitRight.resetToPreferredSizes(); - JSplitPane splitCenter = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight ); + JSplitPane splitCenter = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, m_classesPanel, splitRight ); splitCenter.setResizeWeight( 0 ); // let the right side take all the slack pane.add( splitCenter, BorderLayout.CENTER ); @@ -597,14 +603,22 @@ public class Gui { if( m_jarFileChooser.showOpenDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) { - try - { - m_controller.openJar( m_jarFileChooser.getSelectedFile() ); - } - catch( IOException ex ) + // load the jar in a separate thread + new Thread( ) { - throw new Error( ex ); - } + @Override + public void run( ) + { + try + { + m_controller.openJar( m_jarFileChooser.getSelectedFile() ); + } + catch( IOException ex ) + { + throw new Error( ex ); + } + } + }.start(); } } } ); @@ -786,10 +800,22 @@ public class Gui return m_controller; } - public void onOpenJar( String jarName ) + public void onStartOpenJar( ) + { + m_classesPanel.removeAll(); + JPanel panel = new JPanel(); + panel.setLayout( new FlowLayout() ); + panel.add( new JLabel( "Loading..." ) ); + m_classesPanel.add( panel ); + redraw(); + } + + public void onFinishOpenJar( String jarName ) { // update gui m_frame.setTitle( Constants.Name + " - " + jarName ); + m_classesPanel.removeAll(); + m_classesPanel.add( m_splitClasses ); setSource( null ); // update menu @@ -798,6 +824,8 @@ public class Gui m_saveMappingsMenu.setEnabled( false ); m_saveMappingsAsMenu.setEnabled( true ); m_closeMappingsMenu.setEnabled( true ); + + redraw(); } public void onCloseJar( ) @@ -807,6 +835,7 @@ public class Gui setObfClasses( null ); setDeobfClasses( null ); setSource( null ); + m_classesPanel.removeAll(); // update menu m_closeJarMenu.setEnabled( false ); @@ -814,6 +843,8 @@ public class Gui m_saveMappingsMenu.setEnabled( false ); m_saveMappingsAsMenu.setEnabled( false ); m_closeMappingsMenu.setEnabled( false ); + + redraw(); } public void setObfClasses( List obfClasses ) diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index bd79e480..28794836 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -66,11 +66,12 @@ public class GuiController return m_isDirty; } - public void openJar( File file ) + public void openJar( final File file ) throws IOException { + m_gui.onStartOpenJar(); m_deobfuscator = new Deobfuscator( file ); - m_gui.onOpenJar( m_deobfuscator.getJarName() ); + m_gui.onFinishOpenJar( m_deobfuscator.getJarName() ); refreshClasses(); } -- cgit v1.2.3 From 30f552a3b31b4234d3a63375b6c939826b015ece Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 31 Aug 2014 21:19:57 -0400 Subject: fixed crash finding related method implementations when method doesn't implement interface --- src/cuchaz/enigma/analysis/JarIndex.java | 6 +++++- src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 4279dedd..fc19e7bc 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -663,7 +663,11 @@ public class JarIndex } // look at interface methods too - getRelatedMethodImplementations( methodEntries, getMethodImplementations( null, methodEntry ) ); + MethodImplementationsTreeNode implementations = getMethodImplementations( null, methodEntry ); + if( implementations != null ) + { + getRelatedMethodImplementations( methodEntries, implementations ); + } // recurse for( int i=0; i } return false; } + + public boolean containsDeobfField( String name ) + { + return m_fieldsByDeobf.containsKey( name ); + } + + public boolean containsDeobfMethod( String name, String signature ) + { + return m_methodsByDeobf.containsKey( getMethodKey( name, signature ) ); + } + + public boolean containsArgument( MethodEntry obfMethodEntry, String name ) + { + MethodMapping methodMapping = m_methodsByObf.get( getMethodKey( obfMethodEntry.getName(), obfMethodEntry.getSignature() ) ); + if( methodMapping != null ) + { + return methodMapping.containsArgument( name ); + } + return false; + } } diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 0b03abb0..378d4c0a 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -179,4 +179,39 @@ public class Mappings implements Serializable } return classNames; } + + public boolean containsDeobfClass( String deobfName ) + { + return m_classesByDeobf.containsKey( deobfName ); + } + + public boolean containsDeobfField( ClassEntry obfClassEntry, String deobfName ) + { + ClassMapping classMapping = m_classesByObf.get( obfClassEntry.getName() ); + if( classMapping != null ) + { + return classMapping.containsDeobfField( deobfName ); + } + return false; + } + + public boolean containsDeobfMethod( ClassEntry obfClassEntry, String deobfName, String deobfSignature ) + { + ClassMapping classMapping = m_classesByObf.get( obfClassEntry.getName() ); + if( classMapping != null ) + { + return classMapping.containsDeobfMethod( deobfName, deobfSignature ); + } + return false; + } + + public boolean containsArgument( MethodEntry obfMethodEntry, String name ) + { + ClassMapping classMapping = m_classesByObf.get( obfMethodEntry.getClassName() ); + if( classMapping != null ) + { + return classMapping.containsArgument( obfMethodEntry, name ); + } + return false; + } } diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index b0f7ba84..bf83bd21 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -165,4 +165,16 @@ public class MethodMapping implements Serializable, Comparable } return false; } + + public boolean containsArgument( String name ) + { + for( ArgumentMapping argumentMapping : m_arguments.values() ) + { + if( argumentMapping.getName().equals( name ) ) + { + return true; + } + } + return false; + } } diff --git a/src/cuchaz/enigma/mapping/Renamer.java b/src/cuchaz/enigma/mapping/Renamer.java index 79cbd30d..15d9af4d 100644 --- a/src/cuchaz/enigma/mapping/Renamer.java +++ b/src/cuchaz/enigma/mapping/Renamer.java @@ -13,6 +13,7 @@ package cuchaz.enigma.mapping; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; +import java.util.Set; import java.util.zip.GZIPOutputStream; import cuchaz.enigma.analysis.JarIndex; @@ -31,6 +32,12 @@ public class Renamer public void setClassName( ClassEntry obf, String deobfName ) { deobfName = NameValidator.validateClassName( deobfName ); + ClassEntry targetEntry = new ClassEntry( deobfName ); + if( m_mappings.containsDeobfClass( deobfName ) || m_index.containsObfClass( targetEntry ) ) + { + throw new IllegalNameException( deobfName, "There is already a class with that name" ); + } + ClassMapping classMapping = getOrCreateClassMapping( obf ); if( obf.isInnerClass() ) @@ -50,13 +57,32 @@ public class Renamer public void setFieldName( FieldEntry obf, String deobfName ) { deobfName = NameValidator.validateFieldName( deobfName ); + FieldEntry targetEntry = new FieldEntry( obf.getClassEntry(), deobfName ); + if( m_mappings.containsDeobfField( obf.getClassEntry(), deobfName ) || m_index.containsObfField( targetEntry ) ) + { + throw new IllegalNameException( deobfName, "There is already a field with that name" ); + } + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); classMapping.setFieldName( obf.getName(), deobfName ); } public void setMethodTreeName( MethodEntry obf, String deobfName ) { - for( MethodEntry entry : m_index.getRelatedMethodImplementations( obf ) ) + Set implementations = m_index.getRelatedMethodImplementations( obf ); + + deobfName = NameValidator.validateMethodName( deobfName ); + for( MethodEntry entry : implementations ) + { + MethodEntry targetEntry = new MethodEntry( entry.getClassEntry(), deobfName, entry.getSignature() ); + if( m_mappings.containsDeobfMethod( entry.getClassEntry(), deobfName, entry.getSignature() ) || m_index.containsObfMethod( targetEntry ) ) + { + String className = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ).translateClass( entry.getClassName() ); + throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + className ); + } + } + + for( MethodEntry entry : implementations ) { setMethodName( entry, deobfName ); } @@ -65,6 +91,13 @@ public class Renamer public void setMethodName( MethodEntry obf, String deobfName ) { deobfName = NameValidator.validateMethodName( deobfName ); + MethodEntry targetEntry = new MethodEntry( obf.getClassEntry(), deobfName, obf.getSignature() ); + if( m_mappings.containsDeobfMethod( obf.getClassEntry(), deobfName, obf.getSignature() ) || m_index.containsObfMethod( targetEntry ) ) + { + String className = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ).translateClass( obf.getClassName() ); + throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + className ); + } + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); String deobfSignature = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); @@ -73,6 +106,12 @@ public class Renamer public void setArgumentName( ArgumentEntry obf, String deobfName ) { deobfName = NameValidator.validateArgumentName( deobfName ); + // NOTE: don't need to check arguments for name collisions with names determined by Procyon + if( m_mappings.containsArgument( obf.getMethodEntry(), deobfName ) ) + { + throw new IllegalNameException( deobfName, "There is already an argument with that name" ); + } + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName ); } -- cgit v1.2.3 From a146283291d5529eb9363b2fbc6fd5e643dee85a Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 1 Sep 2014 20:40:11 -0400 Subject: made obfuscated/deobfuscated class selector a bit easier to use --- src/cuchaz/enigma/Deobfuscator.java | 10 +- src/cuchaz/enigma/gui/ClassSelector.java | 181 +++++++++++++++++++++ src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 38 +++++ .../enigma/gui/ClassSelectorPackageNode.java | 36 ++++ src/cuchaz/enigma/gui/Gui.java | 126 +++----------- src/cuchaz/enigma/gui/GuiController.java | 4 +- 6 files changed, 282 insertions(+), 113 deletions(-) create mode 100644 src/cuchaz/enigma/gui/ClassSelector.java create mode 100644 src/cuchaz/enigma/gui/ClassSelectorClassNode.java create mode 100644 src/cuchaz/enigma/gui/ClassSelectorPackageNode.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 9a78f38d..49aa1ff6 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -151,12 +151,12 @@ public class Deobfuscator return m_mappings.getTranslator( m_jarIndex.getAncestries(), direction ); } - public void getSeparatedClasses( List obfClasses, List deobfClasses ) + public void getSeparatedClasses( List obfClasses, List deobfClasses ) { for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() ) { // skip inner classes - if( m_jarIndex.getOuterClass( obfClassEntry.getName() ) != null ) + if( obfClassEntry.isInnerClass() ) { continue; } @@ -166,17 +166,17 @@ public class Deobfuscator if( !deobfClassEntry.equals( obfClassEntry ) ) { // if the class has a mapping, clearly it's deobfuscated - deobfClasses.add( deobfClassEntry.getName() ); + deobfClasses.add( deobfClassEntry ); } else if( !obfClassEntry.getPackageName().equals( Constants.NonePackage ) ) { // also call it deobufscated if it's not in the none package - deobfClasses.add( obfClassEntry.getName() ); + deobfClasses.add( obfClassEntry ); } else { // otherwise, assume it's still obfuscated - obfClasses.add( obfClassEntry.getName() ); + obfClasses.add( obfClassEntry ); } } } diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java new file mode 100644 index 00000000..338ad805 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassSelector.java @@ -0,0 +1,181 @@ +package cuchaz.enigma.gui; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; + +import com.beust.jcommander.internal.Lists; +import com.beust.jcommander.internal.Maps; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + +import cuchaz.enigma.mapping.ClassEntry; + +public class ClassSelector extends JTree +{ + private static final long serialVersionUID = -7632046902384775977L; + + public interface ClassSelectionListener + { + void onSelectClass( ClassEntry classEntry ); + } + + public static Comparator ObfuscatedClassEntryComparator; + public static Comparator DeobfuscatedClassEntryComparator; + + static + { + ObfuscatedClassEntryComparator = new Comparator( ) + { + @Override + public int compare( ClassEntry a, ClassEntry b ) + { + if( a.getName().length() != b.getName().length() ) + { + return a.getName().length() - b.getName().length(); + } + return a.getName().compareTo( b.getName() ); + } + }; + + DeobfuscatedClassEntryComparator = new Comparator( ) + { + @Override + public int compare( ClassEntry a, ClassEntry b ) + { + return a.getName().compareTo( b.getName() ); + } + }; + } + + private ClassSelectionListener m_listener; + private Comparator m_comparator; + + public ClassSelector( Comparator comparator ) + { + m_comparator = comparator; + + // configure the tree control + setRootVisible( false ); + setShowsRootHandles( false ); + setModel( null ); + + // hook events + addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent event ) + { + if( m_listener != null && event.getClickCount() == 2 ) + { + // get the selected node + TreePath path = getSelectionPath(); + if( path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode ) + { + ClassSelectorClassNode node = (ClassSelectorClassNode)path.getLastPathComponent(); + m_listener.onSelectClass( node.getClassEntry() ); + } + } + } + } ); + + // init defaults + m_listener = null; + } + + public void setListener( ClassSelectionListener val ) + { + m_listener = val; + } + + public void setClasses( Collection classEntries ) + { + if( classEntries == null ) + { + setModel( null ); + return; + } + + // build the package names + Map packages = Maps.newHashMap(); + for( ClassEntry classEntry : classEntries ) + { + packages.put( classEntry.getPackageName(), null ); + } + + // sort the packages + List sortedPackageNames = Lists.newArrayList( packages.keySet() ); + Collections.sort( sortedPackageNames, new Comparator( ) + { + @Override + public int compare( String a, String b ) + { + // I can never keep this rule straight when writing these damn things... + // a < b => -1, a == b => 0, a > b => +1 + + String[] aparts = a.split( "/" ); + String[] bparts = b.split( "/" ); + for( int i=0; true; i++ ) + { + if( i >= aparts.length ) + { + return -1; + } + else if( i >= bparts.length ) + { + return 1; + } + + int result = aparts[i].compareTo( bparts[i] ); + if( result != 0 ) + { + return result; + } + } + } + } ); + + // create the root node and the package nodes + DefaultMutableTreeNode root = new DefaultMutableTreeNode(); + for( String packageName : sortedPackageNames ) + { + ClassSelectorPackageNode node = new ClassSelectorPackageNode( packageName ); + packages.put( packageName, node ); + root.add( node ); + } + + // put the classes into packages + Multimap packagedClassEntries = ArrayListMultimap.create(); + for( ClassEntry classEntry : classEntries ) + { + packagedClassEntries.put( classEntry.getPackageName(), classEntry ); + } + + // build the class nodes + for( String packageName : packagedClassEntries.keySet() ) + { + // sort the class entries + List classEntriesInPackage = Lists.newArrayList( packagedClassEntries.get( packageName ) ); + Collections.sort( classEntriesInPackage, m_comparator ); + + // create the nodes in order + for( ClassEntry classEntry : classEntriesInPackage ) + { + ClassSelectorPackageNode node = packages.get( packageName ); + node.add( new ClassSelectorClassNode( classEntry ) ); + } + } + + // finally, update the tree control + setModel( new DefaultTreeModel( root ) ); + } +} diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java new file mode 100644 index 00000000..cffa7952 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import javax.swing.tree.DefaultMutableTreeNode; + +import cuchaz.enigma.mapping.ClassEntry; + +public class ClassSelectorClassNode extends DefaultMutableTreeNode +{ + private static final long serialVersionUID = -8956754339813257380L; + + private ClassEntry m_classEntry; + + public ClassSelectorClassNode( ClassEntry classEntry ) + { + m_classEntry = classEntry; + } + + public ClassEntry getClassEntry( ) + { + return m_classEntry; + } + + @Override + public String toString( ) + { + return m_classEntry.getSimpleName(); + } +} diff --git a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java new file mode 100644 index 00000000..ad88fb44 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import javax.swing.tree.DefaultMutableTreeNode; + +public class ClassSelectorPackageNode extends DefaultMutableTreeNode +{ + private static final long serialVersionUID = -3730868701219548043L; + + private String m_packageName; + + public ClassSelectorPackageNode( String packageName ) + { + m_packageName = packageName; + } + + public String getPackageName( ) + { + return m_packageName; + } + + @Override + public String toString( ) + { + return m_packageName; + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 46395ac2..8ae16f42 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -31,7 +31,6 @@ import java.io.IOException; import java.lang.Thread.UncaughtExceptionHandler; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.Vector; @@ -79,6 +78,7 @@ import cuchaz.enigma.analysis.MethodImplementationsTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.analysis.ReferenceTreeNode; import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; @@ -90,61 +90,12 @@ import cuchaz.enigma.mapping.MethodEntry; public class Gui { - private static Comparator m_obfClassSorter; - private static Comparator m_deobfClassSorter; - - static - { - m_obfClassSorter = new Comparator( ) - { - @Override - public int compare( String a, String b ) - { - if( a.length() != b.length() ) - { - return a.length() - b.length(); - } - return a.compareTo( b ); - } - }; - - m_deobfClassSorter = new Comparator( ) - { - @Override - public int compare( String a, String b ) - { - // I can never keep this rule straight when writing these damn things... - // a < b => -1, a == b => 0, a > b => +1 - - String[] aparts = a.split( "\\." ); - String[] bparts = b.split( "\\." ); - for( int i=0; true; i++ ) - { - if( i >= aparts.length ) - { - return -1; - } - else if( i >= bparts.length ) - { - return 1; - } - - int result = aparts[i].compareTo( bparts[i] ); - if( result != 0 ) - { - return result; - } - } - } - }; - } - private GuiController m_controller; // controls private JFrame m_frame; - private JList m_obfClasses; - private JList m_deobfClasses; + private ClassSelector m_obfClasses; + private ClassSelector m_deobfClasses; private JEditorPane m_editor; private JPanel m_classesPanel; private JSplitPane m_splitClasses; @@ -205,27 +156,17 @@ public class Gui m_exportFileChooser.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY ); // init obfuscated classes list - m_obfClasses = new JList(); - m_obfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); - m_obfClasses.setLayoutOrientation( JList.VERTICAL ); - m_obfClasses.setCellRenderer( new ClassListCellRenderer() ); - m_obfClasses.addMouseListener( new MouseAdapter() + m_obfClasses = new ClassSelector( ClassSelector.ObfuscatedClassEntryComparator ); + m_obfClasses.setListener( new ClassSelectionListener( ) { @Override - public void mouseClicked( MouseEvent event ) + public void onSelectClass( ClassEntry classEntry ) { - if( event.getClickCount() == 2 ) + if( m_reference != null ) { - String selected = m_obfClasses.getSelectedValue(); - if( selected != null ) - { - if( m_reference != null ) - { - m_controller.savePreviousReference( m_reference ); - } - m_controller.openDeclaration( new ClassEntry( selected ) ); - } + m_controller.savePreviousReference( m_reference ); } + m_controller.openDeclaration( classEntry ); } } ); JScrollPane obfScroller = new JScrollPane( m_obfClasses ); @@ -235,27 +176,17 @@ public class Gui obfPanel.add( obfScroller, BorderLayout.CENTER ); // init deobfuscated classes list - m_deobfClasses = new JList(); - m_deobfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); - m_deobfClasses.setLayoutOrientation( JList.VERTICAL ); - m_deobfClasses.setCellRenderer( new ClassListCellRenderer() ); - m_deobfClasses.addMouseListener( new MouseAdapter() + m_deobfClasses = new ClassSelector( ClassSelector.DeobfuscatedClassEntryComparator ); + m_deobfClasses.setListener( new ClassSelectionListener( ) { @Override - public void mouseClicked( MouseEvent event ) + public void onSelectClass( ClassEntry classEntry ) { - if( event.getClickCount() == 2 ) + if( m_reference != null ) { - String selected = m_deobfClasses.getSelectedValue(); - if( selected != null ) - { - if( m_reference != null ) - { - m_controller.savePreviousReference( m_reference ); - } - m_controller.openDeclaration( new ClassEntry( selected ) ); - } + m_controller.savePreviousReference( m_reference ); } + m_controller.openDeclaration( classEntry ); } } ); JScrollPane deobfScroller = new JScrollPane( m_deobfClasses ); @@ -266,6 +197,7 @@ public class Gui // set up classes panel (don't add the splitter yet) m_splitClasses = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); + m_splitClasses.setResizeWeight( 0.3 ); m_classesPanel = new JPanel(); m_classesPanel.setLayout( new BorderLayout() ); m_classesPanel.setPreferredSize( new Dimension( 250, 0 ) ); @@ -847,32 +779,14 @@ public class Gui redraw(); } - public void setObfClasses( List obfClasses ) + public void setObfClasses( Collection obfClasses ) { - if( obfClasses != null ) - { - Vector sortedClasses = new Vector( obfClasses ); - Collections.sort( sortedClasses, m_obfClassSorter ); - m_obfClasses.setListData( sortedClasses ); - } - else - { - m_obfClasses.setListData( new Vector() ); - } + m_obfClasses.setClasses( obfClasses ); } - public void setDeobfClasses( List deobfClasses ) + public void setDeobfClasses( Collection deobfClasses ) { - if( deobfClasses != null ) - { - Vector sortedClasses = new Vector( deobfClasses ); - Collections.sort( sortedClasses, m_deobfClassSorter ); - m_deobfClasses.setListData( sortedClasses ); - } - else - { - m_deobfClasses.setListData( new Vector() ); - } + m_deobfClasses.setClasses( deobfClasses ); } public void setMappingsFile( File file ) diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 28794836..c0fb2e40 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -315,8 +315,8 @@ public class GuiController private void refreshClasses( ) { - List obfClasses = Lists.newArrayList(); - List deobfClasses = Lists.newArrayList(); + List obfClasses = Lists.newArrayList(); + List deobfClasses = Lists.newArrayList(); m_deobfuscator.getSeparatedClasses( obfClasses, deobfClasses ); m_gui.setObfClasses( obfClasses ); m_gui.setDeobfClasses( deobfClasses ); -- cgit v1.2.3 From 0939bdc88702e5d6ba3f99e054e87bdd7cc573e4 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 1 Sep 2014 20:44:18 -0400 Subject: added copyright notice --- src/cuchaz/enigma/gui/ClassSelector.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java index 338ad805..c0f7f3c5 100644 --- a/src/cuchaz/enigma/gui/ClassSelector.java +++ b/src/cuchaz/enigma/gui/ClassSelector.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.gui; import java.awt.event.MouseAdapter; -- cgit v1.2.3 From 360bbd1c2fca8cbd575907b7d930a8072fccb0c2 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 1 Sep 2014 22:52:07 -0400 Subject: refactored jar,translation index. fixed bug with field renaming when fields are shadowed by subclasses --- src/cuchaz/enigma/Deobfuscator.java | 43 ++-- src/cuchaz/enigma/analysis/Ancestries.java | 199 ----------------- .../analysis/ClassImplementationsTreeNode.java | 4 +- .../enigma/analysis/ClassInheritanceTreeNode.java | 4 +- .../enigma/analysis/DeobfuscatedAncestries.java | 59 ----- src/cuchaz/enigma/analysis/EntryRenamer.java | 199 +++++++++++++++++ src/cuchaz/enigma/analysis/JarIndex.java | 240 ++++++--------------- .../analysis/MethodImplementationsTreeNode.java | 2 +- .../enigma/analysis/MethodInheritanceTreeNode.java | 2 +- src/cuchaz/enigma/analysis/TranslationIndex.java | 126 +++++++++++ src/cuchaz/enigma/mapping/Mappings.java | 22 +- src/cuchaz/enigma/mapping/MappingsRenamer.java | 164 ++++++++++++++ src/cuchaz/enigma/mapping/Renamer.java | 160 -------------- src/cuchaz/enigma/mapping/Translator.java | 21 +- 14 files changed, 617 insertions(+), 628 deletions(-) delete mode 100644 src/cuchaz/enigma/analysis/Ancestries.java delete mode 100644 src/cuchaz/enigma/analysis/DeobfuscatedAncestries.java create mode 100644 src/cuchaz/enigma/analysis/EntryRenamer.java create mode 100644 src/cuchaz/enigma/analysis/TranslationIndex.java create mode 100644 src/cuchaz/enigma/mapping/MappingsRenamer.java delete mode 100644 src/cuchaz/enigma/mapping/Renamer.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 49aa1ff6..0356f923 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -15,10 +15,12 @@ import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.util.List; +import java.util.Map; import java.util.jar.JarFile; import javassist.bytecode.Descriptor; +import com.google.common.collect.Maps; import com.strobel.assembler.metadata.MetadataSystem; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.decompiler.DecompilerContext; @@ -42,7 +44,7 @@ import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.MethodMapping; -import cuchaz.enigma.mapping.Renamer; +import cuchaz.enigma.mapping.MappingsRenamer; import cuchaz.enigma.mapping.TranslationDirection; import cuchaz.enigma.mapping.Translator; @@ -59,8 +61,8 @@ public class Deobfuscator private DecompilerSettings m_settings; private JarIndex m_jarIndex; private Mappings m_mappings; - private Renamer m_renamer; - private TranslatingTypeLoader m_typeLoader; + private MappingsRenamer m_renamer; + private Map m_translatorCache; public Deobfuscator( File file ) throws IOException @@ -78,6 +80,9 @@ public class Deobfuscator // DEBUG //m_settings.setShowSyntheticMembers( true ); + // init defaults + m_translatorCache = Maps.newTreeMap(); + // init mappings setMappings( new Mappings() ); } @@ -134,21 +139,19 @@ public class Deobfuscator } m_mappings = val; - m_renamer = new Renamer( m_jarIndex, m_mappings ); - - // update decompiler options - m_typeLoader = new TranslatingTypeLoader( - m_jar, - m_jarIndex, - getTranslator( TranslationDirection.Obfuscating ), - getTranslator( TranslationDirection.Deobfuscating ) - ); - m_settings.setTypeLoader( m_typeLoader ); + m_renamer = new MappingsRenamer( m_jarIndex, m_mappings ); + m_translatorCache.clear(); } public Translator getTranslator( TranslationDirection direction ) { - return m_mappings.getTranslator( m_jarIndex.getAncestries(), direction ); + Translator translator = m_translatorCache.get( direction ); + if( translator == null ) + { + translator = m_mappings.getTranslator( m_jarIndex.getTranslationIndex(), direction ); + m_translatorCache.put( direction, translator ); + } + return translator; } public void getSeparatedClasses( List obfClasses, List deobfClasses ) @@ -192,6 +195,14 @@ public class Deobfuscator className = classMapping.getDeobfName(); } + // set the type loader + m_settings.setTypeLoader( new TranslatingTypeLoader( + m_jar, + m_jarIndex, + getTranslator( TranslationDirection.Obfuscating ), + getTranslator( TranslationDirection.Deobfuscating ) + ) ); + // decompile it! TypeDefinition resolvedType = new MetadataSystem( m_settings.getTypeLoader() ).lookupType( className ).resolve(); DecompilerContext context = new DecompilerContext(); @@ -350,8 +361,8 @@ public class Deobfuscator throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); } - // clear the type loader cache - m_typeLoader.clearCache(); + // clear caches + m_translatorCache.clear(); } public boolean hasMapping( Entry obfEntry ) diff --git a/src/cuchaz/enigma/analysis/Ancestries.java b/src/cuchaz/enigma/analysis/Ancestries.java deleted file mode 100644 index 97241084..00000000 --- a/src/cuchaz/enigma/analysis/Ancestries.java +++ /dev/null @@ -1,199 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import java.io.Serializable; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javassist.bytecode.Descriptor; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; - -public class Ancestries implements Serializable -{ - private static final long serialVersionUID = 738687982126844179L; - - private Map m_superclasses; - private Multimap m_interfaces; - - public Ancestries( ) - { - m_superclasses = Maps.newHashMap(); - m_interfaces = HashMultimap.create(); - } - - public void addSuperclass( String className, String superclassName ) - { - className = Descriptor.toJvmName( className ); - superclassName = Descriptor.toJvmName( superclassName ); - - if( className.equals( superclassName ) ) - { - throw new IllegalArgumentException( "Class cannot be its own superclass! " + className ); - } - - if( !isJre( className ) && !isJre( superclassName ) ) - { - m_superclasses.put( className, superclassName ); - } - } - - public void addInterface( String className, String interfaceName ) - { - className = Descriptor.toJvmName( className ); - interfaceName = Descriptor.toJvmName( interfaceName ); - - if( className.equals( interfaceName ) ) - { - throw new IllegalArgumentException( "Class cannot be its own interface! " + className ); - } - - if( !isJre( className ) && !isJre( interfaceName ) ) - { - m_interfaces.put( className, interfaceName ); - } - } - - public void renameClasses( Map renames ) - { - // rename superclasses - Map newSuperclasses = Maps.newHashMap(); - for( Map.Entry entry : m_superclasses.entrySet() ) - { - String subclass = renames.get( entry.getKey() ); - if( subclass == null ) - { - subclass = entry.getKey(); - } - String superclass = renames.get( entry.getValue() ); - if( superclass == null ) - { - superclass = entry.getValue(); - } - newSuperclasses.put( subclass, superclass ); - } - m_superclasses = newSuperclasses; - - // rename interfaces - Set> entriesToAdd = Sets.newHashSet(); - for( Map.Entry entry : m_interfaces.entries() ) - { - String className = renames.get( entry.getKey() ); - if( className == null ) - { - className = entry.getKey(); - } - String interfaceName = renames.get( entry.getValue() ); - if( interfaceName == null ) - { - interfaceName = entry.getValue(); - } - entriesToAdd.add( new AbstractMap.SimpleEntry( className, interfaceName ) ); - } - m_interfaces.clear(); - for( Map.Entry entry : entriesToAdd ) - { - m_interfaces.put( entry.getKey(), entry.getValue() ); - } - } - - public String getSuperclassName( String className ) - { - return m_superclasses.get( className ); - } - - public List getAncestry( String className ) - { - List ancestors = new ArrayList(); - while( className != null ) - { - className = getSuperclassName( className ); - if( className != null ) - { - ancestors.add( className ); - } - } - return ancestors; - } - - public Set getInterfaces( String className ) - { - Set interfaceNames = new HashSet(); - interfaceNames.addAll( m_interfaces.get( className ) ); - for( String ancestor : getAncestry( className ) ) - { - interfaceNames.addAll( m_interfaces.get( ancestor ) ); - } - return interfaceNames; - } - - public List getSubclasses( String className ) - { - // linear search is fast enough for now - List subclasses = Lists.newArrayList(); - for( Map.Entry entry : m_superclasses.entrySet() ) - { - String subclass = entry.getKey(); - String superclass = entry.getValue(); - if( className.equals( superclass ) ) - { - subclasses.add( subclass ); - } - } - return subclasses; - } - - public Set getImplementingClasses( String targetInterfaceName ) - { - // linear search is fast enough for now - Set classNames = Sets.newHashSet(); - for( Map.Entry entry : m_interfaces.entries() ) - { - String className = entry.getKey(); - String interfaceName = entry.getValue(); - if( interfaceName.equals( targetInterfaceName ) ) - { - classNames.add( className ); - collectSubclasses( classNames, className ); - } - } - return classNames; - } - - public boolean isInterface( String className ) - { - return m_interfaces.containsValue( className ); - } - - private void collectSubclasses( Set classNames, String className ) - { - for( String subclassName : getSubclasses( className ) ) - { - classNames.add( subclassName ); - collectSubclasses( classNames, subclassName ); - } - } - - private boolean isJre( String className ) - { - return className.startsWith( "java/" ) - || className.startsWith( "javax/" ); - } -} diff --git a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java index 98648305..4e9dd523 100644 --- a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java @@ -54,11 +54,11 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode return className; } - public void load( Ancestries ancestries ) + public void load( JarIndex index ) { // get all method implementations List nodes = Lists.newArrayList(); - for( String implementingClassName : ancestries.getImplementingClasses( m_entry.getClassName() ) ) + for( String implementingClassName : index.getImplementingClasses( m_entry.getClassName() ) ) { nodes.add( new ClassImplementationsTreeNode( m_deobfuscatingTranslator, new ClassEntry( implementingClassName ) ) ); } diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java index 2ed141ff..d3fc9dc8 100644 --- a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -53,11 +53,11 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode return m_obfClassName; } - public void load( Ancestries ancestries, boolean recurse ) + public void load( TranslationIndex ancestries, boolean recurse ) { // get all the child nodes List nodes = Lists.newArrayList(); - for( String subclassName : ancestries.getSubclasses( m_obfClassName ) ) + for( String subclassName : ancestries.getSubclassNames( m_obfClassName ) ) { nodes.add( new ClassInheritanceTreeNode( m_deobfuscatingTranslator, subclassName ) ); } diff --git a/src/cuchaz/enigma/analysis/DeobfuscatedAncestries.java b/src/cuchaz/enigma/analysis/DeobfuscatedAncestries.java deleted file mode 100644 index b14eca72..00000000 --- a/src/cuchaz/enigma/analysis/DeobfuscatedAncestries.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import java.util.Map; - -import cuchaz.enigma.mapping.ClassMapping; - -public class DeobfuscatedAncestries extends Ancestries -{ - private static final long serialVersionUID = 8316248774892618324L; - - private Ancestries m_ancestries; - private Map m_classesByObf; - private Map m_classesByDeobf; - - public DeobfuscatedAncestries( Ancestries ancestries, Map classesByObf, Map classesByDeobf ) - { - m_ancestries = ancestries; - m_classesByObf = classesByObf; - m_classesByDeobf = classesByDeobf; - } - - @Override - public String getSuperclassName( String deobfClassName ) - { - // obfuscate the class name - ClassMapping classIndex = m_classesByDeobf.get( deobfClassName ); - if( classIndex == null ) - { - return null; - } - String obfClassName = classIndex.getObfName(); - - // get the superclass - String obfSuperclassName = m_ancestries.getSuperclassName( obfClassName ); - if( obfSuperclassName == null ) - { - return null; - } - - // deobfuscate the superclass name - classIndex = m_classesByObf.get( obfSuperclassName ); - if( classIndex == null ) - { - return null; - } - - return classIndex.getDeobfName(); - } -} diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java new file mode 100644 index 00000000..4b2c0b78 --- /dev/null +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.AbstractMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class EntryRenamer +{ + public static void renameClassesInSet( Map renames, Set set ) + { + List entries = Lists.newArrayList(); + for( T val : set ) + { + entries.add( renameClassesInThing( renames, val ) ); + } + set.clear(); + set.addAll( entries ); + } + + public static void renameClassesInMap( Map renames, Map map ) + { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for( Map.Entry entry : map.entrySet() ) + { + entriesToAdd.add( new AbstractMap.SimpleEntry( + renameClassesInThing( renames, entry.getKey() ), + renameClassesInThing( renames, entry.getValue() ) + ) ); + } + map.clear(); + for( Map.Entry entry : entriesToAdd ) + { + map.put( entry.getKey(), entry.getValue() ); + } + } + + public static void renameClassesInMultimap( Map renames, Multimap map ) + { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for( Map.Entry entry : map.entries() ) + { + entriesToAdd.add( new AbstractMap.SimpleEntry( + renameClassesInThing( renames, entry.getKey() ), + renameClassesInThing( renames, entry.getValue() ) + ) ); + } + map.clear(); + for( Map.Entry entry : entriesToAdd ) + { + map.put( entry.getKey(), entry.getValue() ); + } + } + + public static void renameMethodsInMultimap( Map renames, Multimap map ) + { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + Iterator> iter = map.entries().iterator(); + while( iter.hasNext() ) + { + Map.Entry entry = iter.next(); + iter.remove(); + entriesToAdd.add( new AbstractMap.SimpleEntry( + renameMethodsInThing( renames, entry.getKey() ), + renameMethodsInThing( renames, entry.getValue() ) + ) ); + } + for( Map.Entry entry : entriesToAdd ) + { + map.put( entry.getKey(), entry.getValue() ); + } + } + + @SuppressWarnings( "unchecked" ) + public static T renameMethodsInThing( Map renames, T thing ) + { + if( thing instanceof MethodEntry ) + { + MethodEntry methodEntry = (MethodEntry)thing; + MethodEntry newMethodEntry = renames.get( methodEntry ); + if( newMethodEntry != null ) + { + return (T)new MethodEntry( + methodEntry.getClassEntry(), + newMethodEntry.getName(), + methodEntry.getSignature() + ); + } + return thing; + } + else if( thing instanceof ArgumentEntry ) + { + ArgumentEntry argumentEntry = (ArgumentEntry)thing; + return (T)new ArgumentEntry( + renameMethodsInThing( renames, argumentEntry.getMethodEntry() ), + argumentEntry.getIndex(), + argumentEntry.getName() + ); + } + else if( thing instanceof EntryReference ) + { + EntryReference reference = (EntryReference)thing; + reference.entry = renameMethodsInThing( renames, reference.entry ); + reference.context = renameMethodsInThing( renames, reference.context ); + return thing; + } + return thing; + } + + @SuppressWarnings( "unchecked" ) + public static T renameClassesInThing( Map renames, T thing ) + { + if( thing instanceof String ) + { + String stringEntry = (String)thing; + if( renames.containsKey( stringEntry ) ) + { + return (T)renames.get( stringEntry ); + } + } + else if( thing instanceof ClassEntry ) + { + ClassEntry classEntry = (ClassEntry)thing; + return (T)new ClassEntry( renameClassesInThing( renames, classEntry.getClassName() ) ); + } + else if( thing instanceof FieldEntry ) + { + FieldEntry fieldEntry = (FieldEntry)thing; + return (T)new FieldEntry( + renameClassesInThing( renames, fieldEntry.getClassEntry() ), + fieldEntry.getName() + ); + } + else if( thing instanceof ConstructorEntry ) + { + ConstructorEntry constructorEntry = (ConstructorEntry)thing; + return (T)new ConstructorEntry( + renameClassesInThing( renames, constructorEntry.getClassEntry() ), + constructorEntry.getSignature() + ); + } + else if( thing instanceof MethodEntry ) + { + MethodEntry methodEntry = (MethodEntry)thing; + return (T)new MethodEntry( + renameClassesInThing( renames, methodEntry.getClassEntry() ), + methodEntry.getName(), + methodEntry.getSignature() + ); + } + else if( thing instanceof ArgumentEntry ) + { + ArgumentEntry argumentEntry = (ArgumentEntry)thing; + return (T)new ArgumentEntry( + renameClassesInThing( renames, argumentEntry.getMethodEntry() ), + argumentEntry.getIndex(), + argumentEntry.getName() + ); + } + else if( thing instanceof EntryReference ) + { + EntryReference reference = (EntryReference)thing; + reference.entry = renameClassesInThing( renames, reference.entry ); + reference.context = renameClassesInThing( renames, reference.context ); + return thing; + } + else + { + throw new Error( "Not an entry: " + thing ); + } + + return thing; + } +} diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index e7c92bea..a8ac0013 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -11,9 +11,8 @@ package cuchaz.enigma.analysis; import java.lang.reflect.Modifier; -import java.util.AbstractMap; import java.util.Collection; -import java.util.Iterator; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -43,7 +42,6 @@ import com.google.common.collect.Sets; import cuchaz.enigma.Constants; import cuchaz.enigma.bytecode.ClassRenamer; -import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; @@ -55,7 +53,8 @@ import cuchaz.enigma.mapping.Translator; public class JarIndex { private Set m_obfClassEntries; - private Ancestries m_ancestries; + private TranslationIndex m_translationIndex; + private Multimap m_interfaces; private Map m_access; private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; @@ -68,7 +67,8 @@ public class JarIndex public JarIndex( ) { m_obfClassEntries = Sets.newHashSet(); - m_ancestries = new Ancestries(); + m_translationIndex = new TranslationIndex(); + m_interfaces = HashMultimap.create(); m_access = Maps.newHashMap(); m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); @@ -109,15 +109,25 @@ public class JarIndex } } - // step 3: index the types, methods + // step 3: index extends, implements, fields, and methods for( CtClass c : JarClassIterator.classes( jar ) ) { ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); String className = Descriptor.toJvmName( c.getName() ); - m_ancestries.addSuperclass( className, Descriptor.toJvmName( c.getClassFile().getSuperclass() ) ); + m_translationIndex.addSuperclass( className, Descriptor.toJvmName( c.getClassFile().getSuperclass() ) ); for( String interfaceName : c.getClassFile().getInterfaces() ) { - m_ancestries.addInterface( className, Descriptor.toJvmName( interfaceName ) ); + className = Descriptor.toJvmName( className ); + interfaceName = Descriptor.toJvmName( interfaceName ); + if( className.equals( interfaceName ) ) + { + throw new IllegalArgumentException( "Class cannot be its own interface! " + className ); + } + m_interfaces.put( className, interfaceName ); + } + for( CtField field : c.getDeclaredFields() ) + { + indexField( field ); } for( CtBehavior behavior : c.getDeclaredBehaviors() ) { @@ -159,11 +169,24 @@ public class JarIndex { renames.put( entry.getKey(), entry.getValue() + "$" + new ClassEntry( entry.getKey() ).getSimpleName() ); } - renameClasses( renames ); + EntryRenamer.renameClassesInSet( renames, m_obfClassEntries ); + m_translationIndex.renameClasses( renames ); + EntryRenamer.renameClassesInMultimap( renames, m_interfaces ); + EntryRenamer.renameClassesInMultimap( renames, m_methodImplementations ); + EntryRenamer.renameClassesInMultimap( renames, m_behaviorReferences ); + EntryRenamer.renameClassesInMultimap( renames, m_fieldReferences ); } - // step 5: update other indices with bridge method info - renameMethods( m_bridgeMethods ); + // step 6: update other indices with bridge method info + EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_methodImplementations ); + EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_behaviorReferences ); + EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_fieldReferences ); + } + + private void indexField( CtField field ) + { + String className = Descriptor.toJvmName( field.getDeclaringClass().getName() ); + m_translationIndex.addField( className, field.getName() ); } private void indexBehavior( CtBehavior behavior ) @@ -528,9 +551,9 @@ public class JarIndex return m_obfClassEntries; } - public Ancestries getAncestries( ) + public TranslationIndex getTranslationIndex( ) { - return m_ancestries; + return m_translationIndex; } public Access getAccess( Entry entry ) @@ -553,11 +576,11 @@ public class JarIndex // get the root node List ancestry = Lists.newArrayList(); ancestry.add( obfClassEntry.getName() ); - ancestry.addAll( m_ancestries.getAncestry( obfClassEntry.getName() ) ); + ancestry.addAll( m_translationIndex.getAncestry( obfClassEntry.getName() ) ); ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, ancestry.get( ancestry.size() - 1 ) ); // expand all children recursively - rootNode.load( m_ancestries, true ); + rootNode.load( m_translationIndex, true ); return rootNode; } @@ -565,7 +588,7 @@ public class JarIndex public ClassImplementationsTreeNode getClassImplementations( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) { ClassImplementationsTreeNode node = new ClassImplementationsTreeNode( deobfuscatingTranslator, obfClassEntry ); - node.load( m_ancestries ); + node.load( this ); return node; } @@ -573,7 +596,7 @@ public class JarIndex { // travel to the ancestor implementation String baseImplementationClassName = obfMethodEntry.getClassName(); - for( String ancestorClassName : m_ancestries.getAncestry( obfMethodEntry.getClassName() ) ) + for( String ancestorClassName : m_translationIndex.getAncestry( obfMethodEntry.getClassName() ) ) { MethodEntry ancestorMethodEntry = new MethodEntry( new ClassEntry( ancestorClassName ), @@ -609,7 +632,7 @@ public class JarIndex MethodEntry interfaceMethodEntry; // is this method on an interface? - if( m_ancestries.isInterface( obfMethodEntry.getClassName() ) ) + if( isInterface( obfMethodEntry.getClassName() ) ) { interfaceMethodEntry = obfMethodEntry; } @@ -617,7 +640,7 @@ public class JarIndex { // get the interface class List methodInterfaces = Lists.newArrayList(); - for( String interfaceName : m_ancestries.getInterfaces( obfMethodEntry.getClassName() ) ) + for( String interfaceName : getInterfaces( obfMethodEntry.getClassName() ) ) { // is this method defined in this interface? MethodEntry methodInterface = new MethodEntry( @@ -717,181 +740,44 @@ public class JarIndex return m_anonymousClasses.contains( obfInnerClassName ); } - public MethodEntry getBridgeMethod( MethodEntry methodEntry ) - { - return m_bridgeMethods.get( methodEntry ); - } - - private void renameClasses( Map renames ) + public Set getInterfaces( String className ) { - // rename class entries - Set obfClassEntries = Sets.newHashSet(); - for( ClassEntry classEntry : m_obfClassEntries ) + Set interfaceNames = new HashSet(); + interfaceNames.addAll( m_interfaces.get( className ) ); + for( String ancestor : m_translationIndex.getAncestry( className ) ) { - if( renames.containsKey( classEntry.getName() ) ) - { - obfClassEntries.add( new ClassEntry( renames.get( classEntry.getName() ) ) ); - } - else - { - obfClassEntries.add( classEntry ); - } + interfaceNames.addAll( m_interfaces.get( ancestor ) ); } - m_obfClassEntries = obfClassEntries; - - // rename others - m_ancestries.renameClasses( renames ); - renameClassesInMultimap( renames, m_methodImplementations ); - renameClassesInMultimap( renames, m_behaviorReferences ); - renameClassesInMultimap( renames, m_fieldReferences ); + return interfaceNames; } - private void renameMethods( Map renames ) + public Set getImplementingClasses( String targetInterfaceName ) { - renameMethodsInMultimap( renames, m_methodImplementations ); - renameMethodsInMultimap( renames, m_behaviorReferences ); - renameMethodsInMultimap( renames, m_fieldReferences ); - } - - private void renameClassesInMultimap( Map renames, Multimap map ) - { - // for each key/value pair... - Set> entriesToAdd = Sets.newHashSet(); - for( Map.Entry entry : map.entries() ) + // linear search is fast enough for now + Set classNames = Sets.newHashSet(); + for( Map.Entry entry : m_interfaces.entries() ) { - entriesToAdd.add( new AbstractMap.SimpleEntry( - renameClassesInThing( renames, entry.getKey() ), - renameClassesInThing( renames, entry.getValue() ) - ) ); - } - map.clear(); - for( Map.Entry entry : entriesToAdd ) - { - map.put( entry.getKey(), entry.getValue() ); - } - } - - @SuppressWarnings( "unchecked" ) - private T renameClassesInThing( Map renames, T thing ) - { - if( thing instanceof String ) - { - String stringEntry = (String)thing; - if( renames.containsKey( stringEntry ) ) + String className = entry.getKey(); + String interfaceName = entry.getValue(); + if( interfaceName.equals( targetInterfaceName ) ) { - return (T)renames.get( stringEntry ); + classNames.add( className ); + m_translationIndex.getSubclassNamesRecursively( classNames, className ); } } - else if( thing instanceof ClassEntry ) - { - ClassEntry classEntry = (ClassEntry)thing; - return (T)new ClassEntry( renameClassesInThing( renames, classEntry.getClassName() ) ); - } - else if( thing instanceof FieldEntry ) - { - FieldEntry fieldEntry = (FieldEntry)thing; - return (T)new FieldEntry( - renameClassesInThing( renames, fieldEntry.getClassEntry() ), - fieldEntry.getName() - ); - } - else if( thing instanceof ConstructorEntry ) - { - ConstructorEntry constructorEntry = (ConstructorEntry)thing; - return (T)new ConstructorEntry( - renameClassesInThing( renames, constructorEntry.getClassEntry() ), - constructorEntry.getSignature() - ); - } - else if( thing instanceof MethodEntry ) - { - MethodEntry methodEntry = (MethodEntry)thing; - return (T)new MethodEntry( - renameClassesInThing( renames, methodEntry.getClassEntry() ), - methodEntry.getName(), - methodEntry.getSignature() - ); - } - else if( thing instanceof ArgumentEntry ) - { - ArgumentEntry argumentEntry = (ArgumentEntry)thing; - return (T)new ArgumentEntry( - renameClassesInThing( renames, argumentEntry.getMethodEntry() ), - argumentEntry.getIndex(), - argumentEntry.getName() - ); - } - else if( thing instanceof EntryReference ) - { - EntryReference reference = (EntryReference)thing; - reference.entry = renameClassesInThing( renames, reference.entry ); - reference.context = renameClassesInThing( renames, reference.context ); - return thing; - } - else - { - throw new Error( "Not an entry: " + thing ); - } - - return thing; + return classNames; } - private void renameMethodsInMultimap( Map renames, Multimap map ) + public boolean isInterface( String className ) { - // for each key/value pair... - Set> entriesToAdd = Sets.newHashSet(); - Iterator> iter = map.entries().iterator(); - while( iter.hasNext() ) - { - Map.Entry entry = iter.next(); - iter.remove(); - entriesToAdd.add( new AbstractMap.SimpleEntry( - renameMethodsInThing( renames, entry.getKey() ), - renameMethodsInThing( renames, entry.getValue() ) - ) ); - } - for( Map.Entry entry : entriesToAdd ) - { - map.put( entry.getKey(), entry.getValue() ); - } + return m_interfaces.containsValue( className ); } - @SuppressWarnings( "unchecked" ) - private T renameMethodsInThing( Map renames, T thing ) + public MethodEntry getBridgeMethod( MethodEntry methodEntry ) { - if( thing instanceof MethodEntry ) - { - MethodEntry methodEntry = (MethodEntry)thing; - MethodEntry newMethodEntry = renames.get( methodEntry ); - if( newMethodEntry != null ) - { - return (T)new MethodEntry( - methodEntry.getClassEntry(), - newMethodEntry.getName(), - methodEntry.getSignature() - ); - } - return thing; - } - else if( thing instanceof ArgumentEntry ) - { - ArgumentEntry argumentEntry = (ArgumentEntry)thing; - return (T)new ArgumentEntry( - renameMethodsInThing( renames, argumentEntry.getMethodEntry() ), - argumentEntry.getIndex(), - argumentEntry.getName() - ); - } - else if( thing instanceof EntryReference ) - { - EntryReference reference = (EntryReference)thing; - reference.entry = renameMethodsInThing( renames, reference.entry ); - reference.context = renameMethodsInThing( renames, reference.context ); - return thing; - } - return thing; + return m_bridgeMethods.get( methodEntry ); } - + public boolean containsObfClass( ClassEntry obfClassEntry ) { return m_obfClassEntries.contains( obfClassEntry ); diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index b7434e84..8b9dd2d8 100644 --- a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -74,7 +74,7 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { // get all method implementations List nodes = Lists.newArrayList(); - for( String implementingClassName : index.getAncestries().getImplementingClasses( m_entry.getClassName() ) ) + for( String implementingClassName : index.getImplementingClasses( m_entry.getClassName() ) ) { MethodEntry methodEntry = new MethodEntry( new ClassEntry( implementingClassName ), diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index 73f9714c..d77fd858 100644 --- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -83,7 +83,7 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { // get all the child nodes List nodes = Lists.newArrayList(); - for( String subclassName : index.getAncestries().getSubclasses( m_entry.getClassName() ) ) + for( String subclassName : index.getTranslationIndex().getSubclassNames( m_entry.getClassName() ) ) { MethodEntry methodEntry = new MethodEntry( new ClassEntry( subclassName ), diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java new file mode 100644 index 00000000..5311ec70 --- /dev/null +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javassist.bytecode.Descriptor; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +public class TranslationIndex implements Serializable +{ + private static final long serialVersionUID = 738687982126844179L; + + private Map m_superclasses; + private Multimap m_fields; + + public TranslationIndex( ) + { + m_superclasses = Maps.newHashMap(); + m_fields = HashMultimap.create(); + } + + public TranslationIndex( TranslationIndex other ) + { + m_superclasses = Maps.newHashMap( other.m_superclasses ); + m_fields = HashMultimap.create( other.m_fields ); + } + + public void addSuperclass( String className, String superclassName ) + { + className = Descriptor.toJvmName( className ); + superclassName = Descriptor.toJvmName( superclassName ); + + if( className.equals( superclassName ) ) + { + throw new IllegalArgumentException( "Class cannot be its own superclass! " + className ); + } + + if( !isJre( className ) && !isJre( superclassName ) ) + { + m_superclasses.put( className, superclassName ); + } + } + + public void addField( String className, String fieldName ) + { + m_fields.put( className, fieldName ); + } + + public void renameClasses( Map renames ) + { + EntryRenamer.renameClassesInMap( renames, m_superclasses ); + EntryRenamer.renameClassesInMultimap( renames, m_fields ); + } + + public String getSuperclassName( String className ) + { + return m_superclasses.get( className ); + } + + public List getAncestry( String className ) + { + List ancestors = new ArrayList(); + while( className != null ) + { + className = getSuperclassName( className ); + if( className != null ) + { + ancestors.add( className ); + } + } + return ancestors; + } + + public List getSubclassNames( String className ) + { + // linear search is fast enough for now + List subclasses = Lists.newArrayList(); + for( Map.Entry entry : m_superclasses.entrySet() ) + { + String subclass = entry.getKey(); + String superclass = entry.getValue(); + if( className.equals( superclass ) ) + { + subclasses.add( subclass ); + } + } + return subclasses; + } + + public void getSubclassNamesRecursively( Set out, String className ) + { + for( String subclassName : getSubclassNames( className ) ) + { + out.add( subclassName ); + getSubclassNamesRecursively( out, subclassName ); + } + } + + public boolean containsField( String className, String fieldName ) + { + return m_fields.containsEntry( className, fieldName ); + } + + private boolean isJre( String className ) + { + return className.startsWith( "java/" ) + || className.startsWith( "javax/" ); + } +} diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 378d4c0a..f52094fd 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -24,8 +24,7 @@ import com.beust.jcommander.internal.Sets; import com.google.common.collect.Maps; import cuchaz.enigma.Util; -import cuchaz.enigma.analysis.Ancestries; -import cuchaz.enigma.analysis.DeobfuscatedAncestries; +import cuchaz.enigma.analysis.TranslationIndex; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class Mappings implements Serializable @@ -108,12 +107,27 @@ public class Mappings implements Serializable return m_classesByDeobf.get( deobfName ); } - public Translator getTranslator( Ancestries ancestries, TranslationDirection direction ) + public Translator getTranslator( TranslationIndex index, TranslationDirection direction ) { + if( direction == TranslationDirection.Obfuscating ) + { + // deobfuscate the index + index = new TranslationIndex( index ); + Map renames = Maps.newHashMap(); + for( ClassMapping classMapping : classes() ) + { + renames.put( classMapping.getObfName(), classMapping.getDeobfName() ); + for( ClassMapping innerClassMapping : classMapping.innerClasses() ) + { + renames.put( innerClassMapping.getObfName(), innerClassMapping.getDeobfName() ); + } + } + index.renameClasses( renames ); + } return new Translator( direction, direction.choose( m_classesByObf, m_classesByDeobf ), - direction.choose( ancestries, new DeobfuscatedAncestries( ancestries, m_classesByObf, m_classesByDeobf ) ) + index ); } diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java new file mode 100644 index 00000000..9d036d8f --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.util.Set; +import java.util.zip.GZIPOutputStream; + +import cuchaz.enigma.analysis.JarIndex; + +public class MappingsRenamer +{ + private JarIndex m_index; + private Mappings m_mappings; + + public MappingsRenamer( JarIndex index, Mappings mappings ) + { + m_index = index; + m_mappings = mappings; + } + + public void setClassName( ClassEntry obf, String deobfName ) + { + deobfName = NameValidator.validateClassName( deobfName ); + ClassEntry targetEntry = new ClassEntry( deobfName ); + if( m_mappings.containsDeobfClass( deobfName ) || m_index.containsObfClass( targetEntry ) ) + { + throw new IllegalNameException( deobfName, "There is already a class with that name" ); + } + + ClassMapping classMapping = getOrCreateClassMapping( obf ); + + if( obf.isInnerClass() ) + { + classMapping.setInnerClassName( obf.getInnerClassName(), deobfName ); + } + else + { + m_mappings.m_classesByDeobf.remove( classMapping.getDeobfName() ); + classMapping.setDeobfName( deobfName ); + m_mappings.m_classesByDeobf.put( deobfName, classMapping ); + } + + updateDeobfMethodSignatures(); + } + + public void setFieldName( FieldEntry obf, String deobfName ) + { + deobfName = NameValidator.validateFieldName( deobfName ); + FieldEntry targetEntry = new FieldEntry( obf.getClassEntry(), deobfName ); + if( m_mappings.containsDeobfField( obf.getClassEntry(), deobfName ) || m_index.containsObfField( targetEntry ) ) + { + throw new IllegalNameException( deobfName, "There is already a field with that name" ); + } + + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); + classMapping.setFieldName( obf.getName(), deobfName ); + } + + public void setMethodTreeName( MethodEntry obf, String deobfName ) + { + Set implementations = m_index.getRelatedMethodImplementations( obf ); + + deobfName = NameValidator.validateMethodName( deobfName ); + for( MethodEntry entry : implementations ) + { + MethodEntry targetEntry = new MethodEntry( entry.getClassEntry(), deobfName, entry.getSignature() ); + if( m_mappings.containsDeobfMethod( entry.getClassEntry(), deobfName, entry.getSignature() ) || m_index.containsObfMethod( targetEntry ) ) + { + String deobfClassName = getTranslator( TranslationDirection.Deobfuscating ).translateClass( entry.getClassName() ); + throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + deobfClassName ); + } + } + + for( MethodEntry entry : implementations ) + { + setMethodName( entry, deobfName ); + } + } + + public void setMethodName( MethodEntry obf, String deobfName ) + { + deobfName = NameValidator.validateMethodName( deobfName ); + MethodEntry targetEntry = new MethodEntry( obf.getClassEntry(), deobfName, obf.getSignature() ); + if( m_mappings.containsDeobfMethod( obf.getClassEntry(), deobfName, obf.getSignature() ) || m_index.containsObfMethod( targetEntry ) ) + { + String deobfClassName = getTranslator( TranslationDirection.Deobfuscating ).translateClass( obf.getClassName() ); + throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + deobfClassName ); + } + + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); + String deobfSignature = getTranslator( TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); + classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); + } + + public void setArgumentName( ArgumentEntry obf, String deobfName ) + { + deobfName = NameValidator.validateArgumentName( deobfName ); + // NOTE: don't need to check arguments for name collisions with names determined by Procyon + if( m_mappings.containsArgument( obf.getMethodEntry(), deobfName ) ) + { + throw new IllegalNameException( deobfName, "There is already an argument with that name" ); + } + + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); + classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName ); + } + + public void write( OutputStream out ) + throws IOException + { + // TEMP: just use the object output for now. We can find a more efficient storage format later + GZIPOutputStream gzipout = new GZIPOutputStream( out ); + ObjectOutputStream oout = new ObjectOutputStream( gzipout ); + oout.writeObject( this ); + gzipout.finish(); + } + + private ClassMapping getOrCreateClassMapping( ClassEntry obfClassEntry ) + { + String obfClassName = obfClassEntry.getOuterClassName(); + ClassMapping classMapping = m_mappings.m_classesByObf.get( obfClassName ); + if( classMapping == null ) + { + classMapping = new ClassMapping( obfClassName, obfClassName ); + m_mappings.m_classesByObf.put( classMapping.getObfName(), classMapping ); + m_mappings.m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ); + } + return classMapping; + } + + private ClassMapping getOrCreateClassMappingOrInnerClassMapping( ClassEntry obfClassEntry ) + { + ClassMapping classMapping = getOrCreateClassMapping( obfClassEntry ); + if( obfClassEntry.isInnerClass() ) + { + classMapping = classMapping.getOrCreateInnerClass( obfClassEntry.getInnerClassName() ); + } + return classMapping; + } + + private void updateDeobfMethodSignatures( ) + { + for( ClassMapping classMapping : m_mappings.m_classesByObf.values() ) + { + classMapping.updateDeobfMethodSignatures( getTranslator( TranslationDirection.Deobfuscating ) ); + } + } + + private Translator getTranslator( TranslationDirection direction ) + { + return m_mappings.getTranslator( m_index.getTranslationIndex(), direction ); + } +} diff --git a/src/cuchaz/enigma/mapping/Renamer.java b/src/cuchaz/enigma/mapping/Renamer.java deleted file mode 100644 index 15d9af4d..00000000 --- a/src/cuchaz/enigma/mapping/Renamer.java +++ /dev/null @@ -1,160 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.mapping; - -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.util.Set; -import java.util.zip.GZIPOutputStream; - -import cuchaz.enigma.analysis.JarIndex; - -public class Renamer -{ - private JarIndex m_index; - private Mappings m_mappings; - - public Renamer( JarIndex index, Mappings mappings ) - { - m_index = index; - m_mappings = mappings; - } - - public void setClassName( ClassEntry obf, String deobfName ) - { - deobfName = NameValidator.validateClassName( deobfName ); - ClassEntry targetEntry = new ClassEntry( deobfName ); - if( m_mappings.containsDeobfClass( deobfName ) || m_index.containsObfClass( targetEntry ) ) - { - throw new IllegalNameException( deobfName, "There is already a class with that name" ); - } - - ClassMapping classMapping = getOrCreateClassMapping( obf ); - - if( obf.isInnerClass() ) - { - classMapping.setInnerClassName( obf.getInnerClassName(), deobfName ); - } - else - { - m_mappings.m_classesByDeobf.remove( classMapping.getDeobfName() ); - classMapping.setDeobfName( deobfName ); - m_mappings.m_classesByDeobf.put( deobfName, classMapping ); - } - - updateDeobfMethodSignatures(); - } - - public void setFieldName( FieldEntry obf, String deobfName ) - { - deobfName = NameValidator.validateFieldName( deobfName ); - FieldEntry targetEntry = new FieldEntry( obf.getClassEntry(), deobfName ); - if( m_mappings.containsDeobfField( obf.getClassEntry(), deobfName ) || m_index.containsObfField( targetEntry ) ) - { - throw new IllegalNameException( deobfName, "There is already a field with that name" ); - } - - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); - classMapping.setFieldName( obf.getName(), deobfName ); - } - - public void setMethodTreeName( MethodEntry obf, String deobfName ) - { - Set implementations = m_index.getRelatedMethodImplementations( obf ); - - deobfName = NameValidator.validateMethodName( deobfName ); - for( MethodEntry entry : implementations ) - { - MethodEntry targetEntry = new MethodEntry( entry.getClassEntry(), deobfName, entry.getSignature() ); - if( m_mappings.containsDeobfMethod( entry.getClassEntry(), deobfName, entry.getSignature() ) || m_index.containsObfMethod( targetEntry ) ) - { - String className = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ).translateClass( entry.getClassName() ); - throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + className ); - } - } - - for( MethodEntry entry : implementations ) - { - setMethodName( entry, deobfName ); - } - } - - public void setMethodName( MethodEntry obf, String deobfName ) - { - deobfName = NameValidator.validateMethodName( deobfName ); - MethodEntry targetEntry = new MethodEntry( obf.getClassEntry(), deobfName, obf.getSignature() ); - if( m_mappings.containsDeobfMethod( obf.getClassEntry(), deobfName, obf.getSignature() ) || m_index.containsObfMethod( targetEntry ) ) - { - String className = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ).translateClass( obf.getClassName() ); - throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + className ); - } - - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); - String deobfSignature = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); - classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); - } - - public void setArgumentName( ArgumentEntry obf, String deobfName ) - { - deobfName = NameValidator.validateArgumentName( deobfName ); - // NOTE: don't need to check arguments for name collisions with names determined by Procyon - if( m_mappings.containsArgument( obf.getMethodEntry(), deobfName ) ) - { - throw new IllegalNameException( deobfName, "There is already an argument with that name" ); - } - - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); - classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName ); - } - - public void write( OutputStream out ) - throws IOException - { - // TEMP: just use the object output for now. We can find a more efficient storage format later - GZIPOutputStream gzipout = new GZIPOutputStream( out ); - ObjectOutputStream oout = new ObjectOutputStream( gzipout ); - oout.writeObject( this ); - gzipout.finish(); - } - - private ClassMapping getOrCreateClassMapping( ClassEntry obfClassEntry ) - { - String obfClassName = obfClassEntry.getOuterClassName(); - ClassMapping classMapping = m_mappings.m_classesByObf.get( obfClassName ); - if( classMapping == null ) - { - classMapping = new ClassMapping( obfClassName, obfClassName ); - m_mappings.m_classesByObf.put( classMapping.getObfName(), classMapping ); - m_mappings.m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ); - } - return classMapping; - } - - private ClassMapping getOrCreateClassMappingOrInnerClassMapping( ClassEntry obfClassEntry ) - { - ClassMapping classMapping = getOrCreateClassMapping( obfClassEntry ); - if( obfClassEntry.isInnerClass() ) - { - classMapping = classMapping.getOrCreateInnerClass( obfClassEntry.getInnerClassName() ); - } - return classMapping; - } - - private void updateDeobfMethodSignatures( ) - { - Translator translator = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ); - for( ClassMapping classMapping : m_mappings.m_classesByObf.values() ) - { - classMapping.updateDeobfMethodSignatures( translator ); - } - } -} diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 23bf0951..f5aafdd1 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -16,27 +16,27 @@ import java.util.Map; import com.beust.jcommander.internal.Maps; -import cuchaz.enigma.analysis.Ancestries; +import cuchaz.enigma.analysis.TranslationIndex; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class Translator { private TranslationDirection m_direction; - public Map m_classes; - private Ancestries m_ancestries; + private Map m_classes; + private TranslationIndex m_index; public Translator( ) { m_direction = null; m_classes = Maps.newHashMap(); - m_ancestries = new Ancestries(); + m_index = new TranslationIndex(); } - protected Translator( TranslationDirection direction, Map classes, Ancestries ancestries ) + public Translator( TranslationDirection direction, Map classes, TranslationIndex index ) { m_direction = direction; m_classes = classes; - m_ancestries = ancestries; + m_index = index; } @SuppressWarnings( "unchecked" ) @@ -145,6 +145,13 @@ public class Translator return translatedName; } } + + // is the field implemented in this class? + if( m_index.containsField( className, in.getName() ) ) + { + // stop traversing the superclass chain + break; + } } return null; } @@ -291,7 +298,7 @@ public class Translator { List ancestry = new ArrayList(); ancestry.add( className ); - ancestry.addAll( m_ancestries.getAncestry( className ) ); + ancestry.addAll( m_index.getAncestry( className ) ); return ancestry; } -- cgit v1.2.3 From ddcbdd2a18cca1d3ad05ab8d20eacf98ba035874 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 2 Sep 2014 00:34:57 -0400 Subject: fixed bug with export progress bar --- src/cuchaz/enigma/Deobfuscator.java | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 0356f923..d15c25f9 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -16,10 +16,12 @@ import java.io.IOException; import java.io.StringWriter; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.jar.JarFile; import javassist.bytecode.Descriptor; +import com.beust.jcommander.internal.Sets; import com.google.common.collect.Maps; import com.strobel.assembler.metadata.MetadataSystem; import com.strobel.assembler.metadata.TypeDefinition; @@ -42,9 +44,9 @@ import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsRenamer; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.MethodMapping; -import cuchaz.enigma.mapping.MappingsRenamer; import cuchaz.enigma.mapping.TranslationDirection; import cuchaz.enigma.mapping.Translator; @@ -246,18 +248,12 @@ public class Deobfuscator public void writeSources( File dirOut, ProgressListener progress ) throws IOException { - int numClasses = m_jarIndex.getObfClassEntries().size(); - if( progress != null ) - { - progress.init( numClasses ); - } - int i = 0; - - // DEOBFUSCATE ALL THE THINGS!! @_@ + // get the classes to decompile + Set classEntries = Sets.newHashSet(); for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() ) { // skip inner classes - if( m_jarIndex.getOuterClass( obfClassEntry.getName() ) != null ) + if( obfClassEntry.isInnerClass() ) { continue; } @@ -268,6 +264,18 @@ public class Deobfuscator continue; } + classEntries.add( obfClassEntry ); + } + + if( progress != null ) + { + progress.init( classEntries.size() ); + } + + // DEOBFUSCATE ALL THE THINGS!! @_@ + int i = 0; + for( ClassEntry obfClassEntry : classEntries ) + { ClassEntry deobfClassEntry = deobfuscateEntry( new ClassEntry( obfClassEntry ) ); if( progress != null ) { @@ -287,7 +295,7 @@ public class Deobfuscator } // done! - progress.onProgress( numClasses, "Done!" ); + progress.onProgress( classEntries.size(), "Done!" ); } public T obfuscateEntry( T deobfEntry ) -- cgit v1.2.3 From b5338883d271779c335842c07047d60136316167 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 3 Sep 2014 00:20:36 -0400 Subject: big refactor to better model class/method mappings with no deobf name --- src/cuchaz/enigma/Deobfuscator.java | 2 +- src/cuchaz/enigma/gui/GuiController.java | 2 + src/cuchaz/enigma/mapping/ClassMapping.java | 111 +++++++++++------- src/cuchaz/enigma/mapping/Mappings.java | 90 +++++++++----- src/cuchaz/enigma/mapping/MappingsReader.java | 155 ++++++++++++++----------- src/cuchaz/enigma/mapping/MappingsRenamer.java | 14 ++- src/cuchaz/enigma/mapping/MappingsWriter.java | 29 ++++- src/cuchaz/enigma/mapping/MethodMapping.java | 16 ++- src/cuchaz/enigma/mapping/NameValidator.java | 12 +- 9 files changed, 276 insertions(+), 155 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index d15c25f9..526534da 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -192,7 +192,7 @@ public class Deobfuscator // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out // the decompiler only sees the deobfuscated class, so we need to load it by the deobfuscated name ClassMapping classMapping = m_mappings.getClassByObf( className ); - if( classMapping != null ) + if( classMapping != null && classMapping.getDeobfName() != null ) { className = classMapping.getDeobfName(); } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index c0fb2e40..5057db2d 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -167,6 +167,8 @@ public class GuiController public boolean entryHasMapping( Entry deobfEntry ) { + // TEMP + System.out.println( deobfEntry + " -> " + m_deobfuscator.obfuscateEntry( deobfEntry ) ); return m_deobfuscator.hasMapping( m_deobfuscator.obfuscateEntry( deobfEntry ) ); } diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 5faaf2a1..bce16cc5 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -29,7 +29,11 @@ public class ClassMapping implements Serializable, Comparable private Map m_methodsByObf; private Map m_methodsByDeobf; - // NOTE: this argument order is important for the MethodReader/MethodWriter + public ClassMapping( String obfName ) + { + this( obfName, null ); + } + public ClassMapping( String obfName, String deobfName ) { m_obfName = obfName; @@ -60,14 +64,19 @@ public class ClassMapping implements Serializable, Comparable public Iterable innerClasses( ) { - assert( m_innerClassesByObf.size() == m_innerClassesByDeobf.size() ); + assert( m_innerClassesByObf.size() >= m_innerClassesByDeobf.size() ); return m_innerClassesByObf.values(); } - protected void addInnerClassMapping( ClassMapping classMapping ) + public void addInnerClassMapping( ClassMapping classMapping ) { - m_innerClassesByObf.put( classMapping.getObfName(), classMapping ); - m_innerClassesByDeobf.put( classMapping.getDeobfName(), classMapping ); + boolean obfWasAdded = m_innerClassesByObf.put( classMapping.getObfName(), classMapping ) == null; + assert( obfWasAdded ); + if( classMapping.getDeobfName() != null ) + { + boolean deobfWasAdded = m_innerClassesByDeobf.put( classMapping.getDeobfName(), classMapping ) == null; + assert( deobfWasAdded ); + } } public ClassMapping getOrCreateInnerClass( String obfName ) @@ -75,9 +84,9 @@ public class ClassMapping implements Serializable, Comparable ClassMapping classMapping = m_innerClassesByObf.get( obfName ); if( classMapping == null ) { - classMapping = new ClassMapping( obfName, obfName ); - m_innerClassesByObf.put( obfName, classMapping ); - m_innerClassesByDeobf.put( obfName, classMapping ); + classMapping = new ClassMapping( obfName ); + boolean wasAdded = m_innerClassesByObf.put( obfName, classMapping ) == null; + assert( wasAdded ); } return classMapping; } @@ -115,9 +124,11 @@ public class ClassMapping implements Serializable, Comparable public void setInnerClassName( String obfName, String deobfName ) { ClassMapping classMapping = getOrCreateInnerClass( obfName ); - m_innerClassesByDeobf.remove( classMapping.getDeobfName() ); + boolean wasRemoved = m_innerClassesByDeobf.remove( classMapping.getDeobfName() ) != null; + assert( wasRemoved ); classMapping.setDeobfName( deobfName ); - m_innerClassesByDeobf.put( deobfName, classMapping ); + boolean wasAdded = m_innerClassesByDeobf.put( deobfName, classMapping ) == null; + assert( wasAdded ); } //// FIELDS //////// @@ -128,7 +139,7 @@ public class ClassMapping implements Serializable, Comparable return m_fieldsByObf.values(); } - protected void addFieldMapping( FieldMapping fieldMapping ) + public void addFieldMapping( FieldMapping fieldMapping ) { if( m_fieldsByObf.containsKey( fieldMapping.getObfName() ) ) { @@ -138,8 +149,10 @@ public class ClassMapping implements Serializable, Comparable { throw new Error( "Already have mapping for " + m_deobfName + "." + fieldMapping.getDeobfName() ); } - m_fieldsByObf.put( fieldMapping.getObfName(), fieldMapping ); - m_fieldsByDeobf.put( fieldMapping.getDeobfName(), fieldMapping ); + boolean obfWasAdded = m_fieldsByObf.put( fieldMapping.getObfName(), fieldMapping ) == null; + assert( obfWasAdded ); + boolean deobfWasAdded = m_fieldsByDeobf.put( fieldMapping.getDeobfName(), fieldMapping ) == null; + assert( deobfWasAdded ); assert( m_fieldsByObf.size() == m_fieldsByDeobf.size() ); } @@ -169,38 +182,47 @@ public class ClassMapping implements Serializable, Comparable if( fieldMapping == null ) { fieldMapping = new FieldMapping( obfName, deobfName ); - m_fieldsByObf.put( obfName, fieldMapping ); - m_fieldsByDeobf.put( deobfName, fieldMapping ); + boolean obfWasAdded = m_fieldsByObf.put( obfName, fieldMapping ) == null; + assert( obfWasAdded ); + } + else + { + boolean wasRemoved = m_fieldsByDeobf.remove( fieldMapping.getDeobfName() ) != null; + assert( wasRemoved ); } - - m_fieldsByDeobf.remove( fieldMapping.getDeobfName() ); fieldMapping.setDeobfName( deobfName ); - m_fieldsByDeobf.put( deobfName, fieldMapping ); + boolean wasAdded = m_fieldsByDeobf.put( deobfName, fieldMapping ) == null; + assert( wasAdded ); } //// METHODS //////// public Iterable methods( ) { - assert( m_methodsByObf.size() == m_methodsByDeobf.size() ); + assert( m_methodsByObf.size() >= m_methodsByDeobf.size() ); return m_methodsByObf.values(); } - protected void addMethodMapping( MethodMapping methodMapping ) + public void addMethodMapping( MethodMapping methodMapping ) { String obfKey = getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ); - String deobfKey = getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ); if( m_methodsByObf.containsKey( obfKey ) ) { throw new Error( "Already have mapping for " + m_obfName + "." + obfKey ); } - if( m_methodsByDeobf.containsKey( deobfKey ) ) + boolean wasAdded = m_methodsByObf.put( obfKey, methodMapping ) == null; + assert( wasAdded ); + if( methodMapping.getDeobfName() != null ) { - throw new Error( "Already have mapping for " + m_deobfName + "." + deobfKey ); + String deobfKey = getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ); + if( m_methodsByDeobf.containsKey( deobfKey ) ) + { + throw new Error( "Already have mapping for " + m_deobfName + "." + deobfKey ); + } + boolean deobfWasAdded = m_methodsByDeobf.put( deobfKey, methodMapping ) == null; + assert( deobfWasAdded ); } - m_methodsByObf.put( obfKey, methodMapping ); - m_methodsByDeobf.put( deobfKey, methodMapping ); - assert( m_methodsByObf.size() == m_methodsByDeobf.size() ); + assert( m_methodsByObf.size() >= m_methodsByDeobf.size() ); } public MethodMapping getMethodByObf( String obfName, String signature ) @@ -231,13 +253,17 @@ public class ClassMapping implements Serializable, Comparable MethodMapping methodMapping = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) ); if( methodMapping == null ) { - methodMapping = createMethodIndex( obfName, obfSignature ); + methodMapping = createMethodMapping( obfName, obfSignature ); + } + else + { + boolean wasRemoved = m_methodsByDeobf.remove( getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ) ) != null; + assert( wasRemoved ); } - - m_methodsByDeobf.remove( getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ) ); methodMapping.setDeobfName( deobfName ); methodMapping.setDeobfSignature( deobfSignature ); - m_methodsByDeobf.put( getMethodKey( deobfName, deobfSignature ), methodMapping ); + boolean wasAdded = m_methodsByDeobf.put( getMethodKey( deobfName, deobfSignature ), methodMapping ) == null; + assert( wasAdded ); } public void updateDeobfMethodSignatures( Translator translator ) @@ -255,17 +281,16 @@ public class ClassMapping implements Serializable, Comparable MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ); if( methodIndex == null ) { - methodIndex = createMethodIndex( obfMethodName, obfMethodSignature ); + methodIndex = createMethodMapping( obfMethodName, obfMethodSignature ); } methodIndex.setArgumentName( argumentIndex, argumentName ); } - private MethodMapping createMethodIndex( String obfName, String obfSignature ) + private MethodMapping createMethodMapping( String obfName, String obfSignature ) { - MethodMapping methodMapping = new MethodMapping( obfName, obfName, obfSignature, obfSignature ); - String key = getMethodKey( obfName, obfSignature ); - m_methodsByObf.put( key, methodMapping ); - m_methodsByDeobf.put( key, methodMapping ); + MethodMapping methodMapping = new MethodMapping( obfName, obfSignature ); + boolean wasAdded = m_methodsByObf.put( getMethodKey( obfName, obfSignature ), methodMapping ) == null; + assert( wasAdded ); return methodMapping; } @@ -308,9 +333,10 @@ public class ClassMapping implements Serializable, Comparable { if( innerClassMapping.renameObfClass( oldObfClassName, newObfClassName ) ) { - m_innerClassesByObf.remove( oldObfClassName ); - m_innerClassesByObf.put( newObfClassName, innerClassMapping ); - assert( m_innerClassesByObf.size() == m_innerClassesByDeobf.size() ); + boolean wasRemoved = m_innerClassesByObf.remove( oldObfClassName ) != null; + assert( wasRemoved ); + boolean wasAdded = m_innerClassesByObf.put( newObfClassName, innerClassMapping ) == null; + assert( wasAdded ); } } @@ -320,9 +346,10 @@ public class ClassMapping implements Serializable, Comparable String oldMethodKey = getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ); if( methodMapping.renameObfClass( oldObfClassName, newObfClassName ) ) { - m_methodsByObf.remove( oldMethodKey ); - m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping ); - assert( m_methodsByObf.size() == m_methodsByDeobf.size() ); + boolean wasRemoved = m_methodsByObf.remove( oldMethodKey ) != null; + assert( wasRemoved ); + boolean wasAdded = m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping ) == null; + assert( wasAdded ); } } diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index f52094fd..99cb85f0 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -47,7 +47,10 @@ public class Mappings implements Serializable for( ClassMapping classMapping : classes ) { m_classesByObf.put( classMapping.getObfName(), classMapping ); - m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ); + if( classMapping.getDeobfName() != null ) + { + m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ); + } } } @@ -68,23 +71,27 @@ public class Mappings implements Serializable public Collection classes( ) { - assert( m_classesByObf.size() == m_classesByDeobf.size() ); + assert( m_classesByObf.size() >= m_classesByDeobf.size() ); return m_classesByObf.values(); } - protected void addClassMapping( ClassMapping classMapping ) + public void addClassMapping( ClassMapping classMapping ) { if( m_classesByObf.containsKey( classMapping.getObfName() ) ) { throw new Error( "Already have mapping for " + classMapping.getObfName() ); } - if( m_classesByDeobf.containsKey( classMapping.getDeobfName() ) ) + boolean obfWasAdded = m_classesByObf.put( classMapping.getObfName(), classMapping ) == null; + assert( obfWasAdded ); + if( classMapping.getDeobfName() != null ) { - throw new Error( "Already have mapping for " + classMapping.getDeobfName() ); + if( m_classesByDeobf.containsKey( classMapping.getDeobfName() ) ) + { + throw new Error( "Already have mapping for " + classMapping.getDeobfName() ); + } + boolean deobfWasAdded = m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ) == null; + assert( deobfWasAdded ); } - m_classesByObf.put( classMapping.getObfName(), classMapping ); - m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ); - assert( m_classesByObf.size() == m_classesByDeobf.size() ); } public ClassMapping getClassByObf( ClassEntry entry ) @@ -99,7 +106,7 @@ public class Mappings implements Serializable public ClassMapping getClassByDeobf( ClassEntry entry ) { - return getClassByObf( entry.getName() ); + return getClassByDeobf( entry.getName() ); } public ClassMapping getClassByDeobf( String deobfName ) @@ -109,26 +116,52 @@ public class Mappings implements Serializable public Translator getTranslator( TranslationIndex index, TranslationDirection direction ) { - if( direction == TranslationDirection.Obfuscating ) + switch( direction ) { - // deobfuscate the index - index = new TranslationIndex( index ); - Map renames = Maps.newHashMap(); - for( ClassMapping classMapping : classes() ) - { - renames.put( classMapping.getObfName(), classMapping.getDeobfName() ); - for( ClassMapping innerClassMapping : classMapping.innerClasses() ) + case Deobfuscating: + + return new Translator( direction, m_classesByObf, index ); + + case Obfuscating: + + // deobfuscate the index + index = new TranslationIndex( index ); + Map renames = Maps.newHashMap(); + for( ClassMapping classMapping : classes() ) { - renames.put( innerClassMapping.getObfName(), innerClassMapping.getDeobfName() ); + if( classMapping.getDeobfName() != null ) + { + renames.put( classMapping.getObfName(), classMapping.getDeobfName() ); + } + for( ClassMapping innerClassMapping : classMapping.innerClasses() ) + { + if( innerClassMapping.getDeobfName() != null ) + { + renames.put( innerClassMapping.getObfName(), innerClassMapping.getDeobfName() ); + } + } } - } - index.renameClasses( renames ); + index.renameClasses( renames ); + + // fill in the missing deobf class entries with obf entries + Map classes = Maps.newHashMap(); + for( ClassMapping classMapping : classes() ) + { + if( classMapping.getDeobfName() != null ) + { + classes.put( classMapping.getDeobfName(), classMapping ); + } + else + { + classes.put( classMapping.getObfName(), classMapping ); + } + } + + return new Translator( direction, classes, index ); + + default: + throw new Error( "Invalid translation direction!" ); } - return new Translator( - direction, - direction.choose( m_classesByObf, m_classesByDeobf ), - index - ); } public static Mappings newFromStream( InputStream in ) @@ -162,9 +195,10 @@ public class Mappings implements Serializable { if( classMapping.renameObfClass( oldObfName, newObfName ) ) { - m_classesByObf.remove( oldObfName ); - m_classesByObf.put( newObfName, classMapping ); - assert( m_classesByObf.size() == m_classesByDeobf.size() ); + boolean wasRemoved = m_classesByObf.remove( oldObfName ) != null; + assert( wasRemoved ); + boolean wasAdded = m_classesByObf.put( newObfName, classMapping ) == null; + assert( wasAdded ); } } } diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index 9f42b42c..7888836e 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -14,13 +14,10 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.util.Deque; -import java.util.NoSuchElementException; -import java.util.Scanner; import com.google.common.collect.Queues; import cuchaz.enigma.Constants; -import cuchaz.enigma.Util; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class MappingsReader @@ -73,89 +70,99 @@ public class MappingsReader mappingStack.pop(); } - Scanner scanner = new Scanner( line ); + String[] parts = line.trim().split( "\\s" ); try { - while( scanner.hasNext() ) + // read the first token + String token = parts[0]; + + if( token.equalsIgnoreCase( "CLASS" ) ) { - // read the first token - String token = scanner.next(); - - if( token.equalsIgnoreCase( "CLASS" ) ) + ClassMapping classMapping = readClass( parts ); + if( indent == 0 ) { - ClassMapping classMapping = readClass( scanner ); - if( indent == 0 ) - { - // outer class - mappings.addClassMapping( classMapping ); - } - else if( indent == 1 ) - { - // inner class - if( !( mappingStack.getFirst() instanceof ClassMapping ) ) - { - throw new MappingParseException( lineNumber, "Unexpected CLASS entry here!" ); - } - ((ClassMapping)mappingStack.getFirst()).addInnerClassMapping( classMapping ); - } - else + // outer class + mappings.addClassMapping( classMapping ); + } + else if( indent == 1 ) + { + // inner class + if( !( mappingStack.getFirst() instanceof ClassMapping ) ) { throw new MappingParseException( lineNumber, "Unexpected CLASS entry here!" ); } - mappingStack.push( classMapping ); + ((ClassMapping)mappingStack.getFirst()).addInnerClassMapping( classMapping ); } - else if( token.equalsIgnoreCase( "FIELD" ) ) + else { - if( mappingStack.isEmpty() || !(mappingStack.getFirst() instanceof ClassMapping) ) - { - throw new MappingParseException( lineNumber, "Unexpected FIELD entry here!" ); - } - ((ClassMapping)mappingStack.getFirst()).addFieldMapping( readField( scanner ) ); + throw new MappingParseException( lineNumber, "Unexpected CLASS entry here!" ); } - else if( token.equalsIgnoreCase( "METHOD" ) ) + mappingStack.push( classMapping ); + } + else if( token.equalsIgnoreCase( "FIELD" ) ) + { + if( mappingStack.isEmpty() || !(mappingStack.getFirst() instanceof ClassMapping) ) { - if( mappingStack.isEmpty() || !(mappingStack.getFirst() instanceof ClassMapping) ) - { - throw new MappingParseException( lineNumber, "Unexpected METHOD entry here!" ); - } - MethodMapping methodMapping = readMethod( scanner ); - ((ClassMapping)mappingStack.getFirst()).addMethodMapping( methodMapping ); - mappingStack.push( methodMapping ); + throw new MappingParseException( lineNumber, "Unexpected FIELD entry here!" ); } - else if( token.equalsIgnoreCase( "ARG" ) ) + ((ClassMapping)mappingStack.getFirst()).addFieldMapping( readField( parts ) ); + } + else if( token.equalsIgnoreCase( "METHOD" ) ) + { + if( mappingStack.isEmpty() || !(mappingStack.getFirst() instanceof ClassMapping) ) { - if( mappingStack.isEmpty() || !(mappingStack.getFirst() instanceof MethodMapping) ) - { - throw new MappingParseException( lineNumber, "Unexpected ARG entry here!" ); - } - ((MethodMapping)mappingStack.getFirst()).addArgumentMapping( readArgument( scanner ) ); + throw new MappingParseException( lineNumber, "Unexpected METHOD entry here!" ); + } + MethodMapping methodMapping = readMethod( parts ); + ((ClassMapping)mappingStack.getFirst()).addMethodMapping( methodMapping ); + mappingStack.push( methodMapping ); + } + else if( token.equalsIgnoreCase( "ARG" ) ) + { + if( mappingStack.isEmpty() || !(mappingStack.getFirst() instanceof MethodMapping) ) + { + throw new MappingParseException( lineNumber, "Unexpected ARG entry here!" ); } + ((MethodMapping)mappingStack.getFirst()).addArgumentMapping( readArgument( parts ) ); } } - catch( NoSuchElementException ex ) + catch( ArrayIndexOutOfBoundsException | NumberFormatException ex ) { throw new MappingParseException( lineNumber, "Malformed line!" ); } - finally - { - Util.closeQuietly( scanner ); - } } return mappings; } - private ArgumentMapping readArgument( Scanner scanner ) + private ArgumentMapping readArgument( String[] parts ) { - return new ArgumentMapping( scanner.nextInt(), scanner.next() ); + return new ArgumentMapping( Integer.parseInt( parts[1] ), parts[2] ); } - private ClassMapping readClass( Scanner scanner ) + private ClassMapping readClass( String[] parts ) { - return new ClassMapping( - moveClassOutOfDefaultPackage( scanner.next(), Constants.NonePackage ), - moveClassOutOfDefaultPackage( scanner.next(), Constants.NonePackage ) - ); + if( parts.length == 2 ) + { + String obfName = parts[1]; + return new ClassMapping( moveClassOutOfDefaultPackage( obfName, Constants.NonePackage ) ); + } + else + { + String obfName = parts[1]; + String deobfName = parts[2]; + if( obfName.equals( deobfName ) ) + { + return new ClassMapping( moveClassOutOfDefaultPackage( obfName, Constants.NonePackage ) ); + } + else + { + return new ClassMapping( + moveClassOutOfDefaultPackage( parts[1], Constants.NonePackage ), + moveClassOutOfDefaultPackage( parts[2], Constants.NonePackage ) + ); + } + } } private String moveClassOutOfDefaultPackage( String className, String newPackageName ) @@ -168,18 +175,34 @@ public class MappingsReader return className; } - private FieldMapping readField( Scanner scanner ) + private FieldMapping readField( String[] parts ) { - return new FieldMapping( scanner.next(), scanner.next() ); + return new FieldMapping( parts[1], parts[2] ); } - private MethodMapping readMethod( Scanner scanner ) + private MethodMapping readMethod( String[] parts ) { - return new MethodMapping( - scanner.next(), scanner.next(), - moveSignatureOutOfDefaultPackage( scanner.next(), Constants.NonePackage ), - moveSignatureOutOfDefaultPackage( scanner.next(), Constants.NonePackage ) - ); + if( parts.length == 3 ) + { + String obfName = parts[1]; + String obfSignature = moveSignatureOutOfDefaultPackage( parts[2], Constants.NonePackage ); + return new MethodMapping( obfName, obfSignature ); + } + else + { + String obfName = parts[1]; + String deobfName = parts[2]; + String obfSignature = moveSignatureOutOfDefaultPackage( parts[3], Constants.NonePackage ); + String deobfSignature = moveSignatureOutOfDefaultPackage( parts[4], Constants.NonePackage ); + if( obfName.equals( deobfName ) ) + { + return new MethodMapping( obfName, obfSignature ); + } + else + { + return new MethodMapping( obfName, obfSignature, deobfName, deobfSignature ); + } + } } private String moveSignatureOutOfDefaultPackage( String signature, final String newPackageName ) diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index 9d036d8f..f84b2489 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -46,9 +46,11 @@ public class MappingsRenamer } else { - m_mappings.m_classesByDeobf.remove( classMapping.getDeobfName() ); + boolean wasRemoved = m_mappings.m_classesByDeobf.remove( classMapping.getDeobfName() ) != null; + assert( wasRemoved ); classMapping.setDeobfName( deobfName ); - m_mappings.m_classesByDeobf.put( deobfName, classMapping ); + boolean wasAdded = m_mappings.m_classesByDeobf.put( deobfName, classMapping ) == null; + assert( wasAdded ); } updateDeobfMethodSignatures(); @@ -132,9 +134,11 @@ public class MappingsRenamer ClassMapping classMapping = m_mappings.m_classesByObf.get( obfClassName ); if( classMapping == null ) { - classMapping = new ClassMapping( obfClassName, obfClassName ); - m_mappings.m_classesByObf.put( classMapping.getObfName(), classMapping ); - m_mappings.m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ); + classMapping = new ClassMapping( obfClassName ); + boolean obfWasAdded = m_mappings.m_classesByObf.put( classMapping.getObfName(), classMapping ) == null; + assert( obfWasAdded ); + boolean deobfWasAdded = m_mappings.m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ) == null; + assert( deobfWasAdded ); } return classMapping; } diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java index 62035713..ea6e6558 100644 --- a/src/cuchaz/enigma/mapping/MappingsWriter.java +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -37,7 +37,14 @@ public class MappingsWriter private void write( PrintWriter out, ClassMapping classMapping, int depth ) throws IOException { - out.format( "%sCLASS %s %s\n", getIndent( depth ), classMapping.getObfName(), classMapping.getDeobfName() ); + if( classMapping.getDeobfName() == null ) + { + out.format( "%sCLASS %s\n", getIndent( depth ), classMapping.getObfName() ); + } + else + { + out.format( "%sCLASS %s %s\n", getIndent( depth ), classMapping.getObfName(), classMapping.getDeobfName() ); + } for( ClassMapping innerClassMapping : sorted( classMapping.innerClasses() ) ) { @@ -64,11 +71,21 @@ public class MappingsWriter private void write( PrintWriter out, MethodMapping methodMapping, int depth ) throws IOException { - out.format( "%sMETHOD %s %s %s %s\n", - getIndent( depth ), - methodMapping.getObfName(), methodMapping.getDeobfName(), - methodMapping.getObfSignature(), methodMapping.getDeobfSignature() - ); + if( methodMapping.getDeobfName() == null ) + { + out.format( "%sMETHOD %s %s\n", + getIndent( depth ), + methodMapping.getObfName(), methodMapping.getObfSignature() + ); + } + else + { + out.format( "%sMETHOD %s %s %s %s\n", + getIndent( depth ), + methodMapping.getObfName(), methodMapping.getDeobfName(), + methodMapping.getObfSignature(), methodMapping.getDeobfSignature() + ); + } for( ArgumentMapping argumentMapping : sorted( methodMapping.arguments() ) ) { diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index bf83bd21..e59cb2eb 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -26,8 +26,12 @@ public class MethodMapping implements Serializable, Comparable private String m_deobfSignature; private Map m_arguments; - // NOTE: this argument order is important for the MethodReader/MethodWriter - public MethodMapping( String obfName, String deobfName, String obfSignature, String deobfSignature ) + public MethodMapping( String obfName, String obfSignature ) + { + this( obfName, obfSignature, null, null ); + } + + public MethodMapping( String obfName, String obfSignature, String deobfName, String deobfSignature ) { m_obfName = obfName; m_deobfName = NameValidator.validateMethodName( deobfName ); @@ -69,9 +73,10 @@ public class MethodMapping implements Serializable, Comparable return m_arguments.values(); } - protected void addArgumentMapping( ArgumentMapping argumentMapping ) + public void addArgumentMapping( ArgumentMapping argumentMapping ) { - m_arguments.put( argumentMapping.getIndex(), argumentMapping ); + boolean wasAdded = m_arguments.put( argumentMapping.getIndex(), argumentMapping ) == null; + assert( wasAdded ); } public String getObfArgumentName( int index ) @@ -102,7 +107,8 @@ public class MethodMapping implements Serializable, Comparable if( argumentMapping == null ) { argumentMapping = new ArgumentMapping( index, name ); - m_arguments.put( index, argumentMapping ); + boolean wasAdded = m_arguments.put( index, argumentMapping ) == null; + assert( wasAdded ); } else { diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java index 6df893fb..9adf1ac1 100644 --- a/src/cuchaz/enigma/mapping/NameValidator.java +++ b/src/cuchaz/enigma/mapping/NameValidator.java @@ -57,7 +57,11 @@ public class NameValidator public static String validateClassName( String name ) { - if( name == null || !ClassPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) + if( name == null ) + { + return null; + } + if( !ClassPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) { throw new IllegalNameException( name, "This doesn't look like a legal class name" ); } @@ -70,7 +74,11 @@ public class NameValidator public static String validateFieldName( String name ) { - if( name == null || !IdentifierPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) + if( name == null ) + { + return null; + } + if( !IdentifierPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) { throw new IllegalNameException( name, "This doesn't look like a legal identifier" ); } -- cgit v1.2.3 From 3632307d9c5665edfca976e08931dc6cadf510a0 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 3 Sep 2014 00:29:48 -0400 Subject: fixed issue with method renaming --- src/cuchaz/enigma/gui/GuiController.java | 2 -- src/cuchaz/enigma/mapping/ClassMapping.java | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 5057db2d..c0fb2e40 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -167,8 +167,6 @@ public class GuiController public boolean entryHasMapping( Entry deobfEntry ) { - // TEMP - System.out.println( deobfEntry + " -> " + m_deobfuscator.obfuscateEntry( deobfEntry ) ); return m_deobfuscator.hasMapping( m_deobfuscator.obfuscateEntry( deobfEntry ) ); } diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index bce16cc5..6a89df7f 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -255,7 +255,7 @@ public class ClassMapping implements Serializable, Comparable { methodMapping = createMethodMapping( obfName, obfSignature ); } - else + else if( methodMapping.getDeobfName() != null ) { boolean wasRemoved = m_methodsByDeobf.remove( getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ) ) != null; assert( wasRemoved ); -- cgit v1.2.3 From 5bb9b46e729ba7953a48932d586bce6c30d813c4 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 3 Sep 2014 00:54:55 -0400 Subject: repackaged for 0.5 beta --- build.gradle | 2 +- readme.txt | 2 +- src/cuchaz/enigma/Constants.java | 2 +- src/cuchaz/enigma/convert/ClassMatcher.java | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index 3cc975db..7409f936 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ targetCompatibility = 1.7 group = "com.cuchazinteractive" archivesBaseName = "enigma" -version = "0.4.2b" +version = "0.5b" sourceSets { diff --git a/readme.txt b/readme.txt index ca4d7a5c..1c5c386f 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ -Enigma v0.4.2 beta +Enigma v0.5 beta A tool for deobfuscation of Java bytecode Copyright Jeff Martin, 2014 diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index c8631429..bf6ab846 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -14,7 +14,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.4.2 beta"; + public static final String Version = "0.5 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024*1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index 0821bd3a..135d076d 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -55,16 +55,16 @@ public class ClassMatcher throws IOException, MappingParseException { // TEMP - JarFile sourceJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); - JarFile destJar = new JarFile( new File( "input/1.8-pre3.jar" ) ); - File inMappingsFile = new File( "../minecraft-mappings/1.8-pre.mappings" ); - File outMappingsFile = new File( "../minecraft-mappings/1.8-pre3.mappings" ); + JarFile sourceJar = new JarFile( new File( "input/1.8-pre3.jar" ) ); + JarFile destJar = new JarFile( new File( "input/1.8.jar" ) ); + File inMappingsFile = new File( "../minecraft-mappings/1.8-pre3.mappings" ); + File outMappingsFile = new File( "../minecraft-mappings/1.8.mappings" ); // define a matching to use when the automated system cannot find a match Map fallbackMatching = Maps.newHashMap(); - fallbackMatching.put( "none/ayb", "none/ayb" ); + fallbackMatching.put( "none/ayb", "none/ayf" ); fallbackMatching.put( "none/ayd", "none/ayd" ); - fallbackMatching.put( "none/bgk", "none/bgk" ); + fallbackMatching.put( "none/bgk", "unknown/bgk" ); // do the conversion Mappings mappings = new MappingsReader().read( new FileReader( inMappingsFile ) ); -- cgit v1.2.3 From ac89449bee522e620c8c45e648fea2d255635c01 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 3 Sep 2014 00:55:16 -0400 Subject: updated ignore list --- .hgignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.hgignore b/.hgignore index ddf86ccd..d552dedf 100644 --- a/.hgignore +++ b/.hgignore @@ -12,4 +12,6 @@ syntax: regexp syntax: regexp ^build$ syntax: regexp -^data$ \ No newline at end of file +^data$ +syntax: regexp +^input$ \ No newline at end of file -- cgit v1.2.3 From 1faf3bee250f75d8c13708ab875a881a5b9cb6ed Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 3 Sep 2014 23:56:11 -0400 Subject: removed deobfuscated method signatures from mappings They're too much work to maintain, and they're totally unnecessary! --- src/cuchaz/enigma/Deobfuscator.java | 5 ++++- src/cuchaz/enigma/mapping/ClassMapping.java | 17 ++++------------ src/cuchaz/enigma/mapping/MappingsReader.java | 3 +-- src/cuchaz/enigma/mapping/MappingsRenamer.java | 25 ++++++++---------------- src/cuchaz/enigma/mapping/MappingsWriter.java | 4 ++-- src/cuchaz/enigma/mapping/MethodMapping.java | 27 +++++++++++--------------- src/cuchaz/enigma/mapping/Translator.java | 4 ++-- 7 files changed, 32 insertions(+), 53 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 526534da..8944eec7 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -1,5 +1,6 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2014 Jeff Martin.\ + * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at @@ -78,7 +79,9 @@ public class Deobfuscator // config the decompiler m_settings = DecompilerSettings.javaDefaults(); + m_settings.setMergeVariables( true ); m_settings.setForceExplicitImports( true ); + m_settings.setForceExplicitTypeArguments( true ); // DEBUG //m_settings.setShowSyntheticMembers( true ); diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 6a89df7f..200d9ca2 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -214,7 +214,7 @@ public class ClassMapping implements Serializable, Comparable assert( wasAdded ); if( methodMapping.getDeobfName() != null ) { - String deobfKey = getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ); + String deobfKey = getMethodKey( methodMapping.getDeobfName(), methodMapping.getObfSignature() ); if( m_methodsByDeobf.containsKey( deobfKey ) ) { throw new Error( "Already have mapping for " + m_deobfName + "." + deobfKey ); @@ -248,7 +248,7 @@ public class ClassMapping implements Serializable, Comparable return name + signature; } - public void setMethodNameAndSignature( String obfName, String obfSignature, String deobfName, String deobfSignature ) + public void setMethodName( String obfName, String obfSignature, String deobfName ) { MethodMapping methodMapping = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) ); if( methodMapping == null ) @@ -257,23 +257,14 @@ public class ClassMapping implements Serializable, Comparable } else if( methodMapping.getDeobfName() != null ) { - boolean wasRemoved = m_methodsByDeobf.remove( getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ) ) != null; + boolean wasRemoved = m_methodsByDeobf.remove( getMethodKey( methodMapping.getDeobfName(), methodMapping.getObfSignature() ) ) != null; assert( wasRemoved ); } methodMapping.setDeobfName( deobfName ); - methodMapping.setDeobfSignature( deobfSignature ); - boolean wasAdded = m_methodsByDeobf.put( getMethodKey( deobfName, deobfSignature ), methodMapping ) == null; + boolean wasAdded = m_methodsByDeobf.put( getMethodKey( deobfName, obfSignature ), methodMapping ) == null; assert( wasAdded ); } - public void updateDeobfMethodSignatures( Translator translator ) - { - for( MethodMapping methodIndex : m_methodsByObf.values() ) - { - methodIndex.setDeobfSignature( translator.translateSignature( methodIndex.getObfSignature() ) ); - } - } - //// ARGUMENTS //////// public void setArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName ) diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index 7888836e..5cbad59c 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -193,14 +193,13 @@ public class MappingsReader String obfName = parts[1]; String deobfName = parts[2]; String obfSignature = moveSignatureOutOfDefaultPackage( parts[3], Constants.NonePackage ); - String deobfSignature = moveSignatureOutOfDefaultPackage( parts[4], Constants.NonePackage ); if( obfName.equals( deobfName ) ) { return new MethodMapping( obfName, obfSignature ); } else { - return new MethodMapping( obfName, obfSignature, deobfName, deobfSignature ); + return new MethodMapping( obfName, obfSignature, deobfName ); } } } diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index f84b2489..49e7b5fd 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -46,14 +46,15 @@ public class MappingsRenamer } else { - boolean wasRemoved = m_mappings.m_classesByDeobf.remove( classMapping.getDeobfName() ) != null; - assert( wasRemoved ); + if( classMapping.getDeobfName() != null ) + { + boolean wasRemoved = m_mappings.m_classesByDeobf.remove( classMapping.getDeobfName() ) != null; + assert( wasRemoved ); + } classMapping.setDeobfName( deobfName ); boolean wasAdded = m_mappings.m_classesByDeobf.put( deobfName, classMapping ) == null; assert( wasAdded ); } - - updateDeobfMethodSignatures(); } public void setFieldName( FieldEntry obf, String deobfName ) @@ -76,7 +77,8 @@ public class MappingsRenamer deobfName = NameValidator.validateMethodName( deobfName ); for( MethodEntry entry : implementations ) { - MethodEntry targetEntry = new MethodEntry( entry.getClassEntry(), deobfName, entry.getSignature() ); + String deobfSignature = getTranslator( TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); + MethodEntry targetEntry = new MethodEntry( entry.getClassEntry(), deobfName, deobfSignature ); if( m_mappings.containsDeobfMethod( entry.getClassEntry(), deobfName, entry.getSignature() ) || m_index.containsObfMethod( targetEntry ) ) { String deobfClassName = getTranslator( TranslationDirection.Deobfuscating ).translateClass( entry.getClassName() ); @@ -101,8 +103,7 @@ public class MappingsRenamer } ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); - String deobfSignature = getTranslator( TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); - classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); + classMapping.setMethodName( obf.getName(), obf.getSignature(), deobfName ); } public void setArgumentName( ArgumentEntry obf, String deobfName ) @@ -137,8 +138,6 @@ public class MappingsRenamer classMapping = new ClassMapping( obfClassName ); boolean obfWasAdded = m_mappings.m_classesByObf.put( classMapping.getObfName(), classMapping ) == null; assert( obfWasAdded ); - boolean deobfWasAdded = m_mappings.m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ) == null; - assert( deobfWasAdded ); } return classMapping; } @@ -153,14 +152,6 @@ public class MappingsRenamer return classMapping; } - private void updateDeobfMethodSignatures( ) - { - for( ClassMapping classMapping : m_mappings.m_classesByObf.values() ) - { - classMapping.updateDeobfMethodSignatures( getTranslator( TranslationDirection.Deobfuscating ) ); - } - } - private Translator getTranslator( TranslationDirection direction ) { return m_mappings.getTranslator( m_index.getTranslationIndex(), direction ); diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java index ea6e6558..3c86dfc0 100644 --- a/src/cuchaz/enigma/mapping/MappingsWriter.java +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -80,10 +80,10 @@ public class MappingsWriter } else { - out.format( "%sMETHOD %s %s %s %s\n", + out.format( "%sMETHOD %s %s %s\n", getIndent( depth ), methodMapping.getObfName(), methodMapping.getDeobfName(), - methodMapping.getObfSignature(), methodMapping.getDeobfSignature() + methodMapping.getObfSignature() ); } diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index e59cb2eb..6210fd09 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -23,20 +23,26 @@ public class MethodMapping implements Serializable, Comparable private String m_obfName; private String m_deobfName; private String m_obfSignature; - private String m_deobfSignature; private Map m_arguments; public MethodMapping( String obfName, String obfSignature ) { - this( obfName, obfSignature, null, null ); + this( obfName, obfSignature, null ); } - public MethodMapping( String obfName, String obfSignature, String deobfName, String deobfSignature ) + public MethodMapping( String obfName, String obfSignature, String deobfName ) { + if( obfName == null ) + { + throw new IllegalArgumentException( "obf name cannot be null!" ); + } + if( obfSignature == null ) + { + throw new IllegalArgumentException( "obf signature cannot be null!" ); + } m_obfName = obfName; m_deobfName = NameValidator.validateMethodName( deobfName ); m_obfSignature = obfSignature; - m_deobfSignature = deobfSignature; m_arguments = new TreeMap(); } @@ -59,15 +65,6 @@ public class MethodMapping implements Serializable, Comparable return m_obfSignature; } - public String getDeobfSignature( ) - { - return m_deobfSignature; - } - public void setDeobfSignature( String val ) - { - m_deobfSignature = val; - } - public Iterable arguments( ) { return m_arguments.values(); @@ -127,8 +124,6 @@ public class MethodMapping implements Serializable, Comparable buf.append( "\n" ); buf.append( "\t" ); buf.append( m_obfSignature ); - buf.append( " <-> " ); - buf.append( m_deobfSignature ); buf.append( "\n" ); buf.append( "\tArguments:\n" ); for( ArgumentMapping argumentMapping : m_arguments.values() ) @@ -145,7 +140,7 @@ public class MethodMapping implements Serializable, Comparable @Override public int compareTo( MethodMapping other ) { - return ( m_obfName + m_obfSignature ).compareTo( ( other.m_obfName + other.m_obfSignature ) ); + return ( m_obfName + m_obfSignature ).compareTo( other.m_obfName + other.m_obfSignature ); } public boolean renameObfClass( final String oldObfClassName, final String newObfClassName ) diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index f5aafdd1..659ce9a2 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -180,7 +180,7 @@ public class Translator // look for the method MethodMapping methodMapping = m_direction.choose( classMapping.getMethodByObf( in.getName(), in.getSignature() ), - classMapping.getMethodByDeobf( in.getName(), in.getSignature() ) + classMapping.getMethodByDeobf( in.getName(), translateSignature( in.getSignature() ) ) ); if( methodMapping != null ) { @@ -248,7 +248,7 @@ public class Translator // look for the method MethodMapping methodMapping = m_direction.choose( classMapping.getMethodByObf( in.getMethodName(), in.getMethodSignature() ), - classMapping.getMethodByDeobf( in.getMethodName(), in.getMethodSignature() ) + classMapping.getMethodByDeobf( in.getMethodName(), translateSignature( in.getMethodSignature() ) ) ); if( methodMapping != null ) { -- cgit v1.2.3 From ce8b00f42d8e3de93ece15dc12db1813d745a2c9 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 4 Sep 2014 00:02:26 -0400 Subject: removed hack to avoid procyon loop --- src/cuchaz/enigma/Deobfuscator.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 8944eec7..0847049e 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -261,12 +261,6 @@ public class Deobfuscator continue; } - // TEMP: skip the classes that won't decompile because of a procyon bug - if( obfClassEntry.getName().equals( "none/bgl" ) ) - { - continue; - } - classEntries.add( obfClassEntry ); } -- cgit v1.2.3 From eeeaf6ec9e177fbffdd639f5b7e7e3d0f020ce9a Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 4 Sep 2014 00:08:07 -0400 Subject: fixed spelling error in error message (lol) --- src/cuchaz/enigma/mapping/ClassEntry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index f87ddc1d..f8477c92 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -34,7 +34,7 @@ public class ClassEntry implements Entry, Serializable if( isInnerClass() && getInnerClassName().indexOf( '/' ) >= 0 ) { - throw new IllegalArgumentException( "Inner cast must not have a package: " + getInnerClassName() ); + throw new IllegalArgumentException( "Inner class must not have a package: " + getInnerClassName() ); } } -- cgit v1.2.3 From e70573589c092b0d2474fa745b1346379cf8767b Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 5 Sep 2014 01:15:43 -0400 Subject: added proguard to the gradle config to create obfuscated jars for testing added simple tests for the deobufscator class --- build.gradle | 65 ++++++++++++++++++-------------- test/cuchaz/enigma/TestDeobfuscator.java | 51 +++++++++++++++++++++++++ test/cuchaz/enigma/inputs/Keep.java | 9 +++++ test/cuchaz/enigma/inputs/LoneClass.java | 16 ++++++++ 4 files changed, 113 insertions(+), 28 deletions(-) create mode 100644 test/cuchaz/enigma/TestDeobfuscator.java create mode 100644 test/cuchaz/enigma/inputs/Keep.java create mode 100644 test/cuchaz/enigma/inputs/LoneClass.java diff --git a/build.gradle b/build.gradle index 7409f936..4fd004fd 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,8 @@ buildscript { } dependencies { - classpath 'eu.appsatori:gradle-fatjar-plugin:0.2-rc1' + classpath "eu.appsatori:gradle-fatjar-plugin:0.2-rc1" + classpath "net.sf.proguard:proguard-gradle:5.0" } } @@ -19,41 +20,29 @@ targetCompatibility = 1.7 group = "com.cuchazinteractive" archivesBaseName = "enigma" -version = "0.5b" +version = "0.5.1b" -sourceSets -{ - main - { - java - { +sourceSets { + main { + java { srcDir "src" } - resources - { + resources { srcDir "conf" } } - test - { - java - { + test { + java { srcDir "test" } - resources - { - srcDir "conf" - } } } -repositories -{ +repositories { mavenCentral() } -dependencies -{ +dependencies { compile fileTree( dir: "libs", include: "*.jar" ) compile "de.sciss:jsyntaxpane:1.0.0" compile "com.google.guava:guava:17.0" @@ -62,13 +51,11 @@ dependencies testCompile "junit:junit:4.11" } -fatJar -{ - from ".", { - include "*.txt" +fatJar { + from( "." ) { + include( "*.txt" ) } - manifest - { + manifest { attributes( "Title": archivesBaseName, "Manifest-Version": "1.0", @@ -77,3 +64,25 @@ fatJar ) } } + +task jarTestCases( type: Jar ) { + from( sourceSets.test.output ) { + include( "cuchaz/enigma/inputs/**" ) + } + archiveName( "testCases.jar" ) +} + +task obfTestCases( type: proguard.gradle.ProGuardTask ) { + dependsOn jarTestCases + + injars( "build/libs/testCases.jar" ) + outjars( "build/libs/testCases.obf.jar" ) + + libraryjars( "${System.getProperty('java.home')}/lib/rt.jar" ) + overloadaggressively + repackageclasses + allowaccessmodification + + keep( "class cuchaz.enigma.inputs.Keep" ) + dontshrink +} \ No newline at end of file diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java new file mode 100644 index 00000000..3310fbcc --- /dev/null +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -0,0 +1,51 @@ +package cuchaz.enigma; + +import static org.junit.Assert.*; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import org.junit.Test; + +import com.beust.jcommander.internal.Lists; + +import cuchaz.enigma.mapping.ClassEntry; + +public class TestDeobfuscator +{ + private Deobfuscator getDeobfuscator( ) + throws IOException + { + return new Deobfuscator( new File( "build/libs/testCases.obf.jar" ) ); + } + + @Test + public void loadJar( ) + throws Exception + { + getDeobfuscator(); + } + + @Test + public void getClasses( ) + throws Exception + { + Deobfuscator deobfuscator = getDeobfuscator(); + List obfClasses = Lists.newArrayList(); + List deobfClasses = Lists.newArrayList(); + deobfuscator.getSeparatedClasses( obfClasses, deobfClasses ); + assertEquals( 1, obfClasses.size() ); + assertEquals( "none/a", obfClasses.get( 0 ).getName() ); + assertEquals( 1, deobfClasses.size() ); + assertEquals( "cuchaz/enigma/inputs/Keep", deobfClasses.get( 0 ).getName() ); + } + + @Test + public void decompileClass( ) + throws Exception + { + Deobfuscator deobfuscator = getDeobfuscator(); + deobfuscator.getSource( deobfuscator.getSourceTree( "none/a" ) ); + } +} diff --git a/test/cuchaz/enigma/inputs/Keep.java b/test/cuchaz/enigma/inputs/Keep.java new file mode 100644 index 00000000..3c12baea --- /dev/null +++ b/test/cuchaz/enigma/inputs/Keep.java @@ -0,0 +1,9 @@ +package cuchaz.enigma.inputs; + +public class Keep +{ + public static void main( String[] args ) + { + System.out.println( "Keep me!" ); + } +} diff --git a/test/cuchaz/enigma/inputs/LoneClass.java b/test/cuchaz/enigma/inputs/LoneClass.java new file mode 100644 index 00000000..a3d8cded --- /dev/null +++ b/test/cuchaz/enigma/inputs/LoneClass.java @@ -0,0 +1,16 @@ +package cuchaz.enigma.inputs; + +public class LoneClass +{ + private String m_name; + + public LoneClass( String name ) + { + m_name = name; + } + + public String getName( ) + { + return m_name; + } +} -- cgit v1.2.3 From 730238f3bab1c680424e0ac74178c33b15b43eb5 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 7 Sep 2014 22:30:28 -0400 Subject: added some basic tests for the deobufscator and the jar index --- build.gradle | 15 +- src/cuchaz/enigma/analysis/JarIndex.java | 11 +- src/cuchaz/enigma/convert/ClassIdentity.java | 14 +- test/cuchaz/enigma/EntryFactory.java | 52 ++++++ test/cuchaz/enigma/TestDeobfuscator.java | 13 +- test/cuchaz/enigma/TestJarIndexLoneClass.java | 201 +++++++++++++++++++++ test/cuchaz/enigma/inputs/LoneClass.java | 16 -- test/cuchaz/enigma/inputs/loneClass/LoneClass.java | 16 ++ 8 files changed, 307 insertions(+), 31 deletions(-) create mode 100644 test/cuchaz/enigma/EntryFactory.java create mode 100644 test/cuchaz/enigma/TestJarIndexLoneClass.java delete mode 100644 test/cuchaz/enigma/inputs/LoneClass.java create mode 100644 test/cuchaz/enigma/inputs/loneClass/LoneClass.java diff --git a/build.gradle b/build.gradle index 4fd004fd..14f66145 100644 --- a/build.gradle +++ b/build.gradle @@ -49,6 +49,7 @@ dependencies { compile "org.javassist:javassist:3.18.1-GA" testCompile "junit:junit:4.11" + testCompile "org.hamcrest:hamcrest-all:1.3" } fatJar { @@ -65,23 +66,25 @@ fatJar { } } -task jarTestCases( type: Jar ) { +task jarLoneClass( type: Jar ) { from( sourceSets.test.output ) { - include( "cuchaz/enigma/inputs/**" ) + include( "cuchaz/enigma/inputs/Keep.class" ) + include( "cuchaz/enigma/inputs/loneClass/**" ) } - archiveName( "testCases.jar" ) + archiveName( "testLoneClass.jar" ) } task obfTestCases( type: proguard.gradle.ProGuardTask ) { - dependsOn jarTestCases + dependsOn jarLoneClass - injars( "build/libs/testCases.jar" ) - outjars( "build/libs/testCases.obf.jar" ) + injars( "build/libs/testLoneClass.jar" ) + outjars( "build/libs/testLoneClass.obf.jar" ) libraryjars( "${System.getProperty('java.home')}/lib/rt.jar" ) overloadaggressively repackageclasses allowaccessmodification + dontoptimize keep( "class cuchaz.enigma.inputs.Keep" ) dontshrink diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index a8ac0013..b4096e9d 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -587,9 +587,14 @@ public class JarIndex public ClassImplementationsTreeNode getClassImplementations( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) { - ClassImplementationsTreeNode node = new ClassImplementationsTreeNode( deobfuscatingTranslator, obfClassEntry ); - node.load( this ); - return node; + // is this even an interface? + if( isInterface( obfClassEntry.getClassName() ) ) + { + ClassImplementationsTreeNode node = new ClassImplementationsTreeNode( deobfuscatingTranslator, obfClassEntry ); + node.load( this ); + return node; + } + return null; } public MethodInheritanceTreeNode getMethodInheritance( Translator deobfuscatingTranslator, MethodEntry obfMethodEntry ) diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index bd2824b3..b3b043e5 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -111,12 +111,16 @@ public class ClassIdentity // stuff from the jar index m_implementations = HashMultiset.create(); - @SuppressWarnings( "unchecked" ) - Enumeration implementations = index.getClassImplementations( null, m_classEntry ).children(); - while( implementations.hasMoreElements() ) + ClassImplementationsTreeNode implementationsNode = index.getClassImplementations( null, m_classEntry ); + if( implementationsNode != null ) { - ClassImplementationsTreeNode node = implementations.nextElement(); - m_implementations.add( scrubClassName( node.getClassEntry().getName() ) ); + @SuppressWarnings( "unchecked" ) + Enumeration implementations = implementationsNode.children(); + while( implementations.hasMoreElements() ) + { + ClassImplementationsTreeNode node = implementations.nextElement(); + m_implementations.add( scrubClassName( node.getClassEntry().getName() ) ); + } } m_references = HashMultiset.create(); diff --git a/test/cuchaz/enigma/EntryFactory.java b/test/cuchaz/enigma/EntryFactory.java new file mode 100644 index 00000000..b275719b --- /dev/null +++ b/test/cuchaz/enigma/EntryFactory.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class EntryFactory +{ + public static ClassEntry newClass( String name ) + { + return new ClassEntry( name ); + } + + public static FieldEntry newField( String className, String fieldName ) + { + return new FieldEntry( newClass( className ), fieldName ); + } + + public static MethodEntry newMethod( String className, String methodName, String methodSignature ) + { + return new MethodEntry( newClass( className ), methodName, methodSignature ); + } + + public static ConstructorEntry newConstructor( String className, String signature ) + { + return new ConstructorEntry( newClass( className ), signature ); + } + + public static EntryReference newFieldReferenceByMethod( String fieldClassName, String fieldName, String callerClassName, String callerName, String callerSignature ) + { + return new EntryReference( newField( fieldClassName, fieldName ), newMethod( callerClassName, callerName, callerSignature ) ); + } + + public static EntryReference newFieldReferenceByConstructor( String fieldClassName, String fieldName, String callerClassName, String callerSignature ) + { + return new EntryReference( newField( fieldClassName, fieldName ), newConstructor( callerClassName, callerSignature ) ); + } +} diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java index 3310fbcc..3e679fb9 100644 --- a/test/cuchaz/enigma/TestDeobfuscator.java +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -1,3 +1,14 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma; import static org.junit.Assert.*; @@ -17,7 +28,7 @@ public class TestDeobfuscator private Deobfuscator getDeobfuscator( ) throws IOException { - return new Deobfuscator( new File( "build/libs/testCases.obf.jar" ) ); + return new Deobfuscator( new File( "build/libs/testLoneClass.obf.jar" ) ); } @Test diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java new file mode 100644 index 00000000..56031ddc --- /dev/null +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.newClass; +import static cuchaz.enigma.EntryFactory.newField; +import static cuchaz.enigma.EntryFactory.newFieldReferenceByConstructor; +import static cuchaz.enigma.EntryFactory.newFieldReferenceByMethod; +import static cuchaz.enigma.EntryFactory.newMethod; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import java.util.Collection; +import java.util.Set; +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.Access; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class TestJarIndexLoneClass +{ + private JarIndex m_index; + + public TestJarIndexLoneClass( ) + throws Exception + { + m_index = new JarIndex(); + m_index.indexJar( new JarFile( "build/libs/testLoneClass.obf.jar" ), false ); + } + + @Test + public void obfEntries( ) + { + assertThat( m_index.getObfClassEntries(), containsInAnyOrder( + newClass( "cuchaz/enigma/inputs/Keep" ), + newClass( "none/a" ) + ) ); + } + + @Test + public void translationIndex( ) + { + assertThat( m_index.getTranslationIndex().getSuperclassName( "none/a" ), is( nullValue() ) ); + assertThat( m_index.getTranslationIndex().getSuperclassName( "cuchaz/enigma/inputs/Keep" ), is( nullValue() ) ); + assertThat( m_index.getTranslationIndex().getAncestry( "none/a" ), is( empty() ) ); + assertThat( m_index.getTranslationIndex().getAncestry( "cuchaz/enigma/inputs/Keep" ), is( empty() ) ); + assertThat( m_index.getTranslationIndex().getSubclassNames( "none/a" ), is( empty() ) ); + assertThat( m_index.getTranslationIndex().getSubclassNames( "cuchaz/enigma/inputs/Keep" ), is( empty() ) ); + } + + @Test + public void access( ) + { + assertThat( m_index.getAccess( newField( "none/a", "a" ) ), is( Access.Private ) ); + assertThat( m_index.getAccess( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( Access.Public ) ); + assertThat( m_index.getAccess( newField( "none/a", "b" ) ), is( nullValue() ) ); + } + + @Test + public void isImplemented( ) + { + assertThat( m_index.isMethodImplemented( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( true ) ); + assertThat( m_index.isMethodImplemented( newMethod( "none/a", "b", "()Ljava/lang/String;" ) ), is( false ) ); + } + + @Test + public void classInheritance( ) + { + ClassInheritanceTreeNode node = m_index.getClassInheritance( new Translator(), newClass( "none/a" ) ); + assertThat( node, is( not( nullValue() ) ) ); + assertThat( node.getObfClassName(), is( "none/a" ) ); + assertThat( node.getChildCount(), is( 0 ) ); + } + + + @Test + public void methodInheritance( ) + { + MethodEntry source = newMethod( "none/a", "a", "()Ljava/lang/String;" ); + MethodInheritanceTreeNode node = m_index.getMethodInheritance( new Translator(), source ); + assertThat( node, is( not( nullValue() ) ) ); + assertThat( node.getMethodEntry(), is( source ) ); + assertThat( node.getChildCount(), is( 0 ) ); + } + + @Test + public void classImplementations( ) + { + ClassImplementationsTreeNode node = m_index.getClassImplementations( new Translator(), newClass( "none/a" ) ); + assertThat( node, is( nullValue() ) ); + } + + @Test + public void methodImplementations( ) + { + MethodEntry source = newMethod( "none/a", "a", "()Ljava/lang/String;" ); + MethodImplementationsTreeNode node = m_index.getMethodImplementations( new Translator(), source ); + assertThat( node, is( nullValue() ) ); + } + + @Test + public void relatedMethodImplementations( ) + { + Set entries = m_index.getRelatedMethodImplementations( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ); + assertThat( entries, containsInAnyOrder( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ) ); + } + + @Test + @SuppressWarnings( "unchecked" ) + public void fieldReferences( ) + { + Collection> references = m_index.getFieldReferences( newField( "none/a", "a" ) ); + assertThat( references, containsInAnyOrder( + newFieldReferenceByConstructor( "none/a", "a", "none/a", "(Ljava/lang/String;)V" ), + newFieldReferenceByMethod( "none/a", "a", "none/a", "a", "()Ljava/lang/String;" ) + ) ); + } + + @Test + public void behaviorReferences( ) + { + assertThat( m_index.getBehaviorReferences( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( empty() ) ); + } + + @Test + public void innerClasses( ) + { + assertThat( m_index.getInnerClasses( "none/a" ), is( empty() ) ); + } + + @Test + public void outerClass( ) + { + assertThat( m_index.getOuterClass( "none/a" ), is( nullValue() ) ); + } + + @Test + public void isAnonymousClass( ) + { + assertThat( m_index.isAnonymousClass( "none/a" ), is( false ) ); + } + + @Test + public void interfaces( ) + { + assertThat( m_index.getInterfaces( "none/a" ), is( empty() ) ); + } + + @Test + public void implementingClasses( ) + { + assertThat( m_index.getImplementingClasses( "none/a" ), is( empty() ) ); + } + + @Test + public void isInterface( ) + { + assertThat( m_index.isInterface( "none/a" ), is( false ) ); + } + + @Test + public void bridgeMethods( ) + { + assertThat( m_index.getBridgeMethod( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( nullValue() ) ); + } + + @Test + public void contains( ) + { + assertThat( m_index.containsObfClass( newClass( "none/a" ) ), is( true ) ); + assertThat( m_index.containsObfClass( newClass( "none/b" ) ), is( false ) ); + assertThat( m_index.containsObfField( newField( "none/a", "a" ) ), is( true ) ); + assertThat( m_index.containsObfField( newField( "none/a", "b" ) ), is( false ) ); + assertThat( m_index.containsObfMethod( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( true ) ); + assertThat( m_index.containsObfMethod( newMethod( "none/a", "b", "()Ljava/lang/String;" ) ), is( false ) ); + } +} diff --git a/test/cuchaz/enigma/inputs/LoneClass.java b/test/cuchaz/enigma/inputs/LoneClass.java deleted file mode 100644 index a3d8cded..00000000 --- a/test/cuchaz/enigma/inputs/LoneClass.java +++ /dev/null @@ -1,16 +0,0 @@ -package cuchaz.enigma.inputs; - -public class LoneClass -{ - private String m_name; - - public LoneClass( String name ) - { - m_name = name; - } - - public String getName( ) - { - return m_name; - } -} diff --git a/test/cuchaz/enigma/inputs/loneClass/LoneClass.java b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java new file mode 100644 index 00000000..961b012e --- /dev/null +++ b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java @@ -0,0 +1,16 @@ +package cuchaz.enigma.inputs.loneClass; + +public class LoneClass +{ + private String m_name; + + public LoneClass( String name ) + { + m_name = name; + } + + public String getName( ) + { + return m_name; + } +} -- cgit v1.2.3 From a68dc42b6a835bd513e9d617c9892e85f321ddb6 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 8 Sep 2014 00:23:46 -0400 Subject: added some tests for a small inheritance hierarchy --- build.gradle | 21 +- test/cuchaz/enigma/EntryFactory.java | 18 +- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 255 +++++++++++++++++++++ test/cuchaz/enigma/TestJarIndexLoneClass.java | 8 +- .../enigma/inputs/inheritanceTree/BaseClass.java | 18 ++ .../enigma/inputs/inheritanceTree/SubclassA.java | 9 + .../enigma/inputs/inheritanceTree/SubclassB.java | 24 ++ .../inputs/inheritanceTree/SubsubclassAA.java | 21 ++ 8 files changed, 361 insertions(+), 13 deletions(-) create mode 100644 test/cuchaz/enigma/TestJarIndexInheritanceTree.java create mode 100644 test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java create mode 100644 test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java create mode 100644 test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java create mode 100644 test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java diff --git a/build.gradle b/build.gradle index 14f66145..10294292 100644 --- a/build.gradle +++ b/build.gradle @@ -74,18 +74,29 @@ task jarLoneClass( type: Jar ) { archiveName( "testLoneClass.jar" ) } +task jarInheritanceTree( type: Jar ) { + from( sourceSets.test.output ) { + include( "cuchaz/enigma/inputs/Keep.class" ) + include( "cuchaz/enigma/inputs/inheritanceTree/**" ) + } + archiveName( "testInheritanceTree.jar" ) +} + task obfTestCases( type: proguard.gradle.ProGuardTask ) { - dependsOn jarLoneClass - - injars( "build/libs/testLoneClass.jar" ) - outjars( "build/libs/testLoneClass.obf.jar" ) + dependsOn jarLoneClass, jarInheritanceTree libraryjars( "${System.getProperty('java.home')}/lib/rt.jar" ) overloadaggressively repackageclasses allowaccessmodification dontoptimize + dontshrink keep( "class cuchaz.enigma.inputs.Keep" ) - dontshrink + + def jarNames = [ "LoneClass", "InheritanceTree" ]; + jarNames.each() { + injars( "build/libs/test${it}.jar" ) + outjars( "build/libs/test${it}.obf.jar" ) + } } \ No newline at end of file diff --git a/test/cuchaz/enigma/EntryFactory.java b/test/cuchaz/enigma/EntryFactory.java index b275719b..66f83dfd 100644 --- a/test/cuchaz/enigma/EntryFactory.java +++ b/test/cuchaz/enigma/EntryFactory.java @@ -40,13 +40,23 @@ public class EntryFactory return new ConstructorEntry( newClass( className ), signature ); } - public static EntryReference newFieldReferenceByMethod( String fieldClassName, String fieldName, String callerClassName, String callerName, String callerSignature ) + public static EntryReference newFieldReferenceByMethod( FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature ) { - return new EntryReference( newField( fieldClassName, fieldName ), newMethod( callerClassName, callerName, callerSignature ) ); + return new EntryReference( fieldEntry, newMethod( callerClassName, callerName, callerSignature ) ); } - public static EntryReference newFieldReferenceByConstructor( String fieldClassName, String fieldName, String callerClassName, String callerSignature ) + public static EntryReference newFieldReferenceByConstructor( FieldEntry fieldEntry, String callerClassName, String callerSignature ) { - return new EntryReference( newField( fieldClassName, fieldName ), newConstructor( callerClassName, callerSignature ) ); + return new EntryReference( fieldEntry, newConstructor( callerClassName, callerSignature ) ); + } + + public static EntryReference newBehaviorReferenceByMethod( BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature ) + { + return new EntryReference( behaviorEntry, newMethod( callerClassName, callerName, callerSignature ) ); + } + + public static EntryReference newBehaviorReferenceByConstructor( BehaviorEntry behaviorEntry, String callerClassName, String callerSignature ) + { + return new EntryReference( behaviorEntry, newConstructor( callerClassName, callerSignature ) ); } } diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java new file mode 100644 index 00000000..5ded5df0 --- /dev/null +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -0,0 +1,255 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByConstructor; +import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByMethod; +import static cuchaz.enigma.EntryFactory.newClass; +import static cuchaz.enigma.EntryFactory.newFieldReferenceByConstructor; +import static cuchaz.enigma.EntryFactory.newFieldReferenceByMethod; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +import java.util.Collection; +import java.util.Set; +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.Access; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.analysis.TranslationIndex; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class TestJarIndexInheritanceTree +{ + private JarIndex m_index; + + private ClassEntry m_baseClass = new ClassEntry( "none/a" ); + private ClassEntry m_subClassA = new ClassEntry( "none/b" ); + private ClassEntry m_subClassAA = new ClassEntry( "none/d" ); + private ClassEntry m_subClassB = new ClassEntry( "none/c" ); + private FieldEntry m_nameField = new FieldEntry( m_baseClass, "a" ); + private FieldEntry m_numThingsField = new FieldEntry( m_subClassB, "a" ); + + public TestJarIndexInheritanceTree( ) + throws Exception + { + m_index = new JarIndex(); + m_index.indexJar( new JarFile( "build/libs/testInheritanceTree.obf.jar" ), false ); + } + + @Test + public void obfEntries( ) + { + assertThat( m_index.getObfClassEntries(), containsInAnyOrder( + newClass( "cuchaz/enigma/inputs/Keep" ), + m_baseClass, + m_subClassA, + m_subClassAA, + m_subClassB + ) ); + } + + @Test + public void translationIndex( ) + { + TranslationIndex index = m_index.getTranslationIndex(); + + // base class + assertThat( index.getSuperclassName( m_baseClass.getName() ), is( nullValue() ) ); + assertThat( index.getAncestry( m_baseClass.getName() ), is( empty() ) ); + assertThat( index.getSubclassNames( m_baseClass.getName() ), containsInAnyOrder( + m_subClassA.getName(), + m_subClassB.getName() + ) ); + + // subclass a + assertThat( index.getSuperclassName( m_subClassA.getName() ), is( m_baseClass.getName() ) ); + assertThat( index.getAncestry( m_subClassA.getName() ), contains( m_baseClass.getName() ) ); + assertThat( index.getSubclassNames( m_subClassA.getName() ), contains( m_subClassAA.getName() ) ); + + // subclass aa + assertThat( index.getSuperclassName( m_subClassAA.getName() ), is( m_subClassA.getName() ) ); + assertThat( index.getAncestry( m_subClassAA.getName() ), contains( + m_subClassA.getName(), + m_baseClass.getName() + ) ); + assertThat( index.getSubclassNames( m_subClassAA.getName() ), is( empty() ) ); + + // subclass b + assertThat( index.getSuperclassName( m_subClassB.getName() ), is( m_baseClass.getName() ) ); + assertThat( index.getAncestry( m_subClassB.getName() ), contains( m_baseClass.getName() ) ); + assertThat( index.getSubclassNames( m_subClassB.getName() ), is( empty() ) ); + } + + @Test + public void access( ) + { + assertThat( m_index.getAccess( m_nameField ), is( Access.Private ) ); + assertThat( m_index.getAccess( m_numThingsField ), is( Access.Private ) ); + } + + @Test + public void isImplemented( ) + { + // getName() + assertThat( m_index.isMethodImplemented( new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ) ), is( true ) ); + assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassA, "a", "()Ljava/lang/String;" ) ), is( false ) ); + assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ) ), is( true ) ); + assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassB, "a", "()Ljava/lang/String;" ) ), is( false ) ); + + // doBaseThings() + assertThat( m_index.isMethodImplemented( new MethodEntry( m_baseClass, "a", "()V" ) ), is( true ) ); + assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassA, "a", "()V" ) ), is( false ) ); + assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassAA, "a", "()V" ) ), is( true ) ); + assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassB, "a", "()V" ) ), is( true ) ); + + // doBThings() + assertThat( m_index.isMethodImplemented( new MethodEntry( m_baseClass, "b", "()V" ) ), is( false ) ); + assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassA, "b", "()V" ) ), is( false ) ); + assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassAA, "b", "()V" ) ), is( false ) ); + assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassB, "b", "()V" ) ), is( true ) ); + } + + @Test + public void relatedMethodImplementations( ) + { + Set entries; + + // getName() + entries = m_index.getRelatedMethodImplementations( new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ) ); + assertThat( entries, containsInAnyOrder( + new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ), + new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ) + ) ); + entries = m_index.getRelatedMethodImplementations( new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ) ); + assertThat( entries, containsInAnyOrder( + new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ), + new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ) + ) ); + + // doBaseThings() + entries = m_index.getRelatedMethodImplementations( new MethodEntry( m_baseClass, "a", "()V" ) ); + assertThat( entries, containsInAnyOrder( + new MethodEntry( m_baseClass, "a", "()V" ), + new MethodEntry( m_subClassAA, "a", "()V" ), + new MethodEntry( m_subClassB, "a", "()V" ) + ) ); + entries = m_index.getRelatedMethodImplementations( new MethodEntry( m_subClassAA, "a", "()V" ) ); + assertThat( entries, containsInAnyOrder( + new MethodEntry( m_baseClass, "a", "()V" ), + new MethodEntry( m_subClassAA, "a", "()V" ), + new MethodEntry( m_subClassB, "a", "()V" ) + ) ); + entries = m_index.getRelatedMethodImplementations( new MethodEntry( m_subClassB, "a", "()V" ) ); + assertThat( entries, containsInAnyOrder( + new MethodEntry( m_baseClass, "a", "()V" ), + new MethodEntry( m_subClassAA, "a", "()V" ), + new MethodEntry( m_subClassB, "a", "()V" ) + ) ); + + // doBThings + entries = m_index.getRelatedMethodImplementations( new MethodEntry( m_subClassB, "b", "()V" ) ); + assertThat( entries, containsInAnyOrder( + new MethodEntry( m_subClassB, "b", "()V" ) + ) ); + } + + @Test + @SuppressWarnings( "unchecked" ) + public void fieldReferences( ) + { + Collection> references; + + // name + references = m_index.getFieldReferences( m_nameField ); + assertThat( references, containsInAnyOrder( + newFieldReferenceByConstructor( m_nameField, m_baseClass.getName(), "(Ljava/lang/String;)V" ), + newFieldReferenceByMethod( m_nameField, m_baseClass.getName(), "a", "()Ljava/lang/String;" ) + ) ); + + // numThings + references = m_index.getFieldReferences( m_numThingsField ); + assertThat( references, containsInAnyOrder( + newFieldReferenceByConstructor( m_numThingsField, m_subClassB.getName(), "()V" ), + newFieldReferenceByMethod( m_numThingsField, m_subClassB.getName(), "b", "()V" ) + ) ); + } + + @Test + @SuppressWarnings( "unchecked" ) + public void behaviorReferences( ) + { + BehaviorEntry source; + Collection> references; + + // baseClass constructor + source = new ConstructorEntry( m_baseClass, "(Ljava/lang/String;)V" ); + references = m_index.getBehaviorReferences( source ); + assertThat( references, containsInAnyOrder( + newBehaviorReferenceByConstructor( source, m_subClassA.getName(), "(Ljava/lang/String;)V" ), + newBehaviorReferenceByConstructor( source, m_subClassB.getName(), "()V" ) + ) ); + + // subClassA constructor + source = new ConstructorEntry( m_subClassA, "(Ljava/lang/String;)V" ); + references = m_index.getBehaviorReferences( source ); + assertThat( references, containsInAnyOrder( + newBehaviorReferenceByConstructor( source, m_subClassAA.getName(), "()V" ) + ) ); + + // baseClass.getName() + source = new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ); + references = m_index.getBehaviorReferences( source ); + assertThat( references, containsInAnyOrder( + newBehaviorReferenceByMethod( source, m_subClassAA.getName(), "a", "()Ljava/lang/String;" ) + ) ); + + // subclassAA.getName() + source = new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ); + references = m_index.getBehaviorReferences( source ); + assertThat( references, containsInAnyOrder( + newBehaviorReferenceByMethod( source, m_subClassAA.getName(), "a", "()V" ) + ) ); + } + + @Test + public void containsEntries( ) + { + // classes + assertThat( m_index.containsObfClass( m_baseClass ), is( true ) ); + assertThat( m_index.containsObfClass( m_subClassA ), is( true ) ); + assertThat( m_index.containsObfClass( m_subClassAA ), is( true ) ); + assertThat( m_index.containsObfClass( m_subClassB ), is( true ) ); + + // fields + assertThat( m_index.containsObfField( m_nameField ), is( true ) ); + assertThat( m_index.containsObfField( m_numThingsField ), is( true ) ); + + // methods + assertThat( m_index.containsObfMethod( new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ) ), is( true ) ); + assertThat( m_index.containsObfMethod( new MethodEntry( m_baseClass, "a", "()V" ) ), is( true ) ); + assertThat( m_index.containsObfMethod( new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ) ), is( true ) ); + assertThat( m_index.containsObfMethod( new MethodEntry( m_subClassAA, "a", "()V" ) ), is( true ) ); + assertThat( m_index.containsObfMethod( new MethodEntry( m_subClassB, "a", "()V" ) ), is( true ) ); + assertThat( m_index.containsObfMethod( new MethodEntry( m_subClassB, "b", "()V" ) ), is( true ) ); + } +} diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index 56031ddc..9236d0c1 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -95,7 +95,6 @@ public class TestJarIndexLoneClass assertThat( node.getObfClassName(), is( "none/a" ) ); assertThat( node.getChildCount(), is( 0 ) ); } - @Test public void methodInheritance( ) @@ -133,10 +132,11 @@ public class TestJarIndexLoneClass @SuppressWarnings( "unchecked" ) public void fieldReferences( ) { - Collection> references = m_index.getFieldReferences( newField( "none/a", "a" ) ); + FieldEntry source = newField( "none/a", "a" ); + Collection> references = m_index.getFieldReferences( source ); assertThat( references, containsInAnyOrder( - newFieldReferenceByConstructor( "none/a", "a", "none/a", "(Ljava/lang/String;)V" ), - newFieldReferenceByMethod( "none/a", "a", "none/a", "a", "()Ljava/lang/String;" ) + newFieldReferenceByConstructor( source, "none/a", "(Ljava/lang/String;)V" ), + newFieldReferenceByMethod( source, "none/a", "a", "()Ljava/lang/String;" ) ) ); } diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java new file mode 100644 index 00000000..a6b38454 --- /dev/null +++ b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java @@ -0,0 +1,18 @@ +package cuchaz.enigma.inputs.inheritanceTree; + +public abstract class BaseClass +{ + private String m_name; + + protected BaseClass( String name ) + { + m_name = name; + } + + public String getName( ) + { + return m_name; + } + + public abstract void doBaseThings( ); +} diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java new file mode 100644 index 00000000..f4780a26 --- /dev/null +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java @@ -0,0 +1,9 @@ +package cuchaz.enigma.inputs.inheritanceTree; + +public abstract class SubclassA extends BaseClass +{ + protected SubclassA( String name ) + { + super( name ); + } +} diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java new file mode 100644 index 00000000..4001e7a3 --- /dev/null +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java @@ -0,0 +1,24 @@ +package cuchaz.enigma.inputs.inheritanceTree; + +public class SubclassB extends BaseClass +{ + private int m_numThings; + + protected SubclassB( ) + { + super( "B" ); + + m_numThings = 4; + } + + @Override + public void doBaseThings( ) + { + System.out.println( "Base things by B!" ); + } + + public void doBThings( ) + { + System.out.println( "" + m_numThings + " B things!" ); + } +} diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java new file mode 100644 index 00000000..11196d16 --- /dev/null +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java @@ -0,0 +1,21 @@ +package cuchaz.enigma.inputs.inheritanceTree; + +public class SubsubclassAA extends SubclassA +{ + protected SubsubclassAA( ) + { + super( "AA" ); + } + + @Override + public String getName( ) + { + return "subsub" + super.getName(); + } + + @Override + public void doBaseThings( ) + { + System.out.println( "Base things by " + getName() ); + } +} -- cgit v1.2.3 From aa68099bb252dd1a1c275459f8babe537868bcaf Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 14 Sep 2014 19:06:55 -0400 Subject: fixed bug with method references pointing to wrong class --- src/cuchaz/enigma/analysis/JarIndex.java | 120 ++++++++++++++------- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 3 +- .../enigma/inputs/inheritanceTree/BaseClass.java | 5 + .../enigma/inputs/inheritanceTree/SubclassA.java | 3 + .../enigma/inputs/inheritanceTree/SubclassB.java | 11 +- .../inputs/inheritanceTree/SubsubclassAA.java | 6 ++ 6 files changed, 107 insertions(+), 41 deletions(-) diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index b4096e9d..f4843164 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -127,7 +127,7 @@ public class JarIndex } for( CtField field : c.getDeclaredFields() ) { - indexField( field ); + m_translationIndex.addField( className, field.getName() ); } for( CtBehavior behavior : c.getDeclaredBehaviors() ) { @@ -135,9 +135,19 @@ public class JarIndex } } + // step 4: index field, method, constructor references + for( CtClass c : JarClassIterator.classes( jar ) ) + { + ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); + for( CtBehavior behavior : c.getDeclaredBehaviors() ) + { + indexBehaviorReferences( behavior ); + } + } + if( buildInnerClasses ) { - // step 4: index inner classes and anonymous classes + // step 5: index inner classes and anonymous classes for( CtClass c : JarClassIterator.classes( jar ) ) { ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); @@ -163,7 +173,7 @@ public class JarIndex } } - // step 5: update other indices with inner class info + // step 6: update other indices with inner class info Map renames = Maps.newHashMap(); for( Map.Entry entry : m_outerClasses.entrySet() ) { @@ -183,25 +193,14 @@ public class JarIndex EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_fieldReferences ); } - private void indexField( CtField field ) - { - String className = Descriptor.toJvmName( field.getDeclaringClass().getName() ); - m_translationIndex.addField( className, field.getName() ); - } - private void indexBehavior( CtBehavior behavior ) { - // get the method entry + // get the behavior entry String className = Descriptor.toJvmName( behavior.getDeclaringClass().getName() ); - final BehaviorEntry thisEntry; - if( behavior instanceof CtMethod ) + final BehaviorEntry behaviorEntry = getBehaviorEntry( behavior ); + if( behaviorEntry instanceof MethodEntry ) { - MethodEntry methodEntry = new MethodEntry( - new ClassEntry( className ), - behavior.getName(), - behavior.getSignature() - ); - thisEntry = methodEntry; + MethodEntry methodEntry = (MethodEntry)behaviorEntry; // index implementation m_methodImplementations.put( className, methodEntry ); @@ -218,24 +217,13 @@ public class JarIndex m_bridgeMethods.put( bridgedMethodEntry, methodEntry ); } } - else if( behavior instanceof CtConstructor ) - { - boolean isStatic = behavior.getName().equals( "" ); - if( isStatic ) - { - thisEntry = new ConstructorEntry( new ClassEntry( className ) ); - } - else - { - thisEntry = new ConstructorEntry( new ClassEntry( className ), behavior.getSignature() ); - } - } - else - { - throw new IllegalArgumentException( "behavior must be a method or a constructor!" ); - } - + // looks like we don't care about constructors here + } + + private void indexBehaviorReferences( CtBehavior behavior ) + { // index method calls + final BehaviorEntry behaviorEntry = getBehaviorEntry( behavior ); try { behavior.instrument( new ExprEditor( ) @@ -249,9 +237,10 @@ public class JarIndex call.getMethodName(), call.getSignature() ); + calledMethodEntry = resolveMethodClass( calledMethodEntry ); EntryReference reference = new EntryReference( calledMethodEntry, - thisEntry + behaviorEntry ); m_behaviorReferences.put( calledMethodEntry, reference ); } @@ -266,7 +255,7 @@ public class JarIndex ); EntryReference reference = new EntryReference( calledFieldEntry, - thisEntry + behaviorEntry ); m_fieldReferences.put( calledFieldEntry, reference ); } @@ -284,7 +273,7 @@ public class JarIndex ); EntryReference reference = new EntryReference( calledConstructorEntry, - thisEntry + behaviorEntry ); m_behaviorReferences.put( calledConstructorEntry, reference ); } @@ -299,7 +288,7 @@ public class JarIndex ); EntryReference reference = new EntryReference( calledConstructorEntry, - thisEntry + behaviorEntry ); m_behaviorReferences.put( calledConstructorEntry, reference ); } @@ -311,6 +300,59 @@ public class JarIndex } } + private BehaviorEntry getBehaviorEntry( CtBehavior behavior ) + { + String className = Descriptor.toJvmName( behavior.getDeclaringClass().getName() ); + if( behavior instanceof CtMethod ) + { + return new MethodEntry( + new ClassEntry( className ), + behavior.getName(), + behavior.getSignature() + ); + } + else if( behavior instanceof CtConstructor ) + { + boolean isStatic = behavior.getName().equals( "" ); + if( isStatic ) + { + return new ConstructorEntry( new ClassEntry( className ) ); + } + else + { + return new ConstructorEntry( new ClassEntry( className ), behavior.getSignature() ); + } + } + else + { + throw new IllegalArgumentException( "behavior must be a method or a constructor!" ); + } + } + + private MethodEntry resolveMethodClass( MethodEntry methodEntry ) + { + // this entry could refer to a method on a class where the method is not actually implemented + // travel up the inheritance tree to find the closest implementation + while( !isMethodImplemented( methodEntry ) ) + { + // is there a parent class? + String superclassName = m_translationIndex.getSuperclassName( methodEntry.getClassName() ); + if( superclassName == null ) + { + // this is probably a method from a class in a library + // we can't trace the implementation up any higher unless we index the library + return methodEntry; + } + + // move up to the parent class + methodEntry = new MethodEntry( + new ClassEntry( superclassName ), + methodEntry.getName(), + methodEntry.getSignature() + ); + } + return methodEntry; + } private CtMethod getBridgedMethod( CtMethod method ) { diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 5ded5df0..3d488ee4 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -220,7 +220,8 @@ public class TestJarIndexInheritanceTree source = new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ); references = m_index.getBehaviorReferences( source ); assertThat( references, containsInAnyOrder( - newBehaviorReferenceByMethod( source, m_subClassAA.getName(), "a", "()Ljava/lang/String;" ) + newBehaviorReferenceByMethod( source, m_subClassAA.getName(), "a", "()Ljava/lang/String;" ), + newBehaviorReferenceByMethod( source, m_subClassB.getName(), "a", "()V" ) ) ); // subclassAA.getName() diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java index a6b38454..8402dde3 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java @@ -1,18 +1,23 @@ package cuchaz.enigma.inputs.inheritanceTree; +// none/a public abstract class BaseClass { + // a private String m_name; + // (Ljava/lang/String;)V protected BaseClass( String name ) { m_name = name; } + // a()Ljava/lang/String; public String getName( ) { return m_name; } + // a()V public abstract void doBaseThings( ); } diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java index f4780a26..ed507093 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java @@ -1,9 +1,12 @@ package cuchaz.enigma.inputs.inheritanceTree; +// none/b extends none/a public abstract class SubclassA extends BaseClass { + // (Ljava/lang/String;)V protected SubclassA( String name ) { + // call to none/a.(Ljava/lang/String)V super( name ); } } diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java index 4001e7a3..fc4c8eed 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java @@ -1,24 +1,33 @@ package cuchaz.enigma.inputs.inheritanceTree; +// none/c extends none/a public class SubclassB extends BaseClass { + // a private int m_numThings; + // ()V protected SubclassB( ) { + // none/a.(Ljava/lang/String;)V super( "B" ); + // access to a m_numThings = 4; } @Override + // a()V public void doBaseThings( ) { - System.out.println( "Base things by B!" ); + // call to none/a.a()Ljava/lang/String; + System.out.println( "Base things by B! " + getName() ); } + // b()V public void doBThings( ) { + // access to a System.out.println( "" + m_numThings + " B things!" ); } } diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java index 11196d16..b3b83429 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java @@ -1,21 +1,27 @@ package cuchaz.enigma.inputs.inheritanceTree; +// none/d extends none/b public class SubsubclassAA extends SubclassA { protected SubsubclassAA( ) { + // call to none/b.(Ljava/lang/String;)V super( "AA" ); } @Override + // a()Ljava/lang/String; public String getName( ) { + // call to none/b.a()Ljava/lang/String; return "subsub" + super.getName(); } @Override + // a()V public void doBaseThings( ) { + // call to none/d.a()Ljava/lang/String; System.out.println( "Base things by " + getName() ); } } -- cgit v1.2.3 From 6736d9aac3e7d1591cba33852126abf79dd18a57 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 14 Sep 2014 21:54:58 -0400 Subject: added test to check constructor references --- build.gradle | 12 +- .../enigma/TestJarIndexConstructorReferences.java | 129 +++++++++++++++++++++ .../enigma/inputs/constructors/BaseClass.java | 17 +++ test/cuchaz/enigma/inputs/constructors/Caller.java | 47 ++++++++ .../enigma/inputs/constructors/SubClass.java | 31 +++++ .../enigma/inputs/constructors/SubSubClass.java | 12 ++ 6 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 test/cuchaz/enigma/TestJarIndexConstructorReferences.java create mode 100644 test/cuchaz/enigma/inputs/constructors/BaseClass.java create mode 100644 test/cuchaz/enigma/inputs/constructors/Caller.java create mode 100644 test/cuchaz/enigma/inputs/constructors/SubClass.java create mode 100644 test/cuchaz/enigma/inputs/constructors/SubSubClass.java diff --git a/build.gradle b/build.gradle index 10294292..767b0321 100644 --- a/build.gradle +++ b/build.gradle @@ -82,8 +82,16 @@ task jarInheritanceTree( type: Jar ) { archiveName( "testInheritanceTree.jar" ) } +task jarConstructors( type: Jar ) { + from( sourceSets.test.output ) { + include( "cuchaz/enigma/inputs/Keep.class" ) + include( "cuchaz/enigma/inputs/constructors/**" ) + } + archiveName( "testConstructors.jar" ) +} + task obfTestCases( type: proguard.gradle.ProGuardTask ) { - dependsOn jarLoneClass, jarInheritanceTree + dependsOn jarLoneClass, jarInheritanceTree, jarConstructors libraryjars( "${System.getProperty('java.home')}/lib/rt.jar" ) overloadaggressively @@ -94,7 +102,7 @@ task obfTestCases( type: proguard.gradle.ProGuardTask ) { keep( "class cuchaz.enigma.inputs.Keep" ) - def jarNames = [ "LoneClass", "InheritanceTree" ]; + def jarNames = [ "LoneClass", "InheritanceTree", "Constructors" ]; jarNames.each() { injars( "build/libs/test${it}.jar" ) outjars( "build/libs/test${it}.obf.jar" ) diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java new file mode 100644 index 00000000..38882a0c --- /dev/null +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByConstructor; +import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByMethod; +import static cuchaz.enigma.EntryFactory.newClass; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; + +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; + +public class TestJarIndexConstructorReferences +{ + private JarIndex m_index; + + private ClassEntry m_baseClass = new ClassEntry( "none/a" ); + private ClassEntry m_subClass = new ClassEntry( "none/c" ); + private ClassEntry m_subsubClass = new ClassEntry( "none/d" ); + private ClassEntry m_callerClass = new ClassEntry( "none/b" ); + + public TestJarIndexConstructorReferences( ) + throws Exception + { + m_index = new JarIndex(); + m_index.indexJar( new JarFile( "build/libs/testConstructors.obf.jar" ), false ); + } + + @Test + public void obfEntries( ) + { + assertThat( m_index.getObfClassEntries(), containsInAnyOrder( + newClass( "cuchaz/enigma/inputs/Keep" ), + m_baseClass, + m_subClass, + m_subsubClass, + m_callerClass + ) ); + } + + @Test + @SuppressWarnings( "unchecked" ) + public void baseDefault( ) + { + BehaviorEntry source = new ConstructorEntry( m_baseClass, "()V" ); + assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( + newBehaviorReferenceByMethod( source, m_callerClass.getName(), "a", "()V" ), + newBehaviorReferenceByConstructor( source, m_subClass.getName(), "()V" ), + newBehaviorReferenceByConstructor( source, m_subClass.getName(), "(III)V" ) + ) ); + } + + @Test + @SuppressWarnings( "unchecked" ) + public void baseInt( ) + { + BehaviorEntry source = new ConstructorEntry( m_baseClass, "(I)V" ); + assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( + newBehaviorReferenceByMethod( source, m_callerClass.getName(), "b", "()V" ) + ) ); + } + + @Test + @SuppressWarnings( "unchecked" ) + public void subDefault( ) + { + BehaviorEntry source = new ConstructorEntry( m_subClass, "()V" ); + assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( + newBehaviorReferenceByMethod( source, m_callerClass.getName(), "c", "()V" ), + newBehaviorReferenceByConstructor( source, m_subClass.getName(), "(I)V" ) + ) ); + } + + @Test + @SuppressWarnings( "unchecked" ) + public void subInt( ) + { + BehaviorEntry source = new ConstructorEntry( m_subClass, "(I)V" ); + assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( + newBehaviorReferenceByMethod( source, m_callerClass.getName(), "d", "()V" ), + newBehaviorReferenceByConstructor( source, m_subClass.getName(), "(II)V" ), + newBehaviorReferenceByConstructor( source, m_subsubClass.getName(), "(I)V" ) + ) ); + } + + @Test + @SuppressWarnings( "unchecked" ) + public void subIntInt( ) + { + BehaviorEntry source = new ConstructorEntry( m_subClass, "(II)V" ); + assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( + newBehaviorReferenceByMethod( source, m_callerClass.getName(), "e", "()V" ) + ) ); + } + + @Test + public void subIntIntInt( ) + { + BehaviorEntry source = new ConstructorEntry( m_subClass, "(III)V" ); + assertThat( m_index.getBehaviorReferences( source ), is( empty() ) ); + } + + @Test + @SuppressWarnings( "unchecked" ) + public void subsubInt( ) + { + BehaviorEntry source = new ConstructorEntry( m_subsubClass, "(I)V" ); + assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( + newBehaviorReferenceByMethod( source, m_callerClass.getName(), "f", "()V" ) + ) ); + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/BaseClass.java b/test/cuchaz/enigma/inputs/constructors/BaseClass.java new file mode 100644 index 00000000..e6d87681 --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/BaseClass.java @@ -0,0 +1,17 @@ +package cuchaz.enigma.inputs.constructors; + +// none/a +public class BaseClass +{ + // ()V + public BaseClass( ) + { + System.out.println( "Default constructor" ); + } + + // (I)V + public BaseClass( int i ) + { + System.out.println( "Int constructor " + i ); + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/Caller.java b/test/cuchaz/enigma/inputs/constructors/Caller.java new file mode 100644 index 00000000..f356b1b3 --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/Caller.java @@ -0,0 +1,47 @@ +package cuchaz.enigma.inputs.constructors; + +// none/b +public class Caller +{ + // a()V + public void callBaseDefault( ) + { + // none/a.()V + System.out.println( new BaseClass() ); + } + + // b()V + public void callBaseInt( ) + { + // none/a.(I)V + System.out.println( new BaseClass( 5 ) ); + } + + // c()V + public void callSubDefault( ) + { + // none/c.()V + System.out.println( new SubClass() ); + } + + // d()V + public void callSubInt( ) + { + // none/c.(I)V + System.out.println( new SubClass( 6 ) ); + } + + // e()V + public void callSubIntInt( ) + { + // none/c.(II)V + System.out.println( new SubClass( 4, 2 ) ); + } + + // f()V + public void callSubSubInt( ) + { + // none/d.(I)V + System.out.println( new SubSubClass( 3 ) ); + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/SubClass.java b/test/cuchaz/enigma/inputs/constructors/SubClass.java new file mode 100644 index 00000000..2235de37 --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/SubClass.java @@ -0,0 +1,31 @@ +package cuchaz.enigma.inputs.constructors; + +// none/c extends none/a +public class SubClass extends BaseClass +{ + // ()V + public SubClass( ) + { + // none/a.()V + } + + // (I)V + public SubClass( int num ) + { + // ()V + this(); + System.out.println( "SubClass " + num ); + } + + // (II)V + public SubClass( int a, int b ) + { + // (I)V + this( a + b ); + } + + public SubClass( int a, int b, int c ) + { + // none/a.()V + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java new file mode 100644 index 00000000..a9445d8b --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java @@ -0,0 +1,12 @@ +package cuchaz.enigma.inputs.constructors; + +// none/d extends none/c +public class SubSubClass extends SubClass +{ + // (I)V + public SubSubClass( int i ) + { + // none/c.(I)V + super( i ); + } +} -- cgit v1.2.3 From 72e918a5134c2bf747a476284bcfa1bd2ef2fa21 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 14 Sep 2014 23:56:43 -0400 Subject: added tests to check constructor tokens fixed a bug with constructor tokens too --- src/cuchaz/enigma/analysis/SourceIndex.java | 10 +- .../analysis/SourceIndexBehaviorVisitor.java | 51 ++++++- .../enigma/analysis/SourceIndexClassVisitor.java | 1 - .../enigma/TestJarIndexConstructorReferences.java | 29 +++- test/cuchaz/enigma/TestTokensConstructors.java | 152 +++++++++++++++++++++ test/cuchaz/enigma/TokenChecker.java | 71 ++++++++++ test/cuchaz/enigma/inputs/constructors/Caller.java | 15 +- .../inputs/constructors/DefaultConstructable.java | 6 + .../enigma/inputs/constructors/SubClass.java | 3 +- .../enigma/inputs/constructors/SubSubClass.java | 2 +- 10 files changed, 316 insertions(+), 24 deletions(-) create mode 100644 test/cuchaz/enigma/TestTokensConstructors.java create mode 100644 test/cuchaz/enigma/TokenChecker.java create mode 100644 test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 1a5a80d6..b777f9fd 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -20,7 +20,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.strobel.decompiler.languages.Region; -import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.AstNode; import cuchaz.enigma.mapping.Entry; @@ -56,7 +56,7 @@ public class SourceIndex return m_source; } - public Token getToken( Identifier node ) + public Token getToken( AstNode node ) { // get a token for this node's region Region region = node.getRegion(); @@ -71,7 +71,7 @@ public class SourceIndex ); // for tokens representing inner classes, make sure we only get the simple name - int pos = node.getName().lastIndexOf( '$' ); + int pos = node.toString().lastIndexOf( '$' ); if( pos >= 0 ) { token.end -= pos + 1; @@ -92,7 +92,7 @@ public class SourceIndex return token; } - public void addReference( Identifier node, EntryReference deobfReference ) + public void addReference( AstNode node, EntryReference deobfReference ) { Token token = getToken( node ); if( token != null ) @@ -102,7 +102,7 @@ public class SourceIndex } } - public void addDeclaration( Identifier node, Entry deobfEntry ) + public void addDeclaration( AstNode node, Entry deobfEntry ) { Token token = getToken( node ); if( token != null ) diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index ab505528..a1dac4b5 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -12,9 +12,11 @@ package cuchaz.enigma.analysis; import com.strobel.assembler.metadata.MemberReference; import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.MethodReference; import com.strobel.assembler.metadata.ParameterDefinition; import com.strobel.assembler.metadata.TypeReference; import com.strobel.decompiler.languages.TextLocation; +import com.strobel.decompiler.languages.java.ast.AstNode; import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; import com.strobel.decompiler.languages.java.ast.IdentifierExpression; import com.strobel.decompiler.languages.java.ast.InvocationExpression; @@ -24,6 +26,8 @@ import com.strobel.decompiler.languages.java.ast.MethodDeclaration; import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; @@ -58,14 +62,49 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor public Void visitInvocationExpression( InvocationExpression node, SourceIndex index ) { MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); + + // get the behavior entry ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); - if( node.getTarget() instanceof MemberReferenceExpression ) + BehaviorEntry behaviorEntry = null; + if( ref instanceof MethodReference ) { - MethodEntry methodEntry = new MethodEntry( classEntry, ref.getName(), ref.getSignature() ); - index.addReference( - ((MemberReferenceExpression)node.getTarget()).getMemberNameToken(), - new EntryReference( methodEntry, m_behaviorEntry ) - ); + MethodReference methodRef = (MethodReference)ref; + if( methodRef.isConstructor() ) + { + behaviorEntry = new ConstructorEntry( classEntry, ref.getSignature() ); + } + else if( methodRef.isTypeInitializer() ) + { + behaviorEntry = new ConstructorEntry( classEntry ); + } + else + { + behaviorEntry = new MethodEntry( classEntry, ref.getName(), ref.getSignature() ); + } + } + if( behaviorEntry != null ) + { + // get the node for the token + AstNode tokenNode = null; + if( node.getTarget() instanceof MemberReferenceExpression ) + { + tokenNode = ((MemberReferenceExpression)node.getTarget()).getMemberNameToken(); + } + else if( node.getTarget() instanceof SuperReferenceExpression ) + { + tokenNode = node.getTarget(); + } + else if( node.getTarget() instanceof ThisReferenceExpression ) + { + tokenNode = node.getTarget(); + } + if( tokenNode != null ) + { + index.addReference( + tokenNode, + new EntryReference( behaviorEntry, m_behaviorEntry ) + ); + } } return recurse( node, index ); diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index a1c82711..b7897268 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -97,7 +97,6 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, def.getSignature() ); index.addDeclaration( node.getNameToken(), constructorEntry ); - return node.acceptVisitor( new SourceIndexBehaviorVisitor( constructorEntry ), index ); } diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java index 38882a0c..c0691888 100644 --- a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -18,10 +18,13 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.is; +import java.io.File; +import java.util.Collection; import java.util.jar.JarFile; import org.junit.Test; +import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; @@ -30,17 +33,19 @@ import cuchaz.enigma.mapping.ConstructorEntry; public class TestJarIndexConstructorReferences { private JarIndex m_index; - + private ClassEntry m_baseClass = new ClassEntry( "none/a" ); - private ClassEntry m_subClass = new ClassEntry( "none/c" ); - private ClassEntry m_subsubClass = new ClassEntry( "none/d" ); + private ClassEntry m_subClass = new ClassEntry( "none/d" ); + private ClassEntry m_subsubClass = new ClassEntry( "none/e" ); + private ClassEntry m_defaultClass = new ClassEntry( "none/c" ); private ClassEntry m_callerClass = new ClassEntry( "none/b" ); - + public TestJarIndexConstructorReferences( ) throws Exception { + File jarFile = new File( "build/libs/testConstructors.obf.jar" ); m_index = new JarIndex(); - m_index.indexJar( new JarFile( "build/libs/testConstructors.obf.jar" ), false ); + m_index.indexJar( new JarFile( jarFile ), false ); } @Test @@ -51,6 +56,7 @@ public class TestJarIndexConstructorReferences m_baseClass, m_subClass, m_subsubClass, + m_defaultClass, m_callerClass ) ); } @@ -60,7 +66,8 @@ public class TestJarIndexConstructorReferences public void baseDefault( ) { BehaviorEntry source = new ConstructorEntry( m_baseClass, "()V" ); - assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( + Collection> references = m_index.getBehaviorReferences( source ); + assertThat( references, containsInAnyOrder( newBehaviorReferenceByMethod( source, m_callerClass.getName(), "a", "()V" ), newBehaviorReferenceByConstructor( source, m_subClass.getName(), "()V" ), newBehaviorReferenceByConstructor( source, m_subClass.getName(), "(III)V" ) @@ -126,4 +133,14 @@ public class TestJarIndexConstructorReferences newBehaviorReferenceByMethod( source, m_callerClass.getName(), "f", "()V" ) ) ); } + + @Test + @SuppressWarnings( "unchecked" ) + public void defaultConstructable( ) + { + BehaviorEntry source = new ConstructorEntry( m_defaultClass, "()V" ); + assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( + newBehaviorReferenceByMethod( source, m_callerClass.getName(), "g", "()V" ) + ) ); + } } diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java new file mode 100644 index 00000000..24091532 --- /dev/null +++ b/test/cuchaz/enigma/TestTokensConstructors.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByConstructor; +import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByMethod; +import static cuchaz.enigma.EntryFactory.newConstructor; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +import java.io.File; + +import org.junit.Test; + +import cuchaz.enigma.mapping.BehaviorEntry; + +public class TestTokensConstructors extends TokenChecker +{ + public TestTokensConstructors( ) + throws Exception + { + super( new File( "build/libs/testConstructors.obf.jar" ) ); + } + + @Test + public void baseDeclarations( ) + { + assertThat( getDeclarationToken( newConstructor( "none/a", "()V" ) ), is( "a" ) ); + assertThat( getDeclarationToken( newConstructor( "none/a", "(I)V" ) ), is( "a" ) ); + } + + @Test + public void subDeclarations( ) + { + assertThat( getDeclarationToken( newConstructor( "none/d", "()V" ) ), is( "d" ) ); + assertThat( getDeclarationToken( newConstructor( "none/d", "(I)V" ) ), is( "d" ) ); + assertThat( getDeclarationToken( newConstructor( "none/d", "(II)V" ) ), is( "d" ) ); + assertThat( getDeclarationToken( newConstructor( "none/d", "(III)V" ) ), is( "d" ) ); + } + + @Test + public void subsubDeclarations( ) + { + assertThat( getDeclarationToken( newConstructor( "none/e", "(I)V" ) ), is( "e" ) ); + } + + @Test + public void defaultDeclarations( ) + { + assertThat( getDeclarationToken( newConstructor( "none/c", "()V" ) ), nullValue() ); + } + + @Test + public void baseDefaultReferences( ) + { + BehaviorEntry source = newConstructor( "none/a", "()V" ); + assertThat( + getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "a", "()V" ) ), + containsInAnyOrder( "a" ) + ); + assertThat( + getReferenceTokens( newBehaviorReferenceByConstructor( source, "none/d", "()V" ) ), + containsInAnyOrder( "super" ) // implicit call, decompiled to "super" + ); + assertThat( + getReferenceTokens( newBehaviorReferenceByConstructor( source, "none/d", "(III)V" ) ), + containsInAnyOrder( "super" ) // implicit call, decompiled to "super" + ); + } + + @Test + public void baseIntReferences( ) + { + BehaviorEntry source = newConstructor( "none/a", "(I)V" ); + assertThat( + getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "b", "()V" ) ), + containsInAnyOrder( "a" ) + ); + } + + @Test + public void subDefaultReferences( ) + { + BehaviorEntry source = newConstructor( "none/d", "()V" ); + assertThat( + getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "c", "()V" ) ), + containsInAnyOrder( "d" ) + ); + assertThat( + getReferenceTokens( newBehaviorReferenceByConstructor( source, "none/d", "(I)V" ) ), + containsInAnyOrder( "this" ) + ); + } + + @Test + public void subIntReferences( ) + { + BehaviorEntry source = newConstructor( "none/d", "(I)V" ); + assertThat( + getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "d", "()V" ) ), + containsInAnyOrder( "d" ) + ); + assertThat( + getReferenceTokens( newBehaviorReferenceByConstructor( source, "none/d", "(II)V" ) ), + containsInAnyOrder( "this" ) + ); + assertThat( + getReferenceTokens( newBehaviorReferenceByConstructor( source, "none/e", "(I)V" ) ), + containsInAnyOrder( "super" ) + ); + } + + @Test + public void subIntIntReferences( ) + { + BehaviorEntry source = newConstructor( "none/d", "(II)V" ); + assertThat( + getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "e", "()V" ) ), + containsInAnyOrder( "d" ) + ); + } + + @Test + public void subsubIntReferences( ) + { + BehaviorEntry source = newConstructor( "none/e", "(I)V" ); + assertThat( + getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "f", "()V" ) ), + containsInAnyOrder( "e" ) + ); + } + + @Test + public void defaultConstructableReferences( ) + { + BehaviorEntry source = newConstructor( "none/c", "()V" ); + assertThat( + getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "g", "()V" ) ), + containsInAnyOrder( "c" ) + ); + } +} diff --git a/test/cuchaz/enigma/TokenChecker.java b/test/cuchaz/enigma/TokenChecker.java new file mode 100644 index 00000000..07ab9eca --- /dev/null +++ b/test/cuchaz/enigma/TokenChecker.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +import com.beust.jcommander.internal.Lists; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.analysis.TreeDumpVisitor; +import cuchaz.enigma.mapping.Entry; + +public class TokenChecker +{ + private Deobfuscator m_deobfuscator; + + protected TokenChecker( File jarFile ) + throws IOException + { + m_deobfuscator = new Deobfuscator( jarFile ); + } + + protected String getDeclarationToken( Entry entry ) + { + // decompile the class + CompilationUnit tree = m_deobfuscator.getSourceTree( entry.getClassName() ); + // DEBUG + //tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null ); + String source = m_deobfuscator.getSource( tree ); + SourceIndex index = m_deobfuscator.getSourceIndex( tree, source ); + + // get the token value + Token token = index.getDeclarationToken( entry ); + if( token == null ) + { + return null; + } + return source.substring( token.start, token.end ); + } + + @SuppressWarnings( "unchecked" ) + protected Collection getReferenceTokens( EntryReference reference ) + { + // decompile the class + CompilationUnit tree = m_deobfuscator.getSourceTree( reference.context.getClassName() ); + String source = m_deobfuscator.getSource( tree ); + SourceIndex index = m_deobfuscator.getSourceIndex( tree, source ); + + // get the token values + List values = Lists.newArrayList(); + for( Token token : index.getReferenceTokens( (EntryReference)reference ) ) + { + values.add( source.substring( token.start, token.end ) ); + } + return values; + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/Caller.java b/test/cuchaz/enigma/inputs/constructors/Caller.java index f356b1b3..b2186198 100644 --- a/test/cuchaz/enigma/inputs/constructors/Caller.java +++ b/test/cuchaz/enigma/inputs/constructors/Caller.java @@ -20,28 +20,35 @@ public class Caller // c()V public void callSubDefault( ) { - // none/c.()V + // none/d.()V System.out.println( new SubClass() ); } // d()V public void callSubInt( ) { - // none/c.(I)V + // none/d.(I)V System.out.println( new SubClass( 6 ) ); } // e()V public void callSubIntInt( ) { - // none/c.(II)V + // none/d.(II)V System.out.println( new SubClass( 4, 2 ) ); } // f()V public void callSubSubInt( ) { - // none/d.(I)V + // none/e.(I)V System.out.println( new SubSubClass( 3 ) ); } + + // g()V + public void callDefaultConstructable() + { + // none/c.()V + System.out.println( new DefaultConstructable() ); + } } diff --git a/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java new file mode 100644 index 00000000..6cfd35e6 --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java @@ -0,0 +1,6 @@ +package cuchaz.enigma.inputs.constructors; + +public class DefaultConstructable +{ + // only default constructor +} diff --git a/test/cuchaz/enigma/inputs/constructors/SubClass.java b/test/cuchaz/enigma/inputs/constructors/SubClass.java index 2235de37..6ef77323 100644 --- a/test/cuchaz/enigma/inputs/constructors/SubClass.java +++ b/test/cuchaz/enigma/inputs/constructors/SubClass.java @@ -1,6 +1,6 @@ package cuchaz.enigma.inputs.constructors; -// none/c extends none/a +// none/d extends none/a public class SubClass extends BaseClass { // ()V @@ -24,6 +24,7 @@ public class SubClass extends BaseClass this( a + b ); } + // (III)V public SubClass( int a, int b, int c ) { // none/a.()V diff --git a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java index a9445d8b..76a0f1f3 100644 --- a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java +++ b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java @@ -1,6 +1,6 @@ package cuchaz.enigma.inputs.constructors; -// none/d extends none/c +// none/e extends none/d public class SubSubClass extends SubClass { // (I)V -- cgit v1.2.3 From 17427430e81c27617ec3653c412697b5d2583915 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 15 Sep 2014 00:03:14 -0400 Subject: changed "Show Calls" menu to search for calls to the default constructor when used on a class token --- src/cuchaz/enigma/gui/Gui.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 8ae16f42..7fac02de 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -1041,7 +1041,7 @@ public class Gui m_renameMenu.setEnabled( isToken ); m_showInheritanceMenu.setEnabled( isClassEntry || isMethodEntry || isConstructorEntry ); m_showImplementationsMenu.setEnabled( isClassEntry || isMethodEntry ); - m_showCallsMenu.setEnabled( isFieldEntry || isMethodEntry || isConstructorEntry ); + m_showCallsMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ); m_openEntryMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ); m_openPreviousMenu.setEnabled( m_controller.hasPreviousLocation() ); } @@ -1191,7 +1191,14 @@ public class Gui return; } - if( m_reference.entry instanceof FieldEntry ) + if( m_reference.entry instanceof ClassEntry ) + { + // look for calls to the default constructor + // TODO: get a list of all the constructors and find calls to all of them + BehaviorReferenceTreeNode node = m_controller.getMethodReferences( new ConstructorEntry( (ClassEntry)m_reference.entry, "()V" ) ); + m_callsTree.setModel( new DefaultTreeModel( node ) ); + } + else if( m_reference.entry instanceof FieldEntry ) { FieldReferenceTreeNode node = m_controller.getFieldReferences( (FieldEntry)m_reference.entry ); m_callsTree.setModel( new DefaultTreeModel( node ) ); -- cgit v1.2.3 From c65a64fc89169456febc1b4c953dbcfbafdc5f0e Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 18 Sep 2014 00:17:43 -0400 Subject: added better error handling for source export added checks to make sure we don't try to decopmile classes outside of the jar --- src/cuchaz/enigma/Deobfuscator.java | 29 +++++++--- src/cuchaz/enigma/TranslatingTypeLoader.java | 8 ++- src/cuchaz/enigma/gui/Gui.java | 84 ++++++++++++++-------------- src/cuchaz/enigma/gui/GuiController.java | 13 ++++- 4 files changed, 80 insertions(+), 54 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 0847049e..1178ed60 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -200,6 +200,12 @@ public class Deobfuscator className = classMapping.getDeobfName(); } + // is this class even in the jar? + if( !m_jarIndex.containsObfClass( new ClassEntry( className ) ) ) + { + return null; + } + // set the type loader m_settings.setTypeLoader( new TranslatingTypeLoader( m_jar, @@ -279,15 +285,22 @@ public class Deobfuscator progress.onProgress( i++, deobfClassEntry.toString() ); } - // get the source - String source = getSource( getSourceTree( obfClassEntry.getName() ) ); - - // write the file - File file = new File( dirOut, deobfClassEntry.getName().replace( '.', '/' ) + ".java" ); - file.getParentFile().mkdirs(); - try( FileWriter out = new FileWriter( file ) ) + try + { + // get the source + String source = getSource( getSourceTree( obfClassEntry.getName() ) ); + + // write the file + File file = new File( dirOut, deobfClassEntry.getName().replace( '.', '/' ) + ".java" ); + file.getParentFile().mkdirs(); + try( FileWriter out = new FileWriter( file ) ) + { + out.write( source ); + } + } + catch( Throwable t ) { - out.write( source ); + throw new Error( "Unable to deobfuscate class " + deobfClassEntry.toString() + " (" + obfClassEntry.toString() + ")", t ); } } diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index e70093eb..d321421a 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -124,10 +124,11 @@ public class TranslatingTypeLoader implements ITypeLoader ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry( deobfClassEntry ); // is this an inner class referenced directly? - if( m_jarIndex.getOuterClass( obfClassEntry.getName() ) != null ) + String obfOuterClassName = m_jarIndex.getOuterClass( obfClassEntry.getName() ); + if( obfOuterClassName != null ) { // this class doesn't really exist. Reference it by outer$inner instead - System.err.println( String.format( "WARNING: class %s referenced by bare inner name", deobfClassName ) ); + System.err.println( String.format( "WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassName ) ); return null; } @@ -210,6 +211,9 @@ public class TranslatingTypeLoader implements ITypeLoader // sanity checking assertClassName( c, deobfClassEntry ); + // DEBUG + Util.writeClass( c ); + // we have a transformed class! return c.toBytecode(); } diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 7fac02de..1995cb80 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -162,11 +162,7 @@ public class Gui @Override public void onSelectClass( ClassEntry classEntry ) { - if( m_reference != null ) - { - m_controller.savePreviousReference( m_reference ); - } - m_controller.openDeclaration( classEntry ); + navigateTo( classEntry ); } } ); JScrollPane obfScroller = new JScrollPane( m_obfClasses ); @@ -182,11 +178,7 @@ public class Gui @Override public void onSelectClass( ClassEntry classEntry ) { - if( m_reference != null ) - { - m_controller.savePreviousReference( m_reference ); - } - m_controller.openDeclaration( classEntry ); + navigateTo( classEntry ); } } ); JScrollPane deobfScroller = new JScrollPane( m_deobfClasses ); @@ -247,7 +239,7 @@ public class Gui break; case KeyEvent.VK_N: - openDeclaration(); + navigateTo( m_reference.entry ); break; case KeyEvent.VK_P: @@ -335,7 +327,7 @@ public class Gui @Override public void actionPerformed( ActionEvent event ) { - openDeclaration(); + navigateTo( m_reference.entry ); } } ); menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_N, 0 ) ); @@ -379,22 +371,15 @@ public class Gui Object node = path.getLastPathComponent(); if( node instanceof ClassInheritanceTreeNode ) { - if( m_reference != null ) - { - m_controller.savePreviousReference( m_reference ); - } - m_controller.openDeclaration( new ClassEntry( ((ClassInheritanceTreeNode)node).getObfClassName() ) ); + ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode)node; + navigateTo( new ClassEntry( classNode.getObfClassName() ) ); } else if( node instanceof MethodInheritanceTreeNode ) { MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode)node; if( methodNode.isImplemented() ) { - if( m_reference != null ) - { - m_controller.savePreviousReference( m_reference ); - } - m_controller.openDeclaration( methodNode.getMethodEntry() ); + navigateTo( methodNode.getMethodEntry() ); } } } @@ -425,12 +410,12 @@ public class Gui if( node instanceof ClassImplementationsTreeNode ) { ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode)node; - m_controller.openDeclaration( classNode.getClassEntry() ); + navigateTo( classNode.getClassEntry() ); } else if( node instanceof MethodImplementationsTreeNode ) { MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode)node; - m_controller.openDeclaration( methodNode.getMethodEntry() ); + navigateTo( methodNode.getMethodEntry() ); } } } @@ -460,18 +445,14 @@ public class Gui Object node = path.getLastPathComponent(); if( node instanceof ReferenceTreeNode ) { - if( m_reference != null ) - { - m_controller.savePreviousReference( m_reference ); - } ReferenceTreeNode referenceNode = ((ReferenceTreeNode)node); if( referenceNode.getReference() != null ) { - m_controller.openReference( referenceNode.getReference() ); + navigateTo( referenceNode.getReference() ); } else { - m_controller.openDeclaration( referenceNode.getEntry() ); + navigateTo( referenceNode.getEntry() ); } } } @@ -1028,6 +1009,7 @@ public class Gui boolean isFieldEntry = isToken && m_reference.entry instanceof FieldEntry; boolean isMethodEntry = isToken && m_reference.entry instanceof MethodEntry; boolean isConstructorEntry = isToken && m_reference.entry instanceof ConstructorEntry; + boolean isInJar = isToken && m_controller.entryIsInJar( m_reference.entry.getClassEntry() ); if( isToken ) { @@ -1038,14 +1020,42 @@ public class Gui clearReference(); } - m_renameMenu.setEnabled( isToken ); + m_renameMenu.setEnabled( isInJar && isToken ); m_showInheritanceMenu.setEnabled( isClassEntry || isMethodEntry || isConstructorEntry ); m_showImplementationsMenu.setEnabled( isClassEntry || isMethodEntry ); m_showCallsMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ); - m_openEntryMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ); + m_openEntryMenu.setEnabled( isInJar && ( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ) ); m_openPreviousMenu.setEnabled( m_controller.hasPreviousLocation() ); } + private void navigateTo( Entry entry ) + { + if( !m_controller.entryIsInJar( entry ) ) + { + // entry is not in the jar. Ignore it + return; + } + if( m_reference != null ) + { + m_controller.savePreviousReference( m_reference ); + } + m_controller.openDeclaration( entry ); + } + + private void navigateTo( EntryReference reference ) + { + if( !m_controller.entryIsInJar( reference.getClassEntry() ) ) + { + // reference is not in the jar. Ignore it + return; + } + if( m_reference != null ) + { + m_controller.savePreviousReference( m_reference ); + } + m_controller.openReference( reference ); + } + private void startRename( ) { // init the text box @@ -1232,16 +1242,6 @@ public class Gui return new TreePath( nodes.toArray() ); } - private void openDeclaration( ) - { - if( m_reference == null ) - { - return; - } - m_controller.savePreviousReference( m_reference ); - m_controller.openDeclaration( m_reference.entry ); - } - private void close( ) { if( !m_controller.isDirty() ) diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index c0fb2e40..3f54ecd4 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -170,7 +170,7 @@ public class GuiController return m_deobfuscator.hasMapping( m_deobfuscator.obfuscateEntry( deobfEntry ) ); } - public boolean entryIsObfuscatedIdenfitier( Entry deobfEntry ) + public boolean entryIsInJar( Entry deobfEntry ) { return m_deobfuscator.isObfuscatedIdentifier( m_deobfuscator.obfuscateEntry( deobfEntry ) ); } @@ -268,6 +268,10 @@ public class GuiController // get the reference target class EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); ClassEntry obfClassEntry = obfReference.getClassEntry().getOuterClassEntry(); + if( !m_deobfuscator.isObfuscatedIdentifier( obfClassEntry ) ) + { + throw new IllegalArgumentException( "Entry must be in the jar!" ); + } if( m_currentObfClass == null || !m_currentObfClass.equals( obfClassEntry ) ) { // deobfuscate the class, then navigate to the reference @@ -347,6 +351,11 @@ public class GuiController { // decompile,deobfuscate the bytecode CompilationUnit sourceTree = m_deobfuscator.getSourceTree( classEntry.getClassName() ); + if( sourceTree == null ) + { + // decompilation of this class is not supported + return; + } String source = m_deobfuscator.getSource( sourceTree ); m_index = m_deobfuscator.getSourceIndex( sourceTree, source ); m_gui.setSource( m_index.getSource() ); @@ -365,7 +374,7 @@ public class GuiController { deobfuscatedTokens.add( token ); } - else if( entryIsObfuscatedIdenfitier( reference.entry ) ) + else if( entryIsInJar( reference.entry ) ) { obfuscatedTokens.add( token ); } -- cgit v1.2.3 From a628fb9396d10cfbeb03c88cb1c2c119ae202a21 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 18 Sep 2014 22:41:25 -0400 Subject: fixed crash with jar loading --- src/cuchaz/enigma/bytecode/ClassRenamer.java | 20 ++++++++++++++++++-- src/cuchaz/enigma/mapping/ClassEntry.java | 4 ++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index efe22a18..86a0433b 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -74,7 +74,7 @@ public class ClassRenamer } } - public static Set getAllClassEntries( CtClass c ) + public static Set getAllClassEntries( final CtClass c ) { // get the classes that javassist knows about final Set entries = Sets.newHashSet(); @@ -85,7 +85,23 @@ public class ClassRenamer { if( obj instanceof String ) { - entries.add( new ClassEntry( (String)obj ) ); + String str = (String)obj; + + // javassist throws a lot of weird things at this map + // I either have to implement my on class scanner, or just try to filter out the weirdness + // I'm opting to filter out the weirdness for now + + // skip anything with generic arguments + if( str.indexOf( '<' ) >= 0 || str.indexOf( '>' ) >= 0 || str.indexOf( ';' ) >= 0 ) + { + return null; + } + + // convert path/to/class.inner to path/to/class$inner + str = str.replace( '.', '$' ); + + // remember everything else + entries.add( new ClassEntry( str ) ); } return null; } diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index f8477c92..c6faa506 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -27,14 +27,14 @@ public class ClassEntry implements Entry, Serializable } if( className.indexOf( '.' ) >= 0 ) { - throw new IllegalArgumentException( "Class name must be in JVM format. ie, path/to/package/class$inner" ); + throw new IllegalArgumentException( "Class name must be in JVM format. ie, path/to/package/class$inner : " + className ); } m_name = className; if( isInnerClass() && getInnerClassName().indexOf( '/' ) >= 0 ) { - throw new IllegalArgumentException( "Inner class must not have a package: " + getInnerClassName() ); + throw new IllegalArgumentException( "Inner class must not have a package: " + className ); } } -- cgit v1.2.3 From 725da3d2b3c4e4410ccbd661e83230b357428fab Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 18 Sep 2014 23:03:19 -0400 Subject: fixed issue with inner class detection --- src/cuchaz/enigma/analysis/JarIndex.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index f4843164..6c955bcd 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -417,12 +417,11 @@ public class JarIndex continue; } + ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); + ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getMethodInfo().getDescriptor() ); + // who calls this constructor? Set callerClasses = Sets.newHashSet(); - ConstructorEntry constructorEntry = new ConstructorEntry( - new ClassEntry( Descriptor.toJvmName( c.getName() ) ), - constructor.getMethodInfo().getDescriptor() - ); for( EntryReference reference : getBehaviorReferences( constructorEntry ) ) { callerClasses.add( reference.context.getClassEntry() ); @@ -431,7 +430,13 @@ public class JarIndex // is this called by exactly one class? if( callerClasses.size() == 1 ) { - return callerClasses.iterator().next().getName(); + ClassEntry callerClassEntry = callerClasses.iterator().next(); + + // does this class make sense as an outer class? + if( !callerClassEntry.equals( classEntry ) ) + { + return callerClassEntry.getName(); + } } else if( callerClasses.size() > 1 ) { -- cgit v1.2.3 From 7106c3a4b546aca645a19d40216185d6723409bc Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 20 Sep 2014 12:56:50 -0400 Subject: added warning message to the crash dialog about choosing exit --- src/cuchaz/enigma/gui/CrashDialog.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cuchaz/enigma/gui/CrashDialog.java b/src/cuchaz/enigma/gui/CrashDialog.java index 501080ec..0eb9830c 100644 --- a/src/cuchaz/enigma/gui/CrashDialog.java +++ b/src/cuchaz/enigma/gui/CrashDialog.java @@ -57,6 +57,7 @@ public class CrashDialog FlowLayout buttonsLayout = new FlowLayout(); buttonsLayout.setAlignment( FlowLayout.RIGHT ); buttonsPanel.setLayout( buttonsLayout ); + buttonsPanel.add( GuiTricks.unboldLabel( new JLabel( "If you choose exit, you will lose any unsaved work." ) ) ); JButton ignoreButton = new JButton( "Ignore" ); ignoreButton.addActionListener( new ActionListener( ) { -- cgit v1.2.3 From a8d0f97c270e32653aa246d7437478885077e24d Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 20 Sep 2014 17:11:25 -0400 Subject: cleaned up imports. I have no idea why Eclipse likes importing jcommander classes instead of guava classes, but it's annoyong! --- src/cuchaz/enigma/Deobfuscator.java | 2 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 5 +++-- src/cuchaz/enigma/analysis/EntryRenamer.java | 2 +- src/cuchaz/enigma/analysis/JarClassIterator.java | 2 +- src/cuchaz/enigma/bytecode/BytecodeTools.java | 2 +- src/cuchaz/enigma/bytecode/ClassRenamer.java | 2 +- src/cuchaz/enigma/bytecode/ClassTranslator.java | 2 +- src/cuchaz/enigma/convert/ClassIdentity.java | 2 +- src/cuchaz/enigma/convert/ClassMatcher.java | 4 ++-- src/cuchaz/enigma/convert/ClassMatching.java | 4 ++-- src/cuchaz/enigma/convert/ClassNamer.java | 2 +- src/cuchaz/enigma/gui/ClassSelector.java | 4 ++-- src/cuchaz/enigma/mapping/Mappings.java | 2 +- src/cuchaz/enigma/mapping/Translator.java | 2 +- test/cuchaz/enigma/TestDeobfuscator.java | 4 ++-- test/cuchaz/enigma/TokenChecker.java | 3 +-- 16 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 1178ed60..1d6f02c9 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -22,8 +22,8 @@ import java.util.jar.JarFile; import javassist.bytecode.Descriptor; -import com.beust.jcommander.internal.Sets; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.strobel.assembler.metadata.MetadataSystem; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.decompiler.DecompilerContext; diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index d321421a..8b969857 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -13,6 +13,7 @@ package cuchaz.enigma; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -24,7 +25,7 @@ import javassist.CtClass; import javassist.NotFoundException; import javassist.bytecode.Descriptor; -import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Maps; import com.strobel.assembler.metadata.Buffer; import com.strobel.assembler.metadata.ClasspathTypeLoader; import com.strobel.assembler.metadata.ITypeLoader; @@ -212,7 +213,7 @@ public class TranslatingTypeLoader implements ITypeLoader assertClassName( c, deobfClassEntry ); // DEBUG - Util.writeClass( c ); + //Util.writeClass( c ); // we have a transformed class! return c.toBytecode(); diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java index 4b2c0b78..e9483caa 100644 --- a/src/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -16,7 +16,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java index 10ae8052..f65b8e79 100644 --- a/src/cuchaz/enigma/analysis/JarClassIterator.java +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -25,7 +25,7 @@ import javassist.CtClass; import javassist.NotFoundException; import javassist.bytecode.Descriptor; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import cuchaz.enigma.Constants; import cuchaz.enigma.mapping.ClassEntry; diff --git a/src/cuchaz/enigma/bytecode/BytecodeTools.java b/src/cuchaz/enigma/bytecode/BytecodeTools.java index 0de9bd6b..4407a904 100644 --- a/src/cuchaz/enigma/bytecode/BytecodeTools.java +++ b/src/cuchaz/enigma/bytecode/BytecodeTools.java @@ -26,7 +26,7 @@ import javassist.bytecode.CodeAttribute; import javassist.bytecode.ConstPool; import javassist.bytecode.ExceptionTable; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index 86a0433b..9f0845da 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -20,8 +20,8 @@ import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.InnerClassesAttribute; -import com.beust.jcommander.internal.Sets; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.SignatureUpdater; diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 885b45fe..88926928 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -19,7 +19,7 @@ import javassist.CtMethod; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; -import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Maps; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.FieldEntry; diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index b3b043e5..1de345ff 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -34,9 +34,9 @@ import javassist.expr.FieldAccess; import javassist.expr.MethodCall; import javassist.expr.NewExpr; -import com.beust.jcommander.internal.Maps; import com.google.common.collect.HashMultiset; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Multiset; import cuchaz.enigma.Constants; diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index 135d076d..eb7a31df 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -29,13 +29,13 @@ import java.util.jar.JarFile; import javassist.CtBehavior; import javassist.CtClass; -import com.beust.jcommander.internal.Lists; -import com.beust.jcommander.internal.Sets; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; import cuchaz.enigma.TranslatingTypeLoader; import cuchaz.enigma.analysis.JarIndex; diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java index 5511902d..e45c0e1a 100644 --- a/src/cuchaz/enigma/convert/ClassMatching.java +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -17,11 +17,11 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import com.beust.jcommander.internal.Lists; -import com.beust.jcommander.internal.Maps; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Multimap; public class ClassMatching diff --git a/src/cuchaz/enigma/convert/ClassNamer.java b/src/cuchaz/enigma/convert/ClassNamer.java index 1cd96657..a01aec5c 100644 --- a/src/cuchaz/enigma/convert/ClassNamer.java +++ b/src/cuchaz/enigma/convert/ClassNamer.java @@ -12,8 +12,8 @@ package cuchaz.enigma.convert; import java.util.Map; -import com.beust.jcommander.internal.Maps; import com.google.common.collect.BiMap; +import com.google.common.collect.Maps; public class ClassNamer { diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java index c0f7f3c5..8365def1 100644 --- a/src/cuchaz/enigma/gui/ClassSelector.java +++ b/src/cuchaz/enigma/gui/ClassSelector.java @@ -23,9 +23,9 @@ import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; -import com.beust.jcommander.internal.Lists; -import com.beust.jcommander.internal.Maps; import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import cuchaz.enigma.mapping.ClassEntry; diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 99cb85f0..f855f580 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -20,8 +20,8 @@ import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; -import com.beust.jcommander.internal.Sets; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import cuchaz.enigma.Util; import cuchaz.enigma.analysis.TranslationIndex; diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 659ce9a2..b438e08d 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -14,7 +14,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Maps; import cuchaz.enigma.analysis.TranslationIndex; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java index 3e679fb9..3bb0c480 100644 --- a/test/cuchaz/enigma/TestDeobfuscator.java +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -11,7 +11,7 @@ ******************************************************************************/ package cuchaz.enigma; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import java.io.File; import java.io.IOException; @@ -19,7 +19,7 @@ import java.util.List; import org.junit.Test; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import cuchaz.enigma.mapping.ClassEntry; diff --git a/test/cuchaz/enigma/TokenChecker.java b/test/cuchaz/enigma/TokenChecker.java index 07ab9eca..c0852f32 100644 --- a/test/cuchaz/enigma/TokenChecker.java +++ b/test/cuchaz/enigma/TokenChecker.java @@ -15,13 +15,12 @@ import java.io.IOException; import java.util.Collection; import java.util.List; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import com.strobel.decompiler.languages.java.ast.CompilationUnit; import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.Token; -import cuchaz.enigma.analysis.TreeDumpVisitor; import cuchaz.enigma.mapping.Entry; public class TokenChecker -- cgit v1.2.3 From 0cf045308d25ab020fe23d1d000cb36635f83a75 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 20 Sep 2014 17:15:33 -0400 Subject: avoid some unnecessary work with inner classes --- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 2fb5fe0f..5044e065 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -48,7 +48,7 @@ public class InnerClassWriter // write the inner classes if needed Collection obfInnerClassNames = m_jarIndex.getInnerClasses( obfOuterClassName ); - if( obfInnerClassNames != null ) + if( obfInnerClassNames != null && !obfInnerClassNames.isEmpty() ) { writeInnerClasses( c, obfOuterClassName, obfInnerClassNames ); } -- cgit v1.2.3 From 57b53fc267aa606ee4d1bde5512632c82cbab9c5 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 20 Sep 2014 21:44:50 -0400 Subject: added token highlighting for things outside of the jar --- src/cuchaz/enigma/analysis/SourceIndex.java | 3 ++- src/cuchaz/enigma/gui/BoxHighlightPainter.java | 7 +++++-- src/cuchaz/enigma/gui/Gui.java | 9 +++++++-- src/cuchaz/enigma/gui/GuiController.java | 7 ++++++- src/cuchaz/enigma/gui/OtherHighlightPainter.java | 22 ++++++++++++++++++++++ 5 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 src/cuchaz/enigma/gui/OtherHighlightPainter.java diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index b777f9fd..fb5cc655 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -77,7 +77,7 @@ public class SourceIndex token.end -= pos + 1; } - // HACKHACK: sometimes node regions are off by one + /* HACKHACK: sometimes node regions are off by one // I think this is a bug in Procyon, but it's easy to work around if( !Character.isJavaIdentifierStart( m_source.charAt( token.start ) ) ) { @@ -88,6 +88,7 @@ public class SourceIndex throw new IllegalArgumentException( "Region " + region + " does not describe valid token: '" + m_source.substring( token.start, token.end ) + "'" ); } } + */ return token; } diff --git a/src/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/cuchaz/enigma/gui/BoxHighlightPainter.java index 30b2b021..df63f5a8 100644 --- a/src/cuchaz/enigma/gui/BoxHighlightPainter.java +++ b/src/cuchaz/enigma/gui/BoxHighlightPainter.java @@ -36,8 +36,11 @@ public abstract class BoxHighlightPainter implements Highlighter.HighlightPainte Rectangle bounds = getBounds( text, start, end ); // fill the area - g.setColor( m_fillColor ); - g.fillRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); + if( m_fillColor != null ) + { + g.setColor( m_fillColor ); + g.fillRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); + } // draw a box around the area g.setColor( m_borderColor ); diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 1995cb80..5eed728a 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -102,6 +102,7 @@ public class Gui private JPanel m_infoPanel; private ObfuscatedHighlightPainter m_obfuscatedHighlightPainter; private DeobfuscatedHighlightPainter m_deobfuscatedHighlightPainter; + private OtherHighlightPainter m_otherHighlightPainter; private SelectionHighlightPainter m_selectionHighlightPainter; private JTree m_inheritanceTree; private JTree m_implementationsTree; @@ -205,6 +206,7 @@ public class Gui DefaultSyntaxKit.initKit(); m_obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); m_deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); + m_otherHighlightPainter = new OtherHighlightPainter(); m_selectionHighlightPainter = new SelectionHighlightPainter(); m_editor = new JEditorPane(); m_editor.setEditable( false ); @@ -870,12 +872,11 @@ public class Gui showToken( sortedTokens.get( 0 ) ); } - public void setHighlightedTokens( Iterable obfuscatedTokens, Iterable deobfuscatedTokens ) + public void setHighlightedTokens( Iterable obfuscatedTokens, Iterable deobfuscatedTokens, Iterable otherTokens ) { // remove any old highlighters m_editor.getHighlighter().removeAllHighlights(); - // color things based on the index if( obfuscatedTokens != null ) { @@ -885,6 +886,10 @@ public class Gui { setHighlightedTokens( deobfuscatedTokens, m_deobfuscatedHighlightPainter ); } + if( otherTokens != null ) + { + setHighlightedTokens( otherTokens, m_otherHighlightPainter ); + } redraw(); } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 3f54ecd4..bbefe606 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -367,6 +367,7 @@ public class GuiController // set the highlighted tokens List obfuscatedTokens = Lists.newArrayList(); List deobfuscatedTokens = Lists.newArrayList(); + List otherTokens = Lists.newArrayList(); for( Token token : m_index.referenceTokens() ) { EntryReference reference = m_index.getDeobfReference( token ); @@ -378,8 +379,12 @@ public class GuiController { obfuscatedTokens.add( token ); } + else + { + otherTokens.add( token ); + } } - m_gui.setHighlightedTokens( obfuscatedTokens, deobfuscatedTokens ); + m_gui.setHighlightedTokens( obfuscatedTokens, deobfuscatedTokens, otherTokens ); } }.start(); } diff --git a/src/cuchaz/enigma/gui/OtherHighlightPainter.java b/src/cuchaz/enigma/gui/OtherHighlightPainter.java new file mode 100644 index 00000000..78de7325 --- /dev/null +++ b/src/cuchaz/enigma/gui/OtherHighlightPainter.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; + +public class OtherHighlightPainter extends BoxHighlightPainter +{ + public OtherHighlightPainter( ) + { + // grey + super( null, new Color( 180, 180, 180 ) ); + } +} -- cgit v1.2.3 From 84d3bd601094a733900260ad2bd0577cf65978ab Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 20 Sep 2014 21:45:32 -0400 Subject: removed workaround for procyon bug --- src/cuchaz/enigma/analysis/SourceIndex.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index fb5cc655..49451b90 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -77,19 +77,6 @@ public class SourceIndex token.end -= pos + 1; } - /* HACKHACK: sometimes node regions are off by one - // I think this is a bug in Procyon, but it's easy to work around - if( !Character.isJavaIdentifierStart( m_source.charAt( token.start ) ) ) - { - token.start++; - token.end++; - if( !Character.isJavaIdentifierStart( m_source.charAt( token.start ) ) ) - { - throw new IllegalArgumentException( "Region " + region + " does not describe valid token: '" + m_source.substring( token.start, token.end ) + "'" ); - } - } - */ - return token; } -- cgit v1.2.3 From 8409dea980fa03c06b180969c5e0696f7cb5474b Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 21 Sep 2014 00:32:03 -0400 Subject: started unit testing for inner/anonymous class detection --- build.gradle | 47 ++++-- src/cuchaz/enigma/analysis/JarIndex.java | 181 +++++++++++++++------ src/cuchaz/enigma/mapping/SignatureUpdater.java | 18 ++ test/cuchaz/enigma/TestDeobfuscator.java | 2 +- test/cuchaz/enigma/TestInnerClasses.java | 64 ++++++++ .../enigma/TestJarIndexConstructorReferences.java | 10 +- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 14 +- test/cuchaz/enigma/TestJarIndexLoneClass.java | 14 +- .../enigma/inputs/innerClasses/Anonymous.java | 17 ++ .../inputs/innerClasses/ConstructorArgs.java | 22 +++ test/cuchaz/enigma/inputs/innerClasses/Simple.java | 9 + 11 files changed, 306 insertions(+), 92 deletions(-) create mode 100644 test/cuchaz/enigma/TestInnerClasses.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/Anonymous.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/Simple.java diff --git a/build.gradle b/build.gradle index 767b0321..6b89d32e 100644 --- a/build.gradle +++ b/build.gradle @@ -90,21 +90,46 @@ task jarConstructors( type: Jar ) { archiveName( "testConstructors.jar" ) } -task obfTestCases( type: proguard.gradle.ProGuardTask ) { - dependsOn jarLoneClass, jarInheritanceTree, jarConstructors - +task jarInnerClasses( type: Jar ) { + from( sourceSets.test.output ) { + include( "cuchaz/enigma/inputs/Keep.class" ) + include( "cuchaz/enigma/inputs/innerClasses/**" ) + } + archiveName( "testInnerClasses.jar" ) +} + +tasks.withType( proguard.gradle.ProGuardTask ) { libraryjars( "${System.getProperty('java.home')}/lib/rt.jar" ) overloadaggressively repackageclasses allowaccessmodification dontoptimize dontshrink - keep( "class cuchaz.enigma.inputs.Keep" ) - - def jarNames = [ "LoneClass", "InheritanceTree", "Constructors" ]; - jarNames.each() { - injars( "build/libs/test${it}.jar" ) - outjars( "build/libs/test${it}.obf.jar" ) - } -} \ No newline at end of file +} + +task obfLoneClass( type: proguard.gradle.ProGuardTask, dependsOn: jarLoneClass ) { + def name = "LoneClass" + injars( "build/libs/test${name}.jar" ) + outjars( "build/libs/test${name}.obf.jar" ) +} + +task obfInheritanceTree( type: proguard.gradle.ProGuardTask, dependsOn: jarInheritanceTree ) { + def name = "InheritanceTree" + injars( "build/libs/test${name}.jar" ) + outjars( "build/libs/test${name}.obf.jar" ) +} + +task obfConstructors( type: proguard.gradle.ProGuardTask, dependsOn: jarConstructors ) { + def name = "Constructors" + injars( "build/libs/test${name}.jar" ) + outjars( "build/libs/test${name}.obf.jar" ) +} + +task obfInnerClasses( type: proguard.gradle.ProGuardTask, dependsOn: jarInnerClasses ) { + def name = "InnerClasses" + injars( "build/libs/test${name}.jar" ) + outjars( "build/libs/test${name}.obf.jar" ) +} + +task obfTestCases( dependsOn: tasks.withType( proguard.gradle.ProGuardTask ) ) diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 6c955bcd..43e2bf62 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -48,6 +48,7 @@ import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.SignatureUpdater; import cuchaz.enigma.mapping.Translator; public class JarIndex @@ -56,6 +57,7 @@ public class JarIndex private TranslationIndex m_translationIndex; private Multimap m_interfaces; private Map m_access; + private Map m_fieldClasses; private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; private Multimap> m_fieldReferences; @@ -70,6 +72,7 @@ public class JarIndex m_translationIndex = new TranslationIndex(); m_interfaces = HashMultimap.create(); m_access = Maps.newHashMap(); + m_fieldClasses = Maps.newHashMap(); m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); m_fieldReferences = HashMultimap.create(); @@ -127,7 +130,7 @@ public class JarIndex } for( CtField field : c.getDeclaredFields() ) { - m_translationIndex.addField( className, field.getName() ); + indexField( field ); } for( CtBehavior behavior : c.getDeclaredBehaviors() ) { @@ -193,6 +196,22 @@ public class JarIndex EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_fieldReferences ); } + private void indexField( CtField field ) + { + // get the field entry + String className = Descriptor.toJvmName( field.getDeclaringClass().getName() ); + FieldEntry fieldEntry = new FieldEntry( new ClassEntry( className ), field.getName() ); + + m_translationIndex.addField( className, field.getName() ); + + // is the field a class type? + if( field.getSignature().startsWith( "L" ) ) + { + ClassEntry fieldTypeEntry = new ClassEntry( field.getSignature().substring( 1, field.getSignature().length() - 1 ) ); + m_fieldClasses.put( fieldEntry, fieldTypeEntry ); + } + } + private void indexBehavior( CtBehavior behavior ) { // get the behavior entry @@ -412,7 +431,8 @@ public class JarIndex // use the synthetic fields to find the synthetic constructors for( CtConstructor constructor : c.getDeclaredConstructors() ) { - if( !isIllegalConstructor( constructor ) ) + Set syntheticFieldTypes = Sets.newHashSet(); + if( !isIllegalConstructor( syntheticFieldTypes, constructor ) ) { continue; } @@ -420,27 +440,57 @@ public class JarIndex ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getMethodInfo().getDescriptor() ); + // look at the synthetic types to get candidates for the outer class + Set candidateOuterClasses = Sets.newHashSet(); + for( String type : syntheticFieldTypes ) + { + if( type.startsWith( "L" ) ) + { + candidateOuterClasses.add( new ClassEntry( type.substring( 1, type.length() - 1 ) ) ); + } + } + + // do we have an answer yet? + if( candidateOuterClasses.isEmpty() ) + { + continue; + } + else if( candidateOuterClasses.size() == 1 ) + { + ClassEntry outerClassEntry = candidateOuterClasses.iterator().next(); + + // does this class make sense as an outer class? + if( !outerClassEntry.equals( classEntry ) ) + { + return outerClassEntry.getName(); + } + } + // who calls this constructor? Set callerClasses = Sets.newHashSet(); for( EntryReference reference : getBehaviorReferences( constructorEntry ) ) { - callerClasses.add( reference.context.getClassEntry() ); + // is that one of our candidates? + if( candidateOuterClasses.contains( reference.context.getClassEntry() ) ) + { + callerClasses.add( reference.context.getClassEntry() ); + } } - // is this called by exactly one class? + // do we have an answer yet? if( callerClasses.size() == 1 ) { - ClassEntry callerClassEntry = callerClasses.iterator().next(); + ClassEntry outerClassEntry = callerClasses.iterator().next(); // does this class make sense as an outer class? - if( !callerClassEntry.equals( classEntry ) ) + if( !outerClassEntry.equals( classEntry ) ) { - return callerClassEntry.getName(); + return outerClassEntry.getName(); } } - else if( callerClasses.size() > 1 ) + else { - System.out.println( "WARNING: Illegal constructor called by more than one class!" + callerClasses ); + System.out.println( "WARNING: Unable to choose outer class among options: " + candidateOuterClasses ); } } @@ -448,7 +498,7 @@ public class JarIndex } @SuppressWarnings( "unchecked" ) - private boolean isIllegalConstructor( CtConstructor constructor ) + private boolean isIllegalConstructor( Set syntheticFieldTypes, CtConstructor constructor ) { // illegal constructors only set synthetic member fields, then call super() String className = constructor.getDeclaringClass().getName(); @@ -456,7 +506,6 @@ public class JarIndex // collect all the field accesses, constructor calls, and method calls final List illegalFieldWrites = Lists.newArrayList(); final List constructorCalls = Lists.newArrayList(); - final List methodCalls = Lists.newArrayList(); try { constructor.instrument( new ExprEditor( ) @@ -475,12 +524,6 @@ public class JarIndex { constructorCalls.add( constructorCall ); } - - @Override - public void edit( MethodCall methodCall ) - { - methodCalls.add( methodCall ); - } } ); } catch( CannotCompileException ex ) @@ -489,25 +532,6 @@ public class JarIndex throw new Error( ex ); } - // method calls are not allowed - if( !methodCalls.isEmpty() ) - { - return false; - } - - // is there only one constructor call? - if( constructorCalls.size() != 1 ) - { - return false; - } - - // is the call to super? - ConstructorCall constructorCall = constructorCalls.get( 0 ); - if( !constructorCall.getMethodName().equals( "super" ) ) - { - return false; - } - // are there any illegal field writes? if( illegalFieldWrites.isEmpty() ) { @@ -528,7 +552,7 @@ public class JarIndex FieldInfo fieldInfo = null; for( FieldInfo info : (List)constructor.getDeclaringClass().getClassFile().getFields() ) { - if( info.getName().equals( fieldWrite.getFieldName() ) ) + if( info.getName().equals( fieldWrite.getFieldName() ) && info.getDescriptor().equals( fieldWrite.getSignature() ) ) { fieldInfo = info; break; @@ -542,9 +566,13 @@ public class JarIndex // is this field synthetic? boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; - if( !isSynthetic ) + if( isSynthetic ) + { + syntheticFieldTypes.add( fieldInfo.getDescriptor() ); + } + else { - System.err.println( String.format( "WARNING: illegal write to non synthetic field %s.%s", className, fieldInfo.getName() ) ); + System.err.println( String.format( "WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName() ) ); return false; } } @@ -555,15 +583,15 @@ public class JarIndex private boolean isAnonymousClass( CtClass c, String outerClassName ) { - String innerClassName = Descriptor.toJvmName( c.getName() ); + ClassEntry innerClassEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); // anonymous classes: // can't be abstract // have only one constructor // it's called exactly once by the outer class - // type of inner class not referenced anywhere in outer class + // the type the instance is assigned to can't be this type - // is absract? + // is abstract? if( Modifier.isAbstract( c.getModifiers() ) ) { return false; @@ -577,22 +605,40 @@ public class JarIndex CtConstructor constructor = c.getDeclaredConstructors()[0]; // is this constructor called exactly once? - ConstructorEntry constructorEntry = new ConstructorEntry( - new ClassEntry( innerClassName ), - constructor.getMethodInfo().getDescriptor() - ); - if( getBehaviorReferences( constructorEntry ).size() != 1 ) + ConstructorEntry constructorEntry = new ConstructorEntry( innerClassEntry, constructor.getMethodInfo().getDescriptor() ); + Collection> references = getBehaviorReferences( constructorEntry ); + if( references.size() != 1 ) { return false; } - // TODO: check outer class doesn't reference type - // except this is hard because we can't just load the outer class now - // we'd have to pre-index those references in the JarIndex + // does the caller use this type? + BehaviorEntry caller = references.iterator().next().context; + for( FieldEntry fieldEntry : getReferencedFields( caller ) ) + { + ClassEntry fieldClass = getFieldClass( fieldEntry ); + if( fieldClass != null && fieldClass.equals( innerClassEntry ) ) + { + // caller references this type, so it can't be anonymous + return false; + } + } + for( BehaviorEntry behaviorEntry : getReferencedBehaviors( caller ) ) + { + // get the class types from the signature + for( String className : SignatureUpdater.getClasses( behaviorEntry.getSignature() ) ) + { + if( className.equals( innerClassEntry.getName() ) ) + { + // caller references this type, so it can't be anonymous + return false; + } + } + } return true; } - + public Set getObfClassEntries( ) { return m_obfClassEntries; @@ -608,6 +654,11 @@ public class JarIndex return m_access.get( entry ); } + public ClassEntry getFieldClass( FieldEntry fieldEntry ) + { + return m_fieldClasses.get( fieldEntry ); + } + public boolean isMethodImplemented( MethodEntry methodEntry ) { Collection implementations = m_methodImplementations.get( methodEntry.getClassName() ); @@ -772,11 +823,39 @@ public class JarIndex return m_fieldReferences.get( fieldEntry ); } + public Collection getReferencedFields( BehaviorEntry behaviorEntry ) + { + // linear search is fast enough for now + Set fieldEntries = Sets.newHashSet(); + for( EntryReference reference : m_fieldReferences.values() ) + { + if( reference.context == behaviorEntry ) + { + fieldEntries.add( reference.entry ); + } + } + return fieldEntries; + } + public Collection> getBehaviorReferences( BehaviorEntry behaviorEntry ) { return m_behaviorReferences.get( behaviorEntry ); } + public Collection getReferencedBehaviors( BehaviorEntry behaviorEntry ) + { + // linear search is fast enough for now + Set behaviorEntries = Sets.newHashSet(); + for( EntryReference reference : m_behaviorReferences.values() ) + { + if( reference.context == behaviorEntry ) + { + behaviorEntries.add( reference.entry ); + } + } + return behaviorEntries; + } + public Collection getInnerClasses( String obfOuterClassName ) { return m_innerClasses.get( obfOuterClassName ); diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java index 4c0dbac1..528a7437 100644 --- a/src/cuchaz/enigma/mapping/SignatureUpdater.java +++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java @@ -12,6 +12,9 @@ package cuchaz.enigma.mapping; import java.io.IOException; import java.io.StringReader; +import java.util.List; + +import com.beust.jcommander.internal.Lists; public class SignatureUpdater { @@ -84,4 +87,19 @@ public class SignatureUpdater return null; } + + public static List getClasses( String signature ) + { + final List classNames = Lists.newArrayList(); + update( signature, new ClassNameUpdater( ) + { + @Override + public String update( String className ) + { + classNames.add( className ); + return className; + } + } ); + return classNames; + } } diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java index 3bb0c480..71de24ae 100644 --- a/test/cuchaz/enigma/TestDeobfuscator.java +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -11,7 +11,7 @@ ******************************************************************************/ package cuchaz.enigma; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.io.File; import java.io.IOException; diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java new file mode 100644 index 00000000..c6b1b5fb --- /dev/null +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.JarIndex; + +public class TestInnerClasses +{ + private JarIndex m_index; + + private static final String AnonymousOuter = "none/a"; + private static final String AnonymousInner = "none/b"; + private static final String SimpleOuter = "none/e"; + private static final String SimpleInner = "none/f"; + private static final String ConstructorArgsOuter = "none/c"; + private static final String ConstructorArgsInner = "none/d"; + + public TestInnerClasses( ) + throws Exception + { + m_index = new JarIndex(); + m_index.indexJar( new JarFile( "build/libs/testInnerClasses.obf.jar" ), true ); + } + + @Test + public void simple( ) + { + assertThat( m_index.getOuterClass( SimpleInner ), is( SimpleOuter ) ); + assertThat( m_index.getInnerClasses( SimpleOuter ), containsInAnyOrder( SimpleInner ) ); + assertThat( m_index.isAnonymousClass( SimpleInner ), is( false ) ); + } + + @Test + public void anonymous( ) + { + assertThat( m_index.getOuterClass( AnonymousInner ), is( AnonymousOuter ) ); + assertThat( m_index.getInnerClasses( AnonymousOuter ), containsInAnyOrder( AnonymousInner ) ); + assertThat( m_index.isAnonymousClass( AnonymousInner ), is( true ) ); + } + + @Test + public void constructorArgs( ) + { + assertThat( m_index.getOuterClass( ConstructorArgsInner ), is( ConstructorArgsOuter ) ); + assertThat( m_index.getInnerClasses( ConstructorArgsOuter ), containsInAnyOrder( ConstructorArgsInner ) ); + assertThat( m_index.isAnonymousClass( ConstructorArgsInner ), is( false ) ); + } +} diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java index c0691888..02381718 100644 --- a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -10,13 +10,9 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByConstructor; -import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByMethod; -import static cuchaz.enigma.EntryFactory.newClass; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.is; +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; import java.io.File; import java.util.Collection; diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 3d488ee4..317a6bc4 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -10,17 +10,9 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByConstructor; -import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByMethod; -import static cuchaz.enigma.EntryFactory.newClass; -import static cuchaz.enigma.EntryFactory.newFieldReferenceByConstructor; -import static cuchaz.enigma.EntryFactory.newFieldReferenceByMethod; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; import java.util.Collection; import java.util.Set; diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index 9236d0c1..4c32b703 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -11,17 +11,9 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.newClass; -import static cuchaz.enigma.EntryFactory.newField; -import static cuchaz.enigma.EntryFactory.newFieldReferenceByConstructor; -import static cuchaz.enigma.EntryFactory.newFieldReferenceByMethod; -import static cuchaz.enigma.EntryFactory.newMethod; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; import java.util.Collection; import java.util.Set; diff --git a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java new file mode 100644 index 00000000..dbff5238 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java @@ -0,0 +1,17 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class Anonymous // a +{ + public void foo( ) + { + Runnable runnable = new Runnable( ) // b + { + @Override + public void run( ) + { + // don't care + } + }; + runnable.run(); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java new file mode 100644 index 00000000..d12d9cf7 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java @@ -0,0 +1,22 @@ +package cuchaz.enigma.inputs.innerClasses; + +@SuppressWarnings( "unused" ) +public class ConstructorArgs // c +{ + class Inner // d + { + private int a; + + public Inner( int a ) + { + this.a = a; + } + } + + Inner i; + + public void foo( ) + { + i = new Inner( 5 ); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/Simple.java b/test/cuchaz/enigma/inputs/innerClasses/Simple.java new file mode 100644 index 00000000..f2c08a73 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/Simple.java @@ -0,0 +1,9 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class Simple // e +{ + class Inner // f + { + // nothing to do + } +} -- cgit v1.2.3 From df06f4ddde5e255750edc4087cfba54823404909 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 21 Sep 2014 22:08:05 -0400 Subject: improved inner/anonymous class detection --- src/cuchaz/enigma/analysis/JarIndex.java | 95 +++++++++++++++------- test/cuchaz/enigma/TestInnerClasses.java | 18 +++- .../enigma/inputs/innerClasses/Anonymous.java | 4 +- .../innerClasses/AnonymousWithScopeArgs.java | 16 ++++ .../inputs/innerClasses/ConstructorArgs.java | 4 +- test/cuchaz/enigma/inputs/innerClasses/Simple.java | 4 +- 6 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 43e2bf62..c36e9cb0 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -440,29 +440,17 @@ public class JarIndex ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getMethodInfo().getDescriptor() ); - // look at the synthetic types to get candidates for the outer class - Set candidateOuterClasses = Sets.newHashSet(); + // gather the classes from the illegally-set synthetic fields + Set illegallySetClasses = Sets.newHashSet(); for( String type : syntheticFieldTypes ) { if( type.startsWith( "L" ) ) { - candidateOuterClasses.add( new ClassEntry( type.substring( 1, type.length() - 1 ) ) ); - } - } - - // do we have an answer yet? - if( candidateOuterClasses.isEmpty() ) - { - continue; - } - else if( candidateOuterClasses.size() == 1 ) - { - ClassEntry outerClassEntry = candidateOuterClasses.iterator().next(); - - // does this class make sense as an outer class? - if( !outerClassEntry.equals( classEntry ) ) - { - return outerClassEntry.getName(); + ClassEntry outerClassEntry = new ClassEntry( type.substring( 1, type.length() - 1 ) ); + if( isSaneOuterClass( outerClassEntry, classEntry ) ) + { + illegallySetClasses.add( outerClassEntry ); + } } } @@ -470,33 +458,82 @@ public class JarIndex Set callerClasses = Sets.newHashSet(); for( EntryReference reference : getBehaviorReferences( constructorEntry ) ) { - // is that one of our candidates? - if( candidateOuterClasses.contains( reference.context.getClassEntry() ) ) + // make sure it's not a call to super + if( reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry ) + { + // is the entry a superclass of the context? + String calledClassName = reference.entry.getClassName(); + String callerSuperclassName = m_translationIndex.getSuperclassName( reference.context.getClassName() ); + if( callerSuperclassName != null && callerSuperclassName.equals( calledClassName ) ) + { + // it's a super call, skip + continue; + } + } + + if( isSaneOuterClass( reference.context.getClassEntry(), classEntry ) ) { - callerClasses.add( reference.context.getClassEntry() ); + callerClasses.add( reference.context.getClassEntry() ); } } // do we have an answer yet? - if( callerClasses.size() == 1 ) + if( callerClasses.isEmpty() ) { - ClassEntry outerClassEntry = callerClasses.iterator().next(); - - // does this class make sense as an outer class? - if( !outerClassEntry.equals( classEntry ) ) + if( illegallySetClasses.size() == 1 ) { - return outerClassEntry.getName(); + return illegallySetClasses.iterator().next().getName(); + } + else + { + System.out.println( String.format( "WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry ) ); } } else { - System.out.println( "WARNING: Unable to choose outer class among options: " + candidateOuterClasses ); + if( callerClasses.size() == 1 ) + { + return callerClasses.iterator().next().getName(); + } + else + { + // multiple callers, do the illegally set classes narrow it down? + Set intersection = Sets.newHashSet( callerClasses ); + intersection.retainAll( illegallySetClasses ); + if( intersection.size() == 1 ) + { + return intersection.iterator().next().getName(); + } + else + { + System.out.println( String.format( "WARNING: Unable to choose outer class for %s among options: %s", + classEntry, callerClasses + ) ); + } + } } } return null; } + private boolean isSaneOuterClass( ClassEntry outerClassEntry, ClassEntry innerClassEntry ) + { + // clearly this would be silly + if( outerClassEntry.equals( innerClassEntry ) ) + { + return false; + } + + // is the outer class in the jar? + if( !m_obfClassEntries.contains( outerClassEntry ) ) + { + return false; + } + + return true; + } + @SuppressWarnings( "unchecked" ) private boolean isIllegalConstructor( Set syntheticFieldTypes, CtConstructor constructor ) { diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java index c6b1b5fb..e555d920 100644 --- a/test/cuchaz/enigma/TestInnerClasses.java +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -26,10 +26,12 @@ public class TestInnerClasses private static final String AnonymousOuter = "none/a"; private static final String AnonymousInner = "none/b"; - private static final String SimpleOuter = "none/e"; - private static final String SimpleInner = "none/f"; - private static final String ConstructorArgsOuter = "none/c"; - private static final String ConstructorArgsInner = "none/d"; + private static final String SimpleOuter = "none/g"; + private static final String SimpleInner = "none/h"; + private static final String ConstructorArgsOuter = "none/e"; + private static final String ConstructorArgsInner = "none/f"; + private static final String AnonymousWithScopeArgsOuter = "none/c"; + private static final String AnonymousWithScopeArgsInner = "none/d"; public TestInnerClasses( ) throws Exception @@ -61,4 +63,12 @@ public class TestInnerClasses assertThat( m_index.getInnerClasses( ConstructorArgsOuter ), containsInAnyOrder( ConstructorArgsInner ) ); assertThat( m_index.isAnonymousClass( ConstructorArgsInner ), is( false ) ); } + + @Test + public void anonymousWithScopeArgs( ) + { + assertThat( m_index.getOuterClass( AnonymousWithScopeArgsInner ), is( AnonymousWithScopeArgsOuter ) ); + assertThat( m_index.getInnerClasses( AnonymousWithScopeArgsOuter ), containsInAnyOrder( AnonymousWithScopeArgsInner ) ); + assertThat( m_index.isAnonymousClass( AnonymousWithScopeArgsInner ), is( true ) ); + } } diff --git a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java index dbff5238..d36a514c 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java +++ b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java @@ -1,10 +1,10 @@ package cuchaz.enigma.inputs.innerClasses; -public class Anonymous // a +public class Anonymous { public void foo( ) { - Runnable runnable = new Runnable( ) // b + Runnable runnable = new Runnable( ) { @Override public void run( ) diff --git a/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java b/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java new file mode 100644 index 00000000..e0a65e25 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java @@ -0,0 +1,16 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class AnonymousWithScopeArgs +{ + public static void foo( final Simple arg ) + { + System.out.println( new Object( ) + { + @Override + public String toString( ) + { + return arg.toString(); + } + } ); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java index d12d9cf7..e24395c7 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java +++ b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java @@ -1,9 +1,9 @@ package cuchaz.enigma.inputs.innerClasses; @SuppressWarnings( "unused" ) -public class ConstructorArgs // c +public class ConstructorArgs { - class Inner // d + class Inner { private int a; diff --git a/test/cuchaz/enigma/inputs/innerClasses/Simple.java b/test/cuchaz/enigma/inputs/innerClasses/Simple.java index f2c08a73..405c6399 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/Simple.java +++ b/test/cuchaz/enigma/inputs/innerClasses/Simple.java @@ -1,8 +1,8 @@ package cuchaz.enigma.inputs.innerClasses; -public class Simple // e +public class Simple { - class Inner // f + class Inner { // nothing to do } -- cgit v1.2.3 From 24ed3dc06bc133e4f718acc4a691e905b081fb11 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 21 Sep 2014 23:21:34 -0400 Subject: fixed bugs with anonymous/inner classes --- src/cuchaz/enigma/Deobfuscator.java | 16 ++--------- src/cuchaz/enigma/analysis/JarIndex.java | 31 +++++++++++++--------- .../analysis/SourceIndexBehaviorVisitor.java | 17 +++++++----- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 14 ++++++++++ src/cuchaz/enigma/mapping/SignatureUpdater.java | 2 +- 5 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 1d6f02c9..9c845325 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -232,7 +232,7 @@ public class Deobfuscator sourceTree.acceptVisitor( new SourceIndexVisitor(), index ); // DEBUG - //root.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); + //sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); /* DEBUG for( Token token : index.referenceTokens() ) @@ -420,19 +420,7 @@ public class Deobfuscator { if( obfEntry instanceof ClassEntry ) { - ClassEntry obfClassEntry = (ClassEntry)obfEntry; - if( obfClassEntry.isInnerClass() ) - { - // both classes must be in the list - return m_jarIndex.getObfClassEntries().contains( obfClassEntry.getOuterClassEntry() ) - && m_jarIndex.getObfClassEntries().contains( obfClassEntry.getInnerClassName() ); - // TODO: make sure this works for the inner class!! - } - else - { - // class must be in the list - return m_jarIndex.getObfClassEntries().contains( obfEntry ); - } + return m_jarIndex.getObfClassEntries().contains( obfEntry ); } else { diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index c36e9cb0..8ebce35e 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -63,7 +63,7 @@ public class JarIndex private Multimap> m_fieldReferences; private Multimap m_innerClasses; private Map m_outerClasses; - private Set m_anonymousClasses; + private Map m_anonymousClasses; private Map m_bridgeMethods; public JarIndex( ) @@ -78,7 +78,7 @@ public class JarIndex m_fieldReferences = HashMultimap.create(); m_innerClasses = HashMultimap.create(); m_outerClasses = Maps.newHashMap(); - m_anonymousClasses = Sets.newHashSet(); + m_anonymousClasses = Maps.newHashMap(); m_bridgeMethods = Maps.newHashMap(); } @@ -161,9 +161,10 @@ public class JarIndex m_innerClasses.put( outerClassName, innerClassName ); m_outerClasses.put( innerClassName, outerClassName ); - if( isAnonymousClass( c, outerClassName ) ) + BehaviorEntry enclosingBehavior = isAnonymousClass( c, outerClassName ); + if( enclosingBehavior != null ) { - m_anonymousClasses.add( innerClassName ); + m_anonymousClasses.put( innerClassName, enclosingBehavior ); // DEBUG //System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); @@ -188,6 +189,7 @@ public class JarIndex EntryRenamer.renameClassesInMultimap( renames, m_methodImplementations ); EntryRenamer.renameClassesInMultimap( renames, m_behaviorReferences ); EntryRenamer.renameClassesInMultimap( renames, m_fieldReferences ); + EntryRenamer.renameClassesInMap( renames, m_bridgeMethods ); } // step 6: update other indices with bridge method info @@ -618,7 +620,7 @@ public class JarIndex return true; } - private boolean isAnonymousClass( CtClass c, String outerClassName ) + private BehaviorEntry isAnonymousClass( CtClass c, String outerClassName ) { ClassEntry innerClassEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); @@ -631,13 +633,13 @@ public class JarIndex // is abstract? if( Modifier.isAbstract( c.getModifiers() ) ) { - return false; + return null; } // is there exactly one constructor? if( c.getDeclaredConstructors().length != 1 ) { - return false; + return null; } CtConstructor constructor = c.getDeclaredConstructors()[0]; @@ -646,7 +648,7 @@ public class JarIndex Collection> references = getBehaviorReferences( constructorEntry ); if( references.size() != 1 ) { - return false; + return null; } // does the caller use this type? @@ -657,7 +659,7 @@ public class JarIndex if( fieldClass != null && fieldClass.equals( innerClassEntry ) ) { // caller references this type, so it can't be anonymous - return false; + return null; } } for( BehaviorEntry behaviorEntry : getReferencedBehaviors( caller ) ) @@ -668,12 +670,12 @@ public class JarIndex if( className.equals( innerClassEntry.getName() ) ) { // caller references this type, so it can't be anonymous - return false; + return null; } } } - return true; + return caller; } public Set getObfClassEntries( ) @@ -905,7 +907,12 @@ public class JarIndex public boolean isAnonymousClass( String obfInnerClassName ) { - return m_anonymousClasses.contains( obfInnerClassName ); + return m_anonymousClasses.containsKey( obfInnerClassName ); + } + + public BehaviorEntry getAnonymousClassCaller( String obfInnerClassName ) + { + return m_anonymousClasses.get( obfInnerClassName ); } public Set getInterfaces( String className ) diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index a1dac4b5..6238b1e7 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -177,14 +177,17 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor public Void visitObjectCreationExpression( ObjectCreationExpression node, SourceIndex index ) { MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); - ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); - ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, ref.getSignature() ); - if( node.getType() instanceof SimpleType ) + if( ref != null ) { - index.addReference( - ((SimpleType)node.getType()).getIdentifierToken(), - new EntryReference( constructorEntry, m_behaviorEntry ) - ); + ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); + ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, ref.getSignature() ); + if( node.getType() instanceof SimpleType ) + { + index.addReference( + ((SimpleType)node.getType()).getIdentifierToken(), + new EntryReference( constructorEntry, m_behaviorEntry ) + ); + } } return recurse( node, index ); diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 5044e065..f94a85d7 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -16,8 +16,10 @@ import javassist.CtClass; import javassist.bytecode.AccessFlag; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; +import javassist.bytecode.EnclosingMethodAttribute; import javassist.bytecode.InnerClassesAttribute; import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; public class InnerClassWriter @@ -44,6 +46,18 @@ public class InnerClassWriter // this is an inner class, rename it to outer$inner ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + new ClassEntry( obfClassName ).getSimpleName() ); c.setName( obfClassEntry.getName() ); + + BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller( obfClassName ); + if( caller != null ) + { + // write the enclosing method attribute + c.getClassFile().addAttribute( new EnclosingMethodAttribute( + c.getClassFile().getConstPool(), + caller.getClassName(), + caller.getName(), + caller.getSignature() + ) ); + } } // write the inner classes if needed diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java index 528a7437..d1216bde 100644 --- a/src/cuchaz/enigma/mapping/SignatureUpdater.java +++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java @@ -14,7 +14,7 @@ import java.io.IOException; import java.io.StringReader; import java.util.List; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; public class SignatureUpdater { -- cgit v1.2.3 From 89c287f39f432385febf97779f51c45a6c3eb51b Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 22 Sep 2014 23:35:02 -0400 Subject: fix bug with anonymous classes in class initializers --- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index f94a85d7..a0617925 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -51,12 +51,22 @@ public class InnerClassWriter if( caller != null ) { // write the enclosing method attribute - c.getClassFile().addAttribute( new EnclosingMethodAttribute( - c.getClassFile().getConstPool(), - caller.getClassName(), - caller.getName(), - caller.getSignature() - ) ); + if( caller.getName().equals( "" ) ) + { + c.getClassFile().addAttribute( new EnclosingMethodAttribute( + c.getClassFile().getConstPool(), + caller.getClassName() + ) ); + } + else + { + c.getClassFile().addAttribute( new EnclosingMethodAttribute( + c.getClassFile().getConstPool(), + caller.getClassName(), + caller.getName(), + caller.getSignature() + ) ); + } } } -- cgit v1.2.3 -- cgit v1.2.3 From a09a23871abaf2f0c8c1636ee6dd2f9eaf2474b0 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 23 Sep 2014 01:01:42 -0400 Subject: trying to figure out why some mappings to correspond to anything in the jar file... --- src/cuchaz/enigma/Deobfuscator.java | 102 ++++++++++++++++++++++----- src/cuchaz/enigma/analysis/EntryRenamer.java | 4 -- src/cuchaz/enigma/analysis/JarIndex.java | 14 ++-- src/cuchaz/enigma/mapping/ClassMapping.java | 22 ++++++ 4 files changed, 117 insertions(+), 25 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 9c845325..03c35113 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -22,6 +22,7 @@ import java.util.jar.JarFile; import javassist.bytecode.Descriptor; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.strobel.assembler.metadata.MetadataSystem; @@ -44,6 +45,7 @@ import cuchaz.enigma.mapping.ClassMapping; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.FieldMapping; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsRenamer; import cuchaz.enigma.mapping.MethodEntry; @@ -113,41 +115,107 @@ public class Deobfuscator val = new Mappings(); } - // make sure all the mappings match the classes in the jar + // look for any classes that got moved to inner classes + Map renames = Maps.newHashMap(); for( ClassMapping classMapping : val.classes() ) { - ClassEntry classEntry = new ClassEntry( classMapping.getObfName() ); - if( !m_jarIndex.getObfClassEntries().contains( classEntry ) ) + String outerClassName = m_jarIndex.getOuterClass( classMapping.getObfName() ); + if( outerClassName != null ) { - throw new Error( "Class " + classEntry + " not found in Jar!" ); + // build the composite class name + String newName = outerClassName + "$" + new ClassEntry( classMapping.getObfName() ).getSimpleName(); + + // add a rename + renames.put( classMapping.getObfName(), newName ); + + System.out.println( String.format( "Converted class mapping %s to %s", classMapping.getObfName(), newName ) ); } - - // and method implementations - for( MethodMapping methodMapping : classMapping.methods() ) + } + for( Map.Entry entry : renames.entrySet() ) + { + val.renameObfClass( entry.getKey(), entry.getValue() ); + } + + // drop mappings that don't match the jar + List unknownClasses = Lists.newArrayList(); + for( ClassMapping classMapping : val.classes() ) + { + checkClassMapping( unknownClasses, classMapping ); + } + if( !unknownClasses.isEmpty() ) + { + throw new Error( "Unable to find classes in jar: " + unknownClasses ); + } + + m_mappings = val; + m_renamer = new MappingsRenamer( m_jarIndex, m_mappings ); + m_translatorCache.clear(); + } + + private void checkClassMapping( List unknownClasses, ClassMapping classMapping ) + { + // check the class + ClassEntry classEntry = new ClassEntry( classMapping.getObfName() ); + String outerClassName = m_jarIndex.getOuterClass( classMapping.getObfName() ); + if( outerClassName != null ) + { + classEntry = new ClassEntry( outerClassName + "$" + classEntry.getSimpleName() ); + } + if( !m_jarIndex.getObfClassEntries().contains( classEntry ) ) + { + unknownClasses.add( classEntry ); + } + + // check the fields + for( FieldMapping fieldMapping : Lists.newArrayList( classMapping.fields() ) ) + { + FieldEntry fieldEntry = new FieldEntry( classEntry, fieldMapping.getObfName() ); + if( m_jarIndex.getAccess( fieldEntry ) == null ) + { + System.err.println( "WARNING: unable to find field " + fieldEntry + ". dropping mapping." ); + classMapping.removeFieldMapping( fieldMapping ); + } + } + + // check methods + for( MethodMapping methodMapping : Lists.newArrayList( classMapping.methods() ) ) + { + if( methodMapping.getObfName().equals( "" ) ) + { + // skip static initializers + continue; + } + else if( methodMapping.getObfName().equals( "" ) ) { - if( methodMapping.getObfName().startsWith( "<" ) ) + ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, methodMapping.getObfSignature() ); + if( m_jarIndex.getAccess( constructorEntry ) == null ) { - // skip constructors and static initializers - continue; + System.err.println( "WARNING: unable to find constructor " + constructorEntry + ". dropping mapping." ); + classMapping.removeMethodMapping( methodMapping ); } - + } + else + { MethodEntry methodEntry = new MethodEntry( classEntry, methodMapping.getObfName(), methodMapping.getObfSignature() ); - if( !m_jarIndex.isMethodImplemented( methodEntry ) ) + if( m_jarIndex.getAccess( methodEntry ) == null ) { - throw new Error( "Method " + methodEntry + " not found in Jar!" ); + System.err.println( "WARNING: unable to find method " + methodEntry + ". dropping mapping." ); + classMapping.removeMethodMapping( methodMapping ); } } } - m_mappings = val; - m_renamer = new MappingsRenamer( m_jarIndex, m_mappings ); - m_translatorCache.clear(); + // check inner classes + for( ClassMapping innerClassMapping : classMapping.innerClasses() ) + { + checkClassMapping( unknownClasses, innerClassMapping ); + } } - + public Translator getTranslator( TranslationDirection direction ) { Translator translator = m_translatorCache.get( direction ); diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java index e9483caa..b82b2547 100644 --- a/src/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -189,10 +189,6 @@ public class EntryRenamer reference.context = renameClassesInThing( renames, reference.context ); return thing; } - else - { - throw new Error( "Not an entry: " + thing ); - } return thing; } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 8ebce35e..b51428a2 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -95,7 +95,7 @@ public class JarIndex m_obfClassEntries.add( classEntry ); } - // step 2: index method/field access + // step 2: index field/method/constructor access for( CtClass c : JarClassIterator.classes( jar ) ) { ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); @@ -105,10 +105,15 @@ public class JarIndex FieldEntry fieldEntry = new FieldEntry( classEntry, field.getName() ); m_access.put( fieldEntry, Access.get( field ) ); } - for( CtBehavior behavior : c.getDeclaredBehaviors() ) + for( CtMethod method : c.getDeclaredMethods() ) + { + MethodEntry methodEntry = new MethodEntry( classEntry, method.getName(), method.getSignature() ); + m_access.put( methodEntry, Access.get( method ) ); + } + for( CtConstructor constructor : c.getDeclaredConstructors() ) { - MethodEntry methodEntry = new MethodEntry( classEntry, behavior.getName(), behavior.getSignature() ); - m_access.put( methodEntry, Access.get( behavior ) ); + ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getSignature() ); + m_access.put( constructorEntry, Access.get( constructor ) ); } } @@ -190,6 +195,7 @@ public class JarIndex EntryRenamer.renameClassesInMultimap( renames, m_behaviorReferences ); EntryRenamer.renameClassesInMultimap( renames, m_fieldReferences ); EntryRenamer.renameClassesInMap( renames, m_bridgeMethods ); + EntryRenamer.renameClassesInMap( renames, m_access ); } // step 6: update other indices with bridge method info diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 200d9ca2..88006cff 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -155,6 +155,17 @@ public class ClassMapping implements Serializable, Comparable assert( deobfWasAdded ); assert( m_fieldsByObf.size() == m_fieldsByDeobf.size() ); } + + public void removeFieldMapping( FieldMapping fieldMapping ) + { + boolean obfWasRemoved = m_fieldsByObf.remove( fieldMapping.getObfName() ) != null; + assert( obfWasRemoved ); + if( fieldMapping.getDeobfName() != null ) + { + boolean deobfWasRemoved = m_fieldsByDeobf.remove( fieldMapping.getDeobfName() ) != null; + assert( deobfWasRemoved ); + } + } public String getObfFieldName( String deobfName ) { @@ -225,6 +236,17 @@ public class ClassMapping implements Serializable, Comparable assert( m_methodsByObf.size() >= m_methodsByDeobf.size() ); } + public void removeMethodMapping( MethodMapping methodMapping ) + { + boolean obfWasRemoved = m_methodsByObf.remove( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ) ) != null; + assert( obfWasRemoved ); + if( methodMapping.getDeobfName() != null ) + { + boolean deobfWasRemoved = m_methodsByDeobf.remove( getMethodKey( methodMapping.getDeobfName(), methodMapping.getObfSignature() ) ) != null; + assert( deobfWasRemoved ); + } + } + public MethodMapping getMethodByObf( String obfName, String signature ) { return m_methodsByObf.get( getMethodKey( obfName, signature ) ); -- cgit v1.2.3 From 8776a8ba38123c822530e5f659c626c8db616217 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 24 Sep 2014 01:00:54 -0400 Subject: HOW DO I WRITE SO MANY BUGS?!? --- src/cuchaz/enigma/Deobfuscator.java | 102 ++++++++++++++++----- src/cuchaz/enigma/analysis/EntryRenamer.java | 4 +- src/cuchaz/enigma/analysis/JarIndex.java | 102 +++++++++++++++------ .../analysis/MethodImplementationsTreeNode.java | 2 +- .../enigma/analysis/MethodInheritanceTreeNode.java | 2 +- src/cuchaz/enigma/analysis/SourceIndex.java | 9 ++ .../analysis/SourceIndexBehaviorVisitor.java | 18 +++- .../enigma/bytecode/MethodParameterWriter.java | 22 ++++- src/cuchaz/enigma/convert/ClassMatcher.java | 6 +- src/cuchaz/enigma/gui/Gui.java | 16 ++-- src/cuchaz/enigma/gui/GuiController.java | 3 +- src/cuchaz/enigma/mapping/ArgumentEntry.java | 43 ++++++--- src/cuchaz/enigma/mapping/ClassEntry.java | 18 ++-- src/cuchaz/enigma/mapping/ClassMapping.java | 34 ++++--- src/cuchaz/enigma/mapping/ConstructorEntry.java | 12 +++ src/cuchaz/enigma/mapping/Entry.java | 1 + src/cuchaz/enigma/mapping/FieldEntry.java | 6 ++ src/cuchaz/enigma/mapping/Mappings.java | 6 +- src/cuchaz/enigma/mapping/MappingsRenamer.java | 32 ++++++- src/cuchaz/enigma/mapping/MethodEntry.java | 6 ++ src/cuchaz/enigma/mapping/Translator.java | 2 +- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 46 ++++------ test/cuchaz/enigma/TestJarIndexLoneClass.java | 13 +-- test/cuchaz/enigma/TestSourceIndex.java | 56 +++++++++++ 24 files changed, 412 insertions(+), 149 deletions(-) create mode 100644 test/cuchaz/enigma/TestSourceIndex.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 03c35113..f7f74480 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -39,6 +39,7 @@ import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; +import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; @@ -115,7 +116,7 @@ public class Deobfuscator val = new Mappings(); } - // look for any classes that got moved to inner classes + // pass 1: look for any classes that got moved to inner classes Map renames = Maps.newHashMap(); for( ClassMapping classMapping : val.classes() ) { @@ -136,6 +137,53 @@ public class Deobfuscator val.renameObfClass( entry.getKey(), entry.getValue() ); } + // pass 2: look for fields/methods that are actually declared in superclasses + MappingsRenamer renamer = new MappingsRenamer( m_jarIndex, val ); + for( ClassMapping classMapping : val.classes() ) + { + ClassEntry obfClassEntry = new ClassEntry( classMapping.getObfName() ); + + // fields + for( FieldMapping fieldMapping : Lists.newArrayList( classMapping.fields() ) ) + { + FieldEntry fieldEntry = new FieldEntry( obfClassEntry, fieldMapping.getObfName() ); + ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass( fieldEntry ); + if( resolvedObfClassEntry != null && !resolvedObfClassEntry.equals( fieldEntry.getClassEntry() ) ) + { + boolean wasMoved = renamer.moveFieldToObfClass( classMapping, fieldMapping, resolvedObfClassEntry ); + if( wasMoved ) + { + System.out.println( String.format( "Moved field %s to class %s", fieldEntry, resolvedObfClassEntry ) ); + } + else + { + System.err.println( String.format( "WARNING: Would move field %s to class %s but the field was already there. Dropping instead.", fieldEntry, resolvedObfClassEntry ) ); + } + } + } + + // methods + for( MethodMapping methodMapping : Lists.newArrayList( classMapping.methods() ) ) + { + MethodEntry methodEntry = new MethodEntry( obfClassEntry, methodMapping.getObfName(), methodMapping.getObfSignature() ); + ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass( methodEntry ); + if( resolvedObfClassEntry != null && !resolvedObfClassEntry.equals( methodEntry.getClassEntry() ) ) + { + boolean wasMoved = renamer.moveMethodToObfClass( classMapping, methodMapping, resolvedObfClassEntry ); + if( wasMoved ) + { + System.out.println( String.format( "Moved method %s to class %s", methodEntry, resolvedObfClassEntry ) ); + } + else + { + System.err.println( String.format( "WARNING: Would move method %s to class %s but the method was already there. Dropping instead.", methodEntry, resolvedObfClassEntry ) ); + } + } + } + + // TODO: recurse to inner classes? + } + // drop mappings that don't match the jar List unknownClasses = Lists.newArrayList(); for( ClassMapping classMapping : val.classes() ) @@ -148,7 +196,7 @@ public class Deobfuscator } m_mappings = val; - m_renamer = new MappingsRenamer( m_jarIndex, m_mappings ); + m_renamer = renamer; m_translatorCache.clear(); } @@ -170,7 +218,7 @@ public class Deobfuscator for( FieldMapping fieldMapping : Lists.newArrayList( classMapping.fields() ) ) { FieldEntry fieldEntry = new FieldEntry( classEntry, fieldMapping.getObfName() ); - if( m_jarIndex.getAccess( fieldEntry ) == null ) + if( !m_jarIndex.containsObfField( fieldEntry ) ) { System.err.println( "WARNING: unable to find field " + fieldEntry + ". dropping mapping." ); classMapping.removeFieldMapping( fieldMapping ); @@ -188,7 +236,7 @@ public class Deobfuscator else if( methodMapping.getObfName().equals( "" ) ) { ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, methodMapping.getObfSignature() ); - if( m_jarIndex.getAccess( constructorEntry ) == null ) + if( !m_jarIndex.containsObfBehavior( constructorEntry ) ) { System.err.println( "WARNING: unable to find constructor " + constructorEntry + ". dropping mapping." ); classMapping.removeMethodMapping( methodMapping ); @@ -201,7 +249,7 @@ public class Deobfuscator methodMapping.getObfName(), methodMapping.getObfSignature() ); - if( m_jarIndex.getAccess( methodEntry ) == null ) + if( !m_jarIndex.containsObfBehavior( methodEntry ) ) { System.err.println( "WARNING: unable to find method " + methodEntry + ". dropping mapping." ); classMapping.removeMethodMapping( methodMapping ); @@ -257,19 +305,20 @@ public class Deobfuscator } } - public CompilationUnit getSourceTree( String className ) + public CompilationUnit getSourceTree( String obfClassName ) { // is this class deobfuscated? // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out // the decompiler only sees the deobfuscated class, so we need to load it by the deobfuscated name - ClassMapping classMapping = m_mappings.getClassByObf( className ); + String lookupClassName = obfClassName; + ClassMapping classMapping = m_mappings.getClassByObf( obfClassName ); if( classMapping != null && classMapping.getDeobfName() != null ) { - className = classMapping.getDeobfName(); + lookupClassName = classMapping.getDeobfName(); } // is this class even in the jar? - if( !m_jarIndex.containsObfClass( new ClassEntry( className ) ) ) + if( !m_jarIndex.containsObfClass( new ClassEntry( obfClassName ) ) ) { return null; } @@ -283,7 +332,7 @@ public class Deobfuscator ) ); // decompile it! - TypeDefinition resolvedType = new MetadataSystem( m_settings.getTypeLoader() ).lookupType( className ).resolve(); + TypeDefinition resolvedType = new MetadataSystem( m_settings.getTypeLoader() ).lookupType( lookupClassName ).resolve(); DecompilerContext context = new DecompilerContext(); context.setCurrentType( resolvedType ); context.setSettings( m_settings ); @@ -302,13 +351,29 @@ public class Deobfuscator // DEBUG //sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); - /* DEBUG + // resolve all the classes in the source references for( Token token : index.referenceTokens() ) { - EntryReference reference = index.getDeobfReference( token ); - System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); + EntryReference deobfReference = index.getDeobfReference( token ); + + // get the obfuscated entry + Entry obfEntry = obfuscateEntry( deobfReference.entry ); + + // try to resolve the class + ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass( obfEntry ); + if( resolvedObfClassEntry != null && !resolvedObfClassEntry.equals( obfEntry.getClassEntry() ) ) + { + // change the class of the entry + obfEntry = obfEntry.cloneToNewClass( resolvedObfClassEntry ); + + // save the new deobfuscated reference + deobfReference.entry = deobfuscateEntry( obfEntry ); + index.replaceDeobfReference( token, deobfReference ); + } + + // DEBUG + //System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); } - */ return index; } @@ -486,13 +551,6 @@ public class Deobfuscator public boolean isObfuscatedIdentifier( Entry obfEntry ) { - if( obfEntry instanceof ClassEntry ) - { - return m_jarIndex.getObfClassEntries().contains( obfEntry ); - } - else - { - return isObfuscatedIdentifier( obfEntry.getClassEntry() ); - } + return m_jarIndex.containsObfEntry( obfEntry ); } } diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java index b82b2547..44e0220c 100644 --- a/src/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -117,7 +117,7 @@ public class EntryRenamer { ArgumentEntry argumentEntry = (ArgumentEntry)thing; return (T)new ArgumentEntry( - renameMethodsInThing( renames, argumentEntry.getMethodEntry() ), + renameMethodsInThing( renames, argumentEntry.getBehaviorEntry() ), argumentEntry.getIndex(), argumentEntry.getName() ); @@ -177,7 +177,7 @@ public class EntryRenamer { ArgumentEntry argumentEntry = (ArgumentEntry)thing; return (T)new ArgumentEntry( - renameClassesInThing( renames, argumentEntry.getMethodEntry() ), + renameClassesInThing( renames, argumentEntry.getBehaviorEntry() ), argumentEntry.getIndex(), argumentEntry.getName() ); diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index b51428a2..9f309cec 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -42,6 +42,7 @@ import com.google.common.collect.Sets; import cuchaz.enigma.Constants; import cuchaz.enigma.bytecode.ClassRenamer; +import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; @@ -264,7 +265,15 @@ public class JarIndex call.getMethodName(), call.getSignature() ); - calledMethodEntry = resolveMethodClass( calledMethodEntry ); + ClassEntry resolvedClassEntry = resolveEntryClass( calledMethodEntry ); + if( resolvedClassEntry != null && !resolvedClassEntry.equals( calledMethodEntry.getClassEntry() ) ) + { + calledMethodEntry = new MethodEntry( + resolvedClassEntry, + call.getMethodName(), + call.getSignature() + ); + } EntryReference reference = new EntryReference( calledMethodEntry, behaviorEntry @@ -280,6 +289,14 @@ public class JarIndex new ClassEntry( className ), call.getFieldName() ); + ClassEntry resolvedClassEntry = resolveEntryClass( calledFieldEntry ); + if( resolvedClassEntry != null && !resolvedClassEntry.equals( calledFieldEntry.getClassEntry() ) ) + { + calledFieldEntry = new FieldEntry( + resolvedClassEntry, + call.getFieldName() + ); + } EntryReference reference = new EntryReference( calledFieldEntry, behaviorEntry @@ -355,30 +372,26 @@ public class JarIndex throw new IllegalArgumentException( "behavior must be a method or a constructor!" ); } } - - private MethodEntry resolveMethodClass( MethodEntry methodEntry ) + + public ClassEntry resolveEntryClass( Entry obfEntry ) { // this entry could refer to a method on a class where the method is not actually implemented // travel up the inheritance tree to find the closest implementation - while( !isMethodImplemented( methodEntry ) ) + while( !containsObfEntry( obfEntry ) ) { // is there a parent class? - String superclassName = m_translationIndex.getSuperclassName( methodEntry.getClassName() ); + String superclassName = m_translationIndex.getSuperclassName( obfEntry.getClassName() ); if( superclassName == null ) { // this is probably a method from a class in a library // we can't trace the implementation up any higher unless we index the library - return methodEntry; + return null; } // move up to the parent class - methodEntry = new MethodEntry( - new ClassEntry( superclassName ), - methodEntry.getName(), - methodEntry.getSignature() - ); + obfEntry = obfEntry.cloneToNewClass( new ClassEntry( superclassName ) ); } - return methodEntry; + return obfEntry.getClassEntry(); } private CtMethod getBridgedMethod( CtMethod method ) @@ -704,16 +717,6 @@ public class JarIndex return m_fieldClasses.get( fieldEntry ); } - public boolean isMethodImplemented( MethodEntry methodEntry ) - { - Collection implementations = m_methodImplementations.get( methodEntry.getClassName() ); - if( implementations == null ) - { - return false; - } - return implementations.contains( methodEntry ); - } - public ClassInheritanceTreeNode getClassInheritance( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) { // get the root node @@ -751,7 +754,7 @@ public class JarIndex obfMethodEntry.getName(), obfMethodEntry.getSignature() ); - if( isMethodImplemented( ancestorMethodEntry ) ) + if( containsObfBehavior( ancestorMethodEntry ) ) { baseImplementationClassName = ancestorClassName; } @@ -766,7 +769,7 @@ public class JarIndex MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( deobfuscatingTranslator, methodEntry, - isMethodImplemented( methodEntry ) + containsObfBehavior( methodEntry ) ); // expand the full tree @@ -796,7 +799,7 @@ public class JarIndex obfMethodEntry.getName(), obfMethodEntry.getSignature() ); - if( isMethodImplemented( methodInterface ) ) + if( containsObfBehavior( methodInterface ) ) { methodInterfaces.add( methodInterface ); } @@ -827,7 +830,7 @@ public class JarIndex private void getRelatedMethodImplementations( Set methodEntries, MethodInheritanceTreeNode node ) { MethodEntry methodEntry = node.getMethodEntry(); - if( isMethodImplemented( methodEntry ) ) + if( containsObfBehavior( methodEntry ) ) { // collect the entry methodEntries.add( methodEntry ); @@ -850,7 +853,7 @@ public class JarIndex private void getRelatedMethodImplementations( Set methodEntries, MethodImplementationsTreeNode node ) { MethodEntry methodEntry = node.getMethodEntry(); - if( isMethodImplemented( methodEntry ) ) + if( containsObfBehavior( methodEntry ) ) { // collect the entry methodEntries.add( methodEntry ); @@ -969,8 +972,49 @@ public class JarIndex return m_access.containsKey( obfFieldEntry ); } - public boolean containsObfMethod( MethodEntry obfMethodEntry ) + public boolean containsObfBehavior( BehaviorEntry obfBehaviorEntry ) + { + return m_access.containsKey( obfBehaviorEntry ); + } + + public boolean containsObfArgument( ArgumentEntry obfArgumentEntry ) + { + // check the behavior + if( !containsObfBehavior( obfArgumentEntry.getBehaviorEntry() ) ) + { + return false; + } + + // check the argument + if( obfArgumentEntry.getIndex() >= Descriptor.numOfParameters( obfArgumentEntry.getBehaviorEntry().getSignature() ) ) + { + return false; + } + + return true; + } + + public boolean containsObfEntry( Entry obfEntry ) { - return m_access.containsKey( obfMethodEntry ); + if( obfEntry instanceof ClassEntry ) + { + return containsObfClass( (ClassEntry)obfEntry ); + } + else if( obfEntry instanceof FieldEntry ) + { + return containsObfField( (FieldEntry)obfEntry ); + } + else if( obfEntry instanceof BehaviorEntry ) + { + return containsObfBehavior( (BehaviorEntry)obfEntry ); + } + else if( obfEntry instanceof ArgumentEntry ) + { + return containsObfArgument( (ArgumentEntry)obfEntry ); + } + else + { + throw new Error( "Entry type not supported: " + obfEntry.getClass().getName() ); + } } } diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index 8b9dd2d8..a050282b 100644 --- a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -81,7 +81,7 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode m_entry.getName(), m_entry.getSignature() ); - if( index.isMethodImplemented( methodEntry ) ) + if( index.containsObfBehavior( methodEntry ) ) { nodes.add( new MethodImplementationsTreeNode( m_deobfuscatingTranslator, methodEntry ) ); } diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index d77fd858..bd919518 100644 --- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -93,7 +93,7 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode nodes.add( new MethodInheritanceTreeNode( m_deobfuscatingTranslator, methodEntry, - index.isMethodImplemented( methodEntry ) + index.containsObfBehavior( methodEntry ) ) ); } diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 49451b90..a5d1460f 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -126,6 +126,15 @@ public class SourceIndex return m_tokenToReference.get( token ); } + public void replaceDeobfReference( Token token, EntryReference newDeobfReference ) + { + EntryReference oldDeobfReference = m_tokenToReference.get( token ); + m_tokenToReference.put( token, newDeobfReference ); + Collection tokens = m_referenceToTokens.get( oldDeobfReference ); + m_referenceToTokens.removeAll( oldDeobfReference ); + m_referenceToTokens.putAll( newDeobfReference, tokens ); + } + public Iterable referenceTokens( ) { return m_tokenToReference.keySet(); diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index 6238b1e7..f307c11d 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -116,6 +116,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); if( ref != null ) { + // make sure this is actually a field + if( ref.getSignature().indexOf( '(' ) >= 0 ) + { + throw new Error( "Expected a field here! got " + ref ); + } + ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); index.addReference( @@ -149,8 +155,16 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor ParameterDefinition def = node.getUserData( Keys.PARAMETER_DEFINITION ); ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); MethodDefinition methodDef = (MethodDefinition)def.getMethod(); - MethodEntry methodEntry = new MethodEntry( classEntry, methodDef.getName(), methodDef.getSignature() ); - ArgumentEntry argumentEntry = new ArgumentEntry( methodEntry, def.getPosition(), def.getName() ); + BehaviorEntry behaviorEntry; + if( methodDef.isConstructor() ) + { + behaviorEntry = new ConstructorEntry( classEntry, methodDef.getSignature() ); + } + else + { + behaviorEntry = new MethodEntry( classEntry, methodDef.getName(), methodDef.getSignature() ); + } + ArgumentEntry argumentEntry = new ArgumentEntry( behaviorEntry, def.getPosition(), def.getName() ); index.addDeclaration( node.getNameToken(), argumentEntry ); return recurse( node, index ); diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java index a8d3983f..adea7eae 100644 --- a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -15,9 +15,13 @@ import java.util.List; import javassist.CtBehavior; import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtMethod; import javassist.bytecode.Descriptor; import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; @@ -42,12 +46,26 @@ public class MethodParameterWriter continue; } + // get the behavior entry + BehaviorEntry behaviorEntry; + if( behavior instanceof CtMethod ) + { + behaviorEntry = new MethodEntry( classEntry, behavior.getMethodInfo().getName(), behavior.getSignature() ); + } + else if( behavior instanceof CtConstructor ) + { + behaviorEntry = new ConstructorEntry( classEntry, behavior.getSignature() ); + } + else + { + throw new Error( "Unsupported behavior type: " + behavior.getClass().getName() ); + } + // get the list of parameter names - MethodEntry methodEntry = new MethodEntry( classEntry, behavior.getMethodInfo().getName(), behavior.getSignature() ); List names = new ArrayList( numParams ); for( int i=0; i fallbackMatching = Maps.newHashMap(); @@ -271,7 +271,7 @@ public class ClassMatcher methodMapping.getObfName(), methodMapping.getObfSignature() ); - if( !destIndex.isMethodImplemented( methodEntry ) ) + if( !destIndex.containsObfBehavior( methodEntry ) ) { System.err.println( "WARNING: method doesn't match: " + methodEntry ); diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 5eed728a..1f04aa35 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -229,27 +229,27 @@ public class Gui switch( event.getKeyCode() ) { case KeyEvent.VK_R: - startRename(); + m_renameMenu.doClick(); break; case KeyEvent.VK_I: - showInheritance(); + m_showInheritanceMenu.doClick(); break; case KeyEvent.VK_M: - showImplementations(); + m_showImplementationsMenu.doClick(); break; case KeyEvent.VK_N: - navigateTo( m_reference.entry ); + m_openEntryMenu.doClick(); break; case KeyEvent.VK_P: - m_controller.openPreviousReference(); + m_openPreviousMenu.doClick(); break; case KeyEvent.VK_C: - showCalls(); + m_showCallsMenu.doClick(); break; } } @@ -987,7 +987,7 @@ public class Gui { addNameValue( m_infoPanel, "Argument", entry.getName() ); addNameValue( m_infoPanel, "Class", entry.getClassEntry().getName() ); - addNameValue( m_infoPanel, "Method", entry.getMethodEntry().getName() ); + addNameValue( m_infoPanel, "Method", entry.getBehaviorEntry().getName() ); addNameValue( m_infoPanel, "Index", Integer.toString( entry.getIndex() ) ); } @@ -1014,7 +1014,7 @@ public class Gui boolean isFieldEntry = isToken && m_reference.entry instanceof FieldEntry; boolean isMethodEntry = isToken && m_reference.entry instanceof MethodEntry; boolean isConstructorEntry = isToken && m_reference.entry instanceof ConstructorEntry; - boolean isInJar = isToken && m_controller.entryIsInJar( m_reference.entry.getClassEntry() ); + boolean isInJar = isToken && m_controller.entryIsInJar( m_reference.entry ); if( isToken ) { diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index bbefe606..098e065d 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -270,7 +270,7 @@ public class GuiController ClassEntry obfClassEntry = obfReference.getClassEntry().getOuterClassEntry(); if( !m_deobfuscator.isObfuscatedIdentifier( obfClassEntry ) ) { - throw new IllegalArgumentException( "Entry must be in the jar!" ); + throw new IllegalArgumentException( "Obfuscated class " + obfClassEntry + " was not found in the jar!" ); } if( m_currentObfClass == null || !m_currentObfClass.equals( obfClassEntry ) ) { @@ -354,6 +354,7 @@ public class GuiController if( sourceTree == null ) { // decompilation of this class is not supported + m_gui.setSource("Unable to find class: " + classEntry); return; } String source = m_deobfuscator.getSource( sourceTree ); diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java index 27dcc41d..7ed3d328 100644 --- a/src/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -18,15 +18,15 @@ public class ArgumentEntry implements Entry, Serializable { private static final long serialVersionUID = 4472172468162696006L; - private MethodEntry m_methodEntry; + private BehaviorEntry m_behaviorEntry; private int m_index; private String m_name; - public ArgumentEntry( MethodEntry methodEntry, int index, String name ) + public ArgumentEntry( BehaviorEntry behaviorEntry, int index, String name ) { - if( methodEntry == null ) + if( behaviorEntry == null ) { - throw new IllegalArgumentException( "Method cannot be null!" ); + throw new IllegalArgumentException( "Behavior cannot be null!" ); } if( index < 0 ) { @@ -37,21 +37,28 @@ public class ArgumentEntry implements Entry, Serializable throw new IllegalArgumentException( "Argument name cannot be null!" ); } - m_methodEntry = methodEntry; + m_behaviorEntry = behaviorEntry; m_index = index; m_name = name; } public ArgumentEntry( ArgumentEntry other ) { - m_methodEntry = new MethodEntry( other.m_methodEntry ); + m_behaviorEntry = (BehaviorEntry)m_behaviorEntry.cloneToNewClass( getClassEntry() ); m_index = other.m_index; m_name = other.m_name; } - public MethodEntry getMethodEntry( ) + public ArgumentEntry( ArgumentEntry other, String newClassName ) { - return m_methodEntry; + m_behaviorEntry = (BehaviorEntry)other.m_behaviorEntry.cloneToNewClass( new ClassEntry( newClassName ) ); + m_index = other.m_index; + m_name = other.m_name; + } + + public BehaviorEntry getBehaviorEntry( ) + { + return m_behaviorEntry; } public int getIndex( ) @@ -68,29 +75,35 @@ public class ArgumentEntry implements Entry, Serializable @Override public ClassEntry getClassEntry( ) { - return m_methodEntry.getClassEntry(); + return m_behaviorEntry.getClassEntry(); } @Override public String getClassName( ) { - return m_methodEntry.getClassName(); + return m_behaviorEntry.getClassName(); + } + + @Override + public ArgumentEntry cloneToNewClass( ClassEntry classEntry ) + { + return new ArgumentEntry( this, classEntry.getName() ); } public String getMethodName( ) { - return m_methodEntry.getName(); + return m_behaviorEntry.getName(); } public String getMethodSignature( ) { - return m_methodEntry.getSignature(); + return m_behaviorEntry.getSignature(); } @Override public int hashCode( ) { - return Util.combineHashesOrdered( m_methodEntry, Integer.valueOf( m_index ).hashCode(), m_name.hashCode() ); + return Util.combineHashesOrdered( m_behaviorEntry, Integer.valueOf( m_index ).hashCode(), m_name.hashCode() ); } @Override @@ -105,7 +118,7 @@ public class ArgumentEntry implements Entry, Serializable public boolean equals( ArgumentEntry other ) { - return m_methodEntry.equals( other.m_methodEntry ) + return m_behaviorEntry.equals( other.m_behaviorEntry ) && m_index == other.m_index && m_name.equals( other.m_name ); } @@ -113,6 +126,6 @@ public class ArgumentEntry implements Entry, Serializable @Override public String toString( ) { - return m_methodEntry.toString() + "(" + m_index + ":" + m_name + ")"; + return m_behaviorEntry.toString() + "(" + m_index + ":" + m_name + ")"; } } diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index c6faa506..2c708f2a 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -43,12 +43,6 @@ public class ClassEntry implements Entry, Serializable m_name = other.m_name; } - @Override - public ClassEntry getClassEntry( ) - { - return this; - } - @Override public String getName( ) { @@ -61,6 +55,18 @@ public class ClassEntry implements Entry, Serializable return m_name; } + @Override + public ClassEntry getClassEntry( ) + { + return this; + } + + @Override + public ClassEntry cloneToNewClass( ClassEntry classEntry ) + { + return classEntry; + } + @Override public int hashCode( ) { diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 88006cff..b551d71c 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -139,6 +139,16 @@ public class ClassMapping implements Serializable, Comparable return m_fieldsByObf.values(); } + public boolean containsObfField( String obfName ) + { + return m_fieldsByObf.containsKey( obfName ); + } + + public boolean containsDeobfField( String deobfName ) + { + return m_fieldsByDeobf.containsKey( deobfName ); + } + public void addFieldMapping( FieldMapping fieldMapping ) { if( m_fieldsByObf.containsKey( fieldMapping.getObfName() ) ) @@ -214,6 +224,16 @@ public class ClassMapping implements Serializable, Comparable return m_methodsByObf.values(); } + public boolean containsObfMethod( String obfName, String obfSignature ) + { + return m_methodsByObf.containsKey( getMethodKey( obfName, obfSignature ) ); + } + + public boolean containsDeobfMethod( String deobfName, String deobfSignature ) + { + return m_methodsByDeobf.containsKey( getMethodKey( deobfName, deobfSignature ) ); + } + public void addMethodMapping( MethodMapping methodMapping ) { String obfKey = getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ); @@ -375,19 +395,9 @@ public class ClassMapping implements Serializable, Comparable return false; } - public boolean containsDeobfField( String name ) - { - return m_fieldsByDeobf.containsKey( name ); - } - - public boolean containsDeobfMethod( String name, String signature ) - { - return m_methodsByDeobf.containsKey( getMethodKey( name, signature ) ); - } - - public boolean containsArgument( MethodEntry obfMethodEntry, String name ) + public boolean containsArgument( BehaviorEntry obfBehaviorEntry, String name ) { - MethodMapping methodMapping = m_methodsByObf.get( getMethodKey( obfMethodEntry.getName(), obfMethodEntry.getSignature() ) ); + MethodMapping methodMapping = m_methodsByObf.get( getMethodKey( obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature() ) ); if( methodMapping != null ) { return methodMapping.containsArgument( name ); diff --git a/src/cuchaz/enigma/mapping/ConstructorEntry.java b/src/cuchaz/enigma/mapping/ConstructorEntry.java index ad029e1c..d99d1c35 100644 --- a/src/cuchaz/enigma/mapping/ConstructorEntry.java +++ b/src/cuchaz/enigma/mapping/ConstructorEntry.java @@ -43,6 +43,12 @@ public class ConstructorEntry implements BehaviorEntry, Serializable m_signature = other.m_signature; } + public ConstructorEntry( ConstructorEntry other, String newClassName ) + { + m_classEntry = new ClassEntry( newClassName ); + m_signature = other.m_signature; + } + @Override public ClassEntry getClassEntry( ) { @@ -76,6 +82,12 @@ public class ConstructorEntry implements BehaviorEntry, Serializable return m_classEntry.getName(); } + @Override + public ConstructorEntry cloneToNewClass( ClassEntry classEntry ) + { + return new ConstructorEntry( this, classEntry.getName() ); + } + @Override public int hashCode( ) { diff --git a/src/cuchaz/enigma/mapping/Entry.java b/src/cuchaz/enigma/mapping/Entry.java index e1591f02..8524834c 100644 --- a/src/cuchaz/enigma/mapping/Entry.java +++ b/src/cuchaz/enigma/mapping/Entry.java @@ -15,4 +15,5 @@ public interface Entry String getName( ); String getClassName( ); ClassEntry getClassEntry( ); + Entry cloneToNewClass( ClassEntry classEntry ); } diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java index 435490bd..626af576 100644 --- a/src/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -67,6 +67,12 @@ public class FieldEntry implements Entry, Serializable return m_classEntry.getName(); } + @Override + public FieldEntry cloneToNewClass( ClassEntry classEntry ) + { + return new FieldEntry( this, classEntry.getName() ); + } + @Override public int hashCode( ) { diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index f855f580..0b4e7f3c 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -253,12 +253,12 @@ public class Mappings implements Serializable return false; } - public boolean containsArgument( MethodEntry obfMethodEntry, String name ) + public boolean containsArgument( BehaviorEntry obfBehaviorEntry, String name ) { - ClassMapping classMapping = m_classesByObf.get( obfMethodEntry.getClassName() ); + ClassMapping classMapping = m_classesByObf.get( obfBehaviorEntry.getClassName() ); if( classMapping != null ) { - return classMapping.containsArgument( obfMethodEntry, name ); + return classMapping.containsArgument( obfBehaviorEntry, name ); } return false; } diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index 49e7b5fd..dcceefbd 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -79,7 +79,7 @@ public class MappingsRenamer { String deobfSignature = getTranslator( TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); MethodEntry targetEntry = new MethodEntry( entry.getClassEntry(), deobfName, deobfSignature ); - if( m_mappings.containsDeobfMethod( entry.getClassEntry(), deobfName, entry.getSignature() ) || m_index.containsObfMethod( targetEntry ) ) + if( m_mappings.containsDeobfMethod( entry.getClassEntry(), deobfName, entry.getSignature() ) || m_index.containsObfBehavior( targetEntry ) ) { String deobfClassName = getTranslator( TranslationDirection.Deobfuscating ).translateClass( entry.getClassName() ); throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + deobfClassName ); @@ -96,7 +96,7 @@ public class MappingsRenamer { deobfName = NameValidator.validateMethodName( deobfName ); MethodEntry targetEntry = new MethodEntry( obf.getClassEntry(), deobfName, obf.getSignature() ); - if( m_mappings.containsDeobfMethod( obf.getClassEntry(), deobfName, obf.getSignature() ) || m_index.containsObfMethod( targetEntry ) ) + if( m_mappings.containsDeobfMethod( obf.getClassEntry(), deobfName, obf.getSignature() ) || m_index.containsObfBehavior( targetEntry ) ) { String deobfClassName = getTranslator( TranslationDirection.Deobfuscating ).translateClass( obf.getClassName() ); throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + deobfClassName ); @@ -110,7 +110,7 @@ public class MappingsRenamer { deobfName = NameValidator.validateArgumentName( deobfName ); // NOTE: don't need to check arguments for name collisions with names determined by Procyon - if( m_mappings.containsArgument( obf.getMethodEntry(), deobfName ) ) + if( m_mappings.containsArgument( obf.getBehaviorEntry(), deobfName ) ) { throw new IllegalNameException( deobfName, "There is already an argument with that name" ); } @@ -119,6 +119,32 @@ public class MappingsRenamer classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName ); } + public boolean moveFieldToObfClass( ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass ) + { + classMapping.removeFieldMapping( fieldMapping ); + ClassMapping targetClassMapping = getOrCreateClassMapping( obfClass ); + if( !targetClassMapping.containsObfField( fieldMapping.getObfName() ) + && !targetClassMapping.containsDeobfField( fieldMapping.getDeobfName() ) ) + { + targetClassMapping.addFieldMapping( fieldMapping ); + return true; + } + return false; + } + + public boolean moveMethodToObfClass( ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass ) + { + classMapping.removeMethodMapping( methodMapping ); + ClassMapping targetClassMapping = getOrCreateClassMapping( obfClass ); + if( !targetClassMapping.containsObfMethod( methodMapping.getObfName(), methodMapping.getObfSignature() ) + && !targetClassMapping.containsDeobfMethod( methodMapping.getDeobfName(), methodMapping.getObfSignature() ) ) + { + targetClassMapping.addMethodMapping( methodMapping ); + return true; + } + return false; + } + public void write( OutputStream out ) throws IOException { diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java index a311e636..8adbfe9c 100644 --- a/src/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -80,6 +80,12 @@ public class MethodEntry implements BehaviorEntry, Serializable return m_classEntry.getName(); } + @Override + public MethodEntry cloneToNewClass( ClassEntry classEntry ) + { + return new MethodEntry( this, classEntry.getName() ); + } + @Override public int hashCode( ) { diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index b438e08d..7904ef53 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -271,7 +271,7 @@ public class Translator name = in.getName(); } return new ArgumentEntry( - translateEntry( in.getMethodEntry() ), + translateEntry( in.getBehaviorEntry() ), in.getIndex(), name ); diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 317a6bc4..50c22825 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -99,28 +99,6 @@ public class TestJarIndexInheritanceTree assertThat( m_index.getAccess( m_numThingsField ), is( Access.Private ) ); } - @Test - public void isImplemented( ) - { - // getName() - assertThat( m_index.isMethodImplemented( new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ) ), is( true ) ); - assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassA, "a", "()Ljava/lang/String;" ) ), is( false ) ); - assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ) ), is( true ) ); - assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassB, "a", "()Ljava/lang/String;" ) ), is( false ) ); - - // doBaseThings() - assertThat( m_index.isMethodImplemented( new MethodEntry( m_baseClass, "a", "()V" ) ), is( true ) ); - assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassA, "a", "()V" ) ), is( false ) ); - assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassAA, "a", "()V" ) ), is( true ) ); - assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassB, "a", "()V" ) ), is( true ) ); - - // doBThings() - assertThat( m_index.isMethodImplemented( new MethodEntry( m_baseClass, "b", "()V" ) ), is( false ) ); - assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassA, "b", "()V" ) ), is( false ) ); - assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassAA, "b", "()V" ) ), is( false ) ); - assertThat( m_index.isMethodImplemented( new MethodEntry( m_subClassB, "b", "()V" ) ), is( true ) ); - } - @Test public void relatedMethodImplementations( ) { @@ -238,11 +216,23 @@ public class TestJarIndexInheritanceTree assertThat( m_index.containsObfField( m_numThingsField ), is( true ) ); // methods - assertThat( m_index.containsObfMethod( new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ) ), is( true ) ); - assertThat( m_index.containsObfMethod( new MethodEntry( m_baseClass, "a", "()V" ) ), is( true ) ); - assertThat( m_index.containsObfMethod( new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ) ), is( true ) ); - assertThat( m_index.containsObfMethod( new MethodEntry( m_subClassAA, "a", "()V" ) ), is( true ) ); - assertThat( m_index.containsObfMethod( new MethodEntry( m_subClassB, "a", "()V" ) ), is( true ) ); - assertThat( m_index.containsObfMethod( new MethodEntry( m_subClassB, "b", "()V" ) ), is( true ) ); + // getName() + assertThat( m_index.containsObfBehavior( new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ) ), is( true ) ); + assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassA, "a", "()Ljava/lang/String;" ) ), is( false ) ); + assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ) ), is( true ) ); + assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassB, "a", "()Ljava/lang/String;" ) ), is( false ) ); + + // doBaseThings() + assertThat( m_index.containsObfBehavior( new MethodEntry( m_baseClass, "a", "()V" ) ), is( true ) ); + assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassA, "a", "()V" ) ), is( false ) ); + assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassAA, "a", "()V" ) ), is( true ) ); + assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassB, "a", "()V" ) ), is( true ) ); + + // doBThings() + assertThat( m_index.containsObfBehavior( new MethodEntry( m_baseClass, "b", "()V" ) ), is( false ) ); + assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassA, "b", "()V" ) ), is( false ) ); + assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassAA, "b", "()V" ) ), is( false ) ); + assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassB, "b", "()V" ) ), is( true ) ); + } } diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index 4c32b703..f77d86a7 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin.\ + * Copyright (c) 2014 Jeff Martin. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 @@ -72,13 +72,6 @@ public class TestJarIndexLoneClass assertThat( m_index.getAccess( newField( "none/a", "b" ) ), is( nullValue() ) ); } - @Test - public void isImplemented( ) - { - assertThat( m_index.isMethodImplemented( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( true ) ); - assertThat( m_index.isMethodImplemented( newMethod( "none/a", "b", "()Ljava/lang/String;" ) ), is( false ) ); - } - @Test public void classInheritance( ) { @@ -187,7 +180,7 @@ public class TestJarIndexLoneClass assertThat( m_index.containsObfClass( newClass( "none/b" ) ), is( false ) ); assertThat( m_index.containsObfField( newField( "none/a", "a" ) ), is( true ) ); assertThat( m_index.containsObfField( newField( "none/a", "b" ) ), is( false ) ); - assertThat( m_index.containsObfMethod( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( true ) ); - assertThat( m_index.containsObfMethod( newMethod( "none/a", "b", "()Ljava/lang/String;" ) ), is( false ) ); + assertThat( m_index.containsObfBehavior( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( true ) ); + assertThat( m_index.containsObfBehavior( newMethod( "none/a", "b", "()Ljava/lang/String;" ) ), is( false ) ); } } diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java new file mode 100644 index 00000000..dc6ca7e2 --- /dev/null +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.File; +import java.util.Set; + +import org.junit.Test; + +import com.google.common.collect.Sets; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; + +import cuchaz.enigma.mapping.ClassEntry; + +public class TestSourceIndex +{ + @Test + public void indexEverything( ) + throws Exception + { + Deobfuscator deobfuscator = new Deobfuscator( new File( "input/1.8.jar" ) ); + + // get all classes that aren't inner classes + Set classEntries = Sets.newHashSet(); + for( ClassEntry obfClassEntry : deobfuscator.getJarIndex().getObfClassEntries() ) + { + if( !obfClassEntry.isInnerClass() ) + { + classEntries.add( obfClassEntry ); + } + } + + for( ClassEntry obfClassEntry : classEntries ) + { + try + { + CompilationUnit tree = deobfuscator.getSourceTree( obfClassEntry.getName() ); + String source = deobfuscator.getSource( tree ); + deobfuscator.getSourceIndex( tree, source ); + } + catch( Throwable t ) + { + throw new Error( "Unable to index " + obfClassEntry, t ); + } + } + } +} -- cgit v1.2.3 From 064fe6a628f23f21eb2c8f584215f439e54cfaec Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 24 Sep 2014 20:32:19 -0400 Subject: fixed in-jar detection for bridge-related methods --- src/cuchaz/enigma/Deobfuscator.java | 39 ++++------- src/cuchaz/enigma/analysis/BridgeFixer.java | 22 +++++-- src/cuchaz/enigma/analysis/EntryRenamer.java | 25 +++++-- src/cuchaz/enigma/analysis/JarIndex.java | 40 ++---------- .../enigma/analysis/SourceIndexClassVisitor.java | 12 +--- src/cuchaz/enigma/bytecode/ClassTranslator.java | 41 +++++------- src/cuchaz/enigma/mapping/BehaviorEntry.java | 12 +++- .../enigma/mapping/BehaviorEntryFactory.java | 76 ++++++++++++++++++++++ src/cuchaz/enigma/mapping/MethodEntry.java | 4 ++ src/cuchaz/enigma/mapping/MethodMapping.java | 5 ++ 10 files changed, 170 insertions(+), 106 deletions(-) create mode 100644 src/cuchaz/enigma/mapping/BehaviorEntryFactory.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index f7f74480..9a339176 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -41,6 +41,8 @@ import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; import cuchaz.enigma.mapping.ConstructorEntry; @@ -165,6 +167,12 @@ public class Deobfuscator // methods for( MethodMapping methodMapping : Lists.newArrayList( classMapping.methods() ) ) { + // skip constructors + if( methodMapping.isConstructor() ) + { + continue; + } + MethodEntry methodEntry = new MethodEntry( obfClassEntry, methodMapping.getObfName(), methodMapping.getObfSignature() ); ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass( methodEntry ); if( resolvedObfClassEntry != null && !resolvedObfClassEntry.equals( methodEntry.getClassEntry() ) ) @@ -228,33 +236,12 @@ public class Deobfuscator // check methods for( MethodMapping methodMapping : Lists.newArrayList( classMapping.methods() ) ) { - if( methodMapping.getObfName().equals( "" ) ) - { - // skip static initializers - continue; - } - else if( methodMapping.getObfName().equals( "" ) ) - { - ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, methodMapping.getObfSignature() ); - if( !m_jarIndex.containsObfBehavior( constructorEntry ) ) - { - System.err.println( "WARNING: unable to find constructor " + constructorEntry + ". dropping mapping." ); - classMapping.removeMethodMapping( methodMapping ); - } - } - else + BehaviorEntry obfBehaviorEntry = BehaviorEntryFactory.createObf( classEntry, methodMapping ); + if( !m_jarIndex.containsObfBehavior( obfBehaviorEntry ) ) { - MethodEntry methodEntry = new MethodEntry( - classEntry, - methodMapping.getObfName(), - methodMapping.getObfSignature() - ); - if( !m_jarIndex.containsObfBehavior( methodEntry ) ) - { - System.err.println( "WARNING: unable to find method " + methodEntry + ". dropping mapping." ); - classMapping.removeMethodMapping( methodMapping ); - } - } + System.err.println( "WARNING: unable to find behavior " + obfBehaviorEntry + ". dropping mapping." ); + classMapping.removeMethodMapping( methodMapping ); + } } // check inner classes diff --git a/src/cuchaz/enigma/analysis/BridgeFixer.java b/src/cuchaz/enigma/analysis/BridgeFixer.java index aeaf871a..112b864a 100644 --- a/src/cuchaz/enigma/analysis/BridgeFixer.java +++ b/src/cuchaz/enigma/analysis/BridgeFixer.java @@ -15,6 +15,8 @@ import javassist.CtMethod; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import cuchaz.enigma.bytecode.ConstPoolEditor; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.MethodEntry; @@ -57,17 +59,23 @@ public class BridgeFixer case ConstPool.CONST_Methodref: case ConstPool.CONST_InterfaceMethodref: { - // translate the name and type - MethodEntry methodEntry = new MethodEntry( - new ClassEntry( Descriptor.toJvmName( editor.getMemberrefClassname( i ) ) ), + BehaviorEntry behaviorEntry = BehaviorEntryFactory.create( + Descriptor.toJvmName( editor.getMemberrefClassname( i ) ), editor.getMemberrefName( i ), editor.getMemberrefType( i ) ); - MethodEntry bridgeMethodEntry = m_index.getBridgeMethod( methodEntry ); - if( bridgeMethodEntry != null ) + + if( behaviorEntry instanceof MethodEntry ) { - // FIXIT FIXIT FIXIT FIXIT FIXIT FIXIT FIXIT - editor.changeMemberrefNameAndType( i, bridgeMethodEntry.getName(), bridgeMethodEntry.getSignature() ); + MethodEntry methodEntry = (MethodEntry)behaviorEntry; + + // translate the name and type + MethodEntry bridgeMethodEntry = m_index.getBridgeMethod( methodEntry ); + if( bridgeMethodEntry != null ) + { + // FIXIT FIXIT FIXIT FIXIT FIXIT FIXIT FIXIT + editor.changeMemberrefNameAndType( i, bridgeMethodEntry.getName(), bridgeMethodEntry.getSignature() ); + } } } break; diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java index 44e0220c..2d59fe9d 100644 --- a/src/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -11,7 +11,6 @@ package cuchaz.enigma.analysis; import java.util.AbstractMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -80,16 +79,32 @@ public class EntryRenamer { // for each key/value pair... Set> entriesToAdd = Sets.newHashSet(); - Iterator> iter = map.entries().iterator(); - while( iter.hasNext() ) + for( Map.Entry entry : map.entries() ) { - Map.Entry entry = iter.next(); - iter.remove(); entriesToAdd.add( new AbstractMap.SimpleEntry( renameMethodsInThing( renames, entry.getKey() ), renameMethodsInThing( renames, entry.getValue() ) ) ); } + map.clear(); + for( Map.Entry entry : entriesToAdd ) + { + map.put( entry.getKey(), entry.getValue() ); + } + } + + public static void renameMethodsInMap( Map renames, Map map ) + { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for( Map.Entry entry : map.entrySet() ) + { + entriesToAdd.add( new AbstractMap.SimpleEntry( + renameMethodsInThing( renames, entry.getKey() ), + renameMethodsInThing( renames, entry.getValue() ) + ) ); + } + map.clear(); for( Map.Entry entry : entriesToAdd ) { map.put( entry.getKey(), entry.getValue() ); diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 9f309cec..a2f6bf34 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -44,6 +44,7 @@ import cuchaz.enigma.Constants; import cuchaz.enigma.bytecode.ClassRenamer; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; @@ -203,6 +204,7 @@ public class JarIndex EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_methodImplementations ); EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_behaviorReferences ); EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_fieldReferences ); + EntryRenamer.renameMethodsInMap( m_bridgeMethods, m_access ); } private void indexField( CtField field ) @@ -224,21 +226,20 @@ public class JarIndex private void indexBehavior( CtBehavior behavior ) { // get the behavior entry - String className = Descriptor.toJvmName( behavior.getDeclaringClass().getName() ); - final BehaviorEntry behaviorEntry = getBehaviorEntry( behavior ); + final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create( behavior ); if( behaviorEntry instanceof MethodEntry ) { MethodEntry methodEntry = (MethodEntry)behaviorEntry; // index implementation - m_methodImplementations.put( className, methodEntry ); + m_methodImplementations.put( behaviorEntry.getClassName(), methodEntry ); // look for bridge methods CtMethod bridgedMethod = getBridgedMethod( (CtMethod)behavior ); if( bridgedMethod != null ) { MethodEntry bridgedMethodEntry = new MethodEntry( - new ClassEntry( className ), + behaviorEntry.getClassEntry(), bridgedMethod.getName(), bridgedMethod.getSignature() ); @@ -251,7 +252,7 @@ public class JarIndex private void indexBehaviorReferences( CtBehavior behavior ) { // index method calls - final BehaviorEntry behaviorEntry = getBehaviorEntry( behavior ); + final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create( behavior ); try { behavior.instrument( new ExprEditor( ) @@ -344,35 +345,6 @@ public class JarIndex } } - private BehaviorEntry getBehaviorEntry( CtBehavior behavior ) - { - String className = Descriptor.toJvmName( behavior.getDeclaringClass().getName() ); - if( behavior instanceof CtMethod ) - { - return new MethodEntry( - new ClassEntry( className ), - behavior.getName(), - behavior.getSignature() - ); - } - else if( behavior instanceof CtConstructor ) - { - boolean isStatic = behavior.getName().equals( "" ); - if( isStatic ) - { - return new ConstructorEntry( new ClassEntry( className ) ); - } - else - { - return new ConstructorEntry( new ClassEntry( className ), behavior.getSignature() ); - } - } - else - { - throw new IllegalArgumentException( "behavior must be a method or a constructor!" ); - } - } - public ClassEntry resolveEntryClass( Entry obfEntry ) { // this entry could refer to a method on a class where the method is not actually implemented diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index b7897268..5d8a3833 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -25,11 +25,11 @@ import com.strobel.decompiler.languages.java.ast.TypeDeclaration; import com.strobel.decompiler.languages.java.ast.VariableInitializer; import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.MethodEntry; public class SourceIndexClassVisitor extends SourceIndexVisitor { @@ -77,15 +77,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - BehaviorEntry behaviorEntry; - if( def.getName().equals( "" ) ) - { - behaviorEntry = new ConstructorEntry( classEntry ); - } - else - { - behaviorEntry = new MethodEntry( classEntry, def.getName(), def.getSignature() ); - } + BehaviorEntry behaviorEntry = BehaviorEntryFactory.create( classEntry, def.getName(), def.getSignature() ); index.addDeclaration( node.getNameToken(), behaviorEntry ); return node.acceptVisitor( new SourceIndexBehaviorVisitor( behaviorEntry ), index ); } diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 88926928..db28f21b 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -21,6 +21,8 @@ import javassist.bytecode.Descriptor; import com.google.common.collect.Maps; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; @@ -53,19 +55,15 @@ public class ClassTranslator new ClassEntry( Descriptor.toJvmName( constants.getFieldrefClassName( i ) ) ), constants.getFieldrefName( i ) ); - String translatedName = m_translator.translate( entry ); - if( translatedName == null ) - { - translatedName = entry.getName(); - } + FieldEntry translatedEntry = m_translator.translateEntry( entry ); // translate the type String type = constants.getFieldrefType( i ); String translatedType = m_translator.translateSignature( type ); - if( !entry.getName().equals( translatedName ) || !type.equals( translatedType ) ) + if( !entry.equals( translatedEntry ) || !type.equals( translatedType ) ) { - editor.changeMemberrefNameAndType( i, translatedName, translatedType ); + editor.changeMemberrefNameAndType( i, translatedEntry.getName(), translatedType ); } } break; @@ -74,21 +72,16 @@ public class ClassTranslator case ConstPool.CONST_InterfaceMethodref: { // translate the name and type - MethodEntry entry = new MethodEntry( - new ClassEntry( Descriptor.toJvmName( editor.getMemberrefClassname( i ) ) ), + BehaviorEntry entry = BehaviorEntryFactory.create( + Descriptor.toJvmName( editor.getMemberrefClassname( i ) ), editor.getMemberrefName( i ), editor.getMemberrefType( i ) ); - String translatedName = m_translator.translate( entry ); - if( translatedName == null ) - { - translatedName = entry.getName(); - } - String translatedSignature = m_translator.translateSignature( entry.getSignature() ); + BehaviorEntry translatedEntry = m_translator.translateEntry( entry ); - if( !entry.getName().equals( translatedName ) || !entry.getSignature().equals( translatedSignature ) ) + if( !entry.getName().equals( translatedEntry.getName() ) || !entry.getSignature().equals( translatedEntry.getSignature() ) ) { - editor.changeMemberrefNameAndType( i, translatedName, translatedSignature ); + editor.changeMemberrefNameAndType( i, translatedEntry.getName(), translatedEntry.getSignature() ); } } break; @@ -116,14 +109,16 @@ public class ClassTranslator // translate all the methods and constructors for( CtBehavior behavior : c.getDeclaredBehaviors() ) { - // translate the name - MethodEntry entry = new MethodEntry( classEntry, behavior.getName(), behavior.getSignature() ); - String translatedName = m_translator.translate( entry ); - if( translatedName != null ) + if( behavior instanceof CtMethod ) { - if( behavior instanceof CtMethod ) + CtMethod method = (CtMethod)behavior; + + // translate the name + MethodEntry entry = new MethodEntry( classEntry, method.getName(), method.getSignature() ); + String translatedName = m_translator.translate( entry ); + if( translatedName != null ) { - ((CtMethod)behavior).setName( translatedName ); + method.setName( translatedName ); } } diff --git a/src/cuchaz/enigma/mapping/BehaviorEntry.java b/src/cuchaz/enigma/mapping/BehaviorEntry.java index 99fdd28d..8fc4eaf0 100644 --- a/src/cuchaz/enigma/mapping/BehaviorEntry.java +++ b/src/cuchaz/enigma/mapping/BehaviorEntry.java @@ -1,6 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.mapping; public interface BehaviorEntry extends Entry { - public String getSignature(); + String getSignature(); } diff --git a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java new file mode 100644 index 00000000..d3cfb938 --- /dev/null +++ b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import javassist.CtBehavior; +import javassist.CtConstructor; +import javassist.CtMethod; +import javassist.bytecode.Descriptor; + + +public class BehaviorEntryFactory +{ + public static BehaviorEntry create( String className, String name, String signature ) + { + return create( new ClassEntry( className ), name, signature ); + } + + public static BehaviorEntry create( ClassEntry classEntry, String name, String signature ) + { + if( name.equals( "" ) ) + { + return new ConstructorEntry( classEntry, signature ); + } + else if( name.equals( "" ) ) + { + return new ConstructorEntry( classEntry ); + } + else + { + return new MethodEntry( classEntry, name, signature ); + } + } + + public static BehaviorEntry create( CtBehavior behavior ) + { + String className = Descriptor.toJvmName( behavior.getDeclaringClass().getName() ); + if( behavior instanceof CtMethod ) + { + return create( className, behavior.getName(), behavior.getSignature() ); + } + else if( behavior instanceof CtConstructor ) + { + CtConstructor constructor = (CtConstructor)behavior; + if( constructor.isClassInitializer() ) + { + return create( className, "", null ); + } + else + { + return create( className, "", constructor.getSignature() ); + } + } + else + { + throw new IllegalArgumentException( "Unable to create BehaviorEntry from " + behavior ); + } + } + + public static BehaviorEntry createObf( ClassEntry classEntry, MethodMapping methodMapping ) + { + return create( classEntry, methodMapping.getObfName(), methodMapping.getObfSignature() ); + } + + public static BehaviorEntry createDeobf( ClassEntry classEntry, MethodMapping methodMapping ) + { + return create( classEntry, methodMapping.getDeobfName(), methodMapping.getObfSignature() ); + } +} diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java index 8adbfe9c..dbc18855 100644 --- a/src/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -36,6 +36,10 @@ public class MethodEntry implements BehaviorEntry, Serializable { throw new IllegalArgumentException( "Method signature cannot be null!" ); } + if( name.startsWith( "<" ) ) + { + throw new IllegalArgumentException( "Don't use MethodEntry for a constructor!" ); + } m_classEntry = classEntry; m_name = name; diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index 6210fd09..b076fa33 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -70,6 +70,11 @@ public class MethodMapping implements Serializable, Comparable return m_arguments.values(); } + public boolean isConstructor( ) + { + return m_obfName.startsWith( "<" ); + } + public void addArgumentMapping( ArgumentMapping argumentMapping ) { boolean wasAdded = m_arguments.put( argumentMapping.getIndex(), argumentMapping ) == null; -- cgit v1.2.3 From 2b2b82b2ff6a7907fc5209f4ea6e1c072eb9c28e Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 24 Sep 2014 23:02:07 -0400 Subject: changed warnings for mappings migration --- src/cuchaz/enigma/mapping/MappingsRenamer.java | 28 ++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index dcceefbd..182bbd19 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -123,11 +123,17 @@ public class MappingsRenamer { classMapping.removeFieldMapping( fieldMapping ); ClassMapping targetClassMapping = getOrCreateClassMapping( obfClass ); - if( !targetClassMapping.containsObfField( fieldMapping.getObfName() ) - && !targetClassMapping.containsDeobfField( fieldMapping.getDeobfName() ) ) + if( !targetClassMapping.containsObfField( fieldMapping.getObfName() ) ) { - targetClassMapping.addFieldMapping( fieldMapping ); - return true; + if( !targetClassMapping.containsDeobfField( fieldMapping.getDeobfName() ) ) + { + targetClassMapping.addFieldMapping( fieldMapping ); + return true; + } + else + { + System.err.println( "WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName() ); + } } return false; } @@ -136,11 +142,17 @@ public class MappingsRenamer { classMapping.removeMethodMapping( methodMapping ); ClassMapping targetClassMapping = getOrCreateClassMapping( obfClass ); - if( !targetClassMapping.containsObfMethod( methodMapping.getObfName(), methodMapping.getObfSignature() ) - && !targetClassMapping.containsDeobfMethod( methodMapping.getDeobfName(), methodMapping.getObfSignature() ) ) + if( !targetClassMapping.containsObfMethod( methodMapping.getObfName(), methodMapping.getObfSignature() ) ) { - targetClassMapping.addMethodMapping( methodMapping ); - return true; + if( !targetClassMapping.containsDeobfMethod( methodMapping.getDeobfName(), methodMapping.getObfSignature() ) ) + { + targetClassMapping.addMethodMapping( methodMapping ); + return true; + } + else + { + System.err.println( "WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature() ); + } } return false; } -- cgit v1.2.3 From 5bff7fee33756029579f0f544d6c74800a034fe2 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 25 Sep 2014 22:24:20 -0400 Subject: fixed invalid tokens issue --- src/cuchaz/enigma/analysis/SourceIndex.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index a5d1460f..38d10daa 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -62,13 +62,20 @@ public class SourceIndex Region region = node.getRegion(); if( region.getBeginLine() == 0 || region.getEndLine() == 0 ) { - System.err.println( "WARNING: " + node.getNodeType() + " node has invalid region: " + region ); + // DEBUG + //System.err.println( "WARNING: " + node.getNodeType() + " node has invalid region: " + region ); return null; } Token token = new Token( toPos( region.getBeginLine(), region.getBeginColumn() ), toPos( region.getEndLine(), region.getEndColumn() ) ); + if( token.start == 0 ) + { + // DEBUG + //System.err.println( "WARNING: " + node.getNodeType() + " node has invalid start: " + region ); + return null; + } // for tokens representing inner classes, make sure we only get the simple name int pos = node.toString().lastIndexOf( '$' ); -- cgit v1.2.3 From 575447097876b5cf3dfbae8fa1f6f749819e97b8 Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 26 Sep 2014 00:33:07 -0400 Subject: implemented mark-as-deobfuscated and reset-to-obfuscated --- src/cuchaz/enigma/Deobfuscator.java | 68 +++++++++++++++--- src/cuchaz/enigma/gui/Gui.java | 63 ++++++++++++++--- src/cuchaz/enigma/gui/GuiController.java | 18 +++++ src/cuchaz/enigma/mapping/ClassMapping.java | 62 ++++++++++++---- src/cuchaz/enigma/mapping/Mappings.java | 11 +++ src/cuchaz/enigma/mapping/MappingsRenamer.java | 97 ++++++++++++++++++++++++++ src/cuchaz/enigma/mapping/MethodMapping.java | 6 ++ 7 files changed, 296 insertions(+), 29 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 9a339176..44845ba2 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -508,23 +508,19 @@ public class Deobfuscator Translator translator = getTranslator( TranslationDirection.Deobfuscating ); if( obfEntry instanceof ClassEntry ) { - String deobfName = translator.translate( (ClassEntry)obfEntry ); - return deobfName != null && !deobfName.equals( obfEntry.getName() ); + return translator.translate( (ClassEntry)obfEntry ) != null; } else if( obfEntry instanceof FieldEntry ) { - String deobfName = translator.translate( (FieldEntry)obfEntry ); - return deobfName != null && !deobfName.equals( obfEntry.getName() ); + return translator.translate( (FieldEntry)obfEntry ) != null; } else if( obfEntry instanceof MethodEntry ) { - String deobfName = translator.translate( (MethodEntry)obfEntry ); - return deobfName != null && !deobfName.equals( obfEntry.getName() ); + return translator.translate( (MethodEntry)obfEntry ) != null; } else if( obfEntry instanceof ConstructorEntry ) { - String deobfName = translator.translate( obfEntry.getClassEntry() ); - return deobfName != null && !deobfName.equals( obfEntry.getClassName() ); + return translator.translate( obfEntry.getClassEntry() ) != null; } else if( obfEntry instanceof ArgumentEntry ) { @@ -540,4 +536,60 @@ public class Deobfuscator { return m_jarIndex.containsObfEntry( obfEntry ); } + + public void removeMapping( Entry obfEntry ) + { + if( obfEntry instanceof ClassEntry ) + { + m_renamer.removeClassMapping( (ClassEntry)obfEntry ); + } + else if( obfEntry instanceof FieldEntry ) + { + m_renamer.removeFieldMapping( (FieldEntry)obfEntry ); + } + else if( obfEntry instanceof MethodEntry ) + { + m_renamer.removeMethodTreeMapping( (MethodEntry)obfEntry ); + } + else if( obfEntry instanceof ConstructorEntry ) + { + m_renamer.removeClassMapping( obfEntry.getClassEntry() ); + } + else if( obfEntry instanceof ArgumentEntry ) + { + m_renamer.removeArgumentMapping( (ArgumentEntry)obfEntry ); + } + else + { + throw new Error( "Unknown entry type: " + obfEntry ); + } + } + + public void markAsDeobfuscated( Entry obfEntry ) + { + if( obfEntry instanceof ClassEntry ) + { + m_renamer.markClassAsDeobfuscated( (ClassEntry)obfEntry ); + } + else if( obfEntry instanceof FieldEntry ) + { + m_renamer.markFieldAsDeobfuscated( (FieldEntry)obfEntry ); + } + else if( obfEntry instanceof MethodEntry ) + { + m_renamer.markMethodTreeAsDeobfuscated( (MethodEntry)obfEntry ); + } + else if( obfEntry instanceof ConstructorEntry ) + { + m_renamer.markClassAsDeobfuscated( obfEntry.getClassEntry() ); + } + else if( obfEntry instanceof ArgumentEntry ) + { + m_renamer.markArgumentAsDeobfuscated( (ArgumentEntry)obfEntry ); + } + else + { + throw new Error( "Unknown entry type: " + obfEntry ); + } + } } diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 1f04aa35..8bf6ce95 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -122,6 +122,7 @@ public class Gui private JMenuItem m_openPreviousMenu; private JMenuItem m_showCallsMenu; private JMenuItem m_showImplementationsMenu; + private JMenuItem m_toggleMappingMenu; // state private EntryReference m_reference; @@ -136,17 +137,20 @@ public class Gui final Container pane = m_frame.getContentPane(); pane.setLayout( new BorderLayout() ); - // install a global exception handler to the event thread - CrashDialog.init( m_frame ); - Thread.setDefaultUncaughtExceptionHandler( new UncaughtExceptionHandler( ) + if( Boolean.parseBoolean( System.getProperty( "enigma.catchExceptions", "true" ) ) ) { - @Override - public void uncaughtException( Thread thread, Throwable ex ) + // install a global exception handler to the event thread + CrashDialog.init( m_frame ); + Thread.setDefaultUncaughtExceptionHandler( new UncaughtExceptionHandler( ) { - ex.printStackTrace( System.err ); - CrashDialog.show( ex ); - } - } ); + @Override + public void uncaughtException( Thread thread, Throwable ex ) + { + ex.printStackTrace( System.err ); + CrashDialog.show( ex ); + } + } ); + } m_controller = new GuiController( this ); @@ -251,6 +255,10 @@ public class Gui case KeyEvent.VK_C: m_showCallsMenu.doClick(); break; + + case KeyEvent.VK_T: + m_toggleMappingMenu.doClick(); + break; } } } ); @@ -352,6 +360,21 @@ public class Gui popupMenu.add( menu ); m_openPreviousMenu = menu; } + { + JMenuItem menu = new JMenuItem( "Mark as deobfuscated" ); + menu.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + toggleMapping(); + } + } ); + menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_T, 0 ) ); + menu.setEnabled( false ); + popupMenu.add( menu ); + m_toggleMappingMenu = menu; + } // init inheritance panel m_inheritanceTree = new JTree(); @@ -1031,6 +1054,16 @@ public class Gui m_showCallsMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ); m_openEntryMenu.setEnabled( isInJar && ( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ) ); m_openPreviousMenu.setEnabled( m_controller.hasPreviousLocation() ); + m_toggleMappingMenu.setEnabled( isInJar && isToken ); + + if( isToken && m_controller.entryHasMapping( m_reference.entry ) ) + { + m_toggleMappingMenu.setText( "Reset to obfuscated" ); + } + else + { + m_toggleMappingMenu.setText( "Mark as deobfuscated" ); + } } private void navigateTo( Entry entry ) @@ -1233,6 +1266,18 @@ public class Gui redraw(); } + private void toggleMapping() + { + if( m_controller.entryHasMapping( m_reference.entry ) ) + { + m_controller.removeMapping( m_reference ); + } + else + { + m_controller.markAsDeobfuscated( m_reference ); + } + } + private TreePath getPathToRoot( TreeNode node ) { List nodes = Lists.newArrayList(); diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 098e065d..3adaf91d 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -249,6 +249,24 @@ public class GuiController refreshCurrentClass( obfReference ); } + public void removeMapping( EntryReference deobfReference ) + { + EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); + m_deobfuscator.removeMapping( obfReference.entry ); + m_isDirty = true; + refreshClasses(); + refreshCurrentClass( obfReference ); + } + + public void markAsDeobfuscated( EntryReference deobfReference ) + { + EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); + m_deobfuscator.markAsDeobfuscated( obfReference.entry ); + m_isDirty = true; + refreshClasses(); + refreshCurrentClass( obfReference ); + } + public void openDeclaration( Entry deobfEntry ) { if( deobfEntry == null ) diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index b551d71c..88106dfa 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -79,6 +79,17 @@ public class ClassMapping implements Serializable, Comparable } } + public void removeInnerClassMapping( ClassMapping classMapping ) + { + boolean obfWasRemoved = m_innerClassesByObf.remove( classMapping.getObfName() ) != null; + assert( obfWasRemoved ); + if( classMapping.getDeobfName() != null ) + { + boolean deobfWasRemoved = m_innerClassesByDeobf.remove( classMapping.getDeobfName() ) != null; + assert( deobfWasRemoved ); + } + } + public ClassMapping getOrCreateInnerClass( String obfName ) { ClassMapping classMapping = m_innerClassesByObf.get( obfName ); @@ -124,11 +135,17 @@ public class ClassMapping implements Serializable, Comparable public void setInnerClassName( String obfName, String deobfName ) { ClassMapping classMapping = getOrCreateInnerClass( obfName ); - boolean wasRemoved = m_innerClassesByDeobf.remove( classMapping.getDeobfName() ) != null; - assert( wasRemoved ); + if( classMapping.getDeobfName() != null ) + { + boolean wasRemoved = m_innerClassesByDeobf.remove( classMapping.getDeobfName() ) != null; + assert( wasRemoved ); + } classMapping.setDeobfName( deobfName ); - boolean wasAdded = m_innerClassesByDeobf.put( deobfName, classMapping ) == null; - assert( wasAdded ); + if( deobfName != null ) + { + boolean wasAdded = m_innerClassesByDeobf.put( deobfName, classMapping ) == null; + assert( wasAdded ); + } } //// FIELDS //////// @@ -176,6 +193,16 @@ public class ClassMapping implements Serializable, Comparable assert( deobfWasRemoved ); } } + + public FieldMapping getFieldByObf( String obfName ) + { + return m_fieldsByObf.get( obfName ); + } + + public FieldMapping getFieldByDeobf( String deobfName ) + { + return m_fieldsByDeobf.get( deobfName ); + } public String getObfFieldName( String deobfName ) { @@ -212,8 +239,11 @@ public class ClassMapping implements Serializable, Comparable assert( wasRemoved ); } fieldMapping.setDeobfName( deobfName ); - boolean wasAdded = m_fieldsByDeobf.put( deobfName, fieldMapping ) == null; - assert( wasAdded ); + if( deobfName != null ) + { + boolean wasAdded = m_fieldsByDeobf.put( deobfName, fieldMapping ) == null; + assert( wasAdded ); + } } //// METHODS //////// @@ -303,20 +333,28 @@ public class ClassMapping implements Serializable, Comparable assert( wasRemoved ); } methodMapping.setDeobfName( deobfName ); - boolean wasAdded = m_methodsByDeobf.put( getMethodKey( deobfName, obfSignature ), methodMapping ) == null; - assert( wasAdded ); + if( deobfName != null ) + { + boolean wasAdded = m_methodsByDeobf.put( getMethodKey( deobfName, obfSignature ), methodMapping ) == null; + assert( wasAdded ); + } } //// ARGUMENTS //////// public void setArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName ) { - MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ); - if( methodIndex == null ) + MethodMapping methodMapping = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ); + if( methodMapping == null ) { - methodIndex = createMethodMapping( obfMethodName, obfMethodSignature ); + methodMapping = createMethodMapping( obfMethodName, obfMethodSignature ); } - methodIndex.setArgumentName( argumentIndex, argumentName ); + methodMapping.setArgumentName( argumentIndex, argumentName ); + } + + public void removeArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex ) + { + m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ).removeArgumentName( argumentIndex ); } private MethodMapping createMethodMapping( String obfName, String obfSignature ) diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 0b4e7f3c..45b41bcd 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -94,6 +94,17 @@ public class Mappings implements Serializable } } + public void removeClassMapping( ClassMapping classMapping ) + { + boolean obfWasRemoved = m_classesByObf.remove( classMapping.getObfName() ) != null; + assert( obfWasRemoved ); + if( classMapping.getDeobfName() != null ) + { + boolean deobfWasRemoved = m_classesByDeobf.remove( classMapping.getDeobfName() ) != null; + assert( deobfWasRemoved ); + } + } + public ClassMapping getClassByObf( ClassEntry entry ) { return getClassByObf( entry.getName() ); diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index 182bbd19..957b6d68 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -57,6 +57,36 @@ public class MappingsRenamer } } + public void removeClassMapping( ClassEntry obf ) + { + ClassMapping classMapping = getClassMapping( obf ); + if( obf.isInnerClass() ) + { + classMapping.setInnerClassName( obf.getName(), null ); + } + else + { + boolean wasRemoved = m_mappings.m_classesByDeobf.remove( classMapping.getDeobfName() ) != null; + assert( wasRemoved ); + classMapping.setDeobfName( null ); + } + } + + public void markClassAsDeobfuscated( ClassEntry obf ) + { + ClassMapping classMapping = getOrCreateClassMapping( obf ); + if( obf.isInnerClass() ) + { + classMapping.setInnerClassName( obf.getName(), obf.getName() ); + } + else + { + classMapping.setDeobfName( obf.getName() ); + boolean wasAdded = m_mappings.m_classesByDeobf.put( obf.getName(), classMapping ) == null; + assert( wasAdded ); + } + } + public void setFieldName( FieldEntry obf, String deobfName ) { deobfName = NameValidator.validateFieldName( deobfName ); @@ -70,6 +100,18 @@ public class MappingsRenamer classMapping.setFieldName( obf.getName(), deobfName ); } + public void removeFieldMapping( FieldEntry obf ) + { + ClassMapping classMapping = getClassMappingOrInnerClassMapping( obf.getClassEntry() ); + classMapping.setFieldName( obf.getName(), null ); + } + + public void markFieldAsDeobfuscated( FieldEntry obf ) + { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); + classMapping.setFieldName( obf.getName(), obf.getName() ); + } + public void setMethodTreeName( MethodEntry obf, String deobfName ) { Set implementations = m_index.getRelatedMethodImplementations( obf ); @@ -106,6 +148,34 @@ public class MappingsRenamer classMapping.setMethodName( obf.getName(), obf.getSignature(), deobfName ); } + public void removeMethodTreeMapping( MethodEntry obf ) + { + for( MethodEntry implementation : m_index.getRelatedMethodImplementations( obf ) ) + { + removeMethodMapping( implementation ); + } + } + + public void removeMethodMapping( MethodEntry obf ) + { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); + classMapping.setMethodName( obf.getName(), obf.getSignature(), null ); + } + + public void markMethodTreeAsDeobfuscated( MethodEntry obf ) + { + for( MethodEntry implementation : m_index.getRelatedMethodImplementations( obf ) ) + { + markMethodAsDeobfuscated( implementation ); + } + } + + public void markMethodAsDeobfuscated( MethodEntry obf ) + { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); + classMapping.setMethodName( obf.getName(), obf.getSignature(), obf.getName() ); + } + public void setArgumentName( ArgumentEntry obf, String deobfName ) { deobfName = NameValidator.validateArgumentName( deobfName ); @@ -119,6 +189,18 @@ public class MappingsRenamer classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName ); } + public void removeArgumentMapping( ArgumentEntry obf ) + { + ClassMapping classMapping = getClassMappingOrInnerClassMapping( obf.getClassEntry() ); + classMapping.removeArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex() ); + } + + public void markArgumentAsDeobfuscated( ArgumentEntry obf ) + { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); + classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName() ); + } + public boolean moveFieldToObfClass( ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass ) { classMapping.removeFieldMapping( fieldMapping ); @@ -167,6 +249,11 @@ public class MappingsRenamer gzipout.finish(); } + private ClassMapping getClassMapping( ClassEntry obfClassEntry ) + { + return m_mappings.m_classesByObf.get( obfClassEntry.getOuterClassName() ); + } + private ClassMapping getOrCreateClassMapping( ClassEntry obfClassEntry ) { String obfClassName = obfClassEntry.getOuterClassName(); @@ -180,6 +267,16 @@ public class MappingsRenamer return classMapping; } + private ClassMapping getClassMappingOrInnerClassMapping( ClassEntry obfClassEntry ) + { + ClassMapping classMapping = getClassMapping( obfClassEntry ); + if( obfClassEntry.isInDefaultPackage() ) + { + classMapping = classMapping.getInnerClassByObf( obfClassEntry.getInnerClassName() ); + } + return classMapping; + } + private ClassMapping getOrCreateClassMappingOrInnerClassMapping( ClassEntry obfClassEntry ) { ClassMapping classMapping = getOrCreateClassMapping( obfClassEntry ); diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index b076fa33..c51b0110 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -118,6 +118,12 @@ public class MethodMapping implements Serializable, Comparable } } + public void removeArgumentName( int index ) + { + boolean wasRemoved = m_arguments.remove( index ) != null; + assert( wasRemoved ); + } + @Override public String toString( ) { -- cgit v1.2.3 From cf3ffcee30083a71e68e3edb9ecbb936cc255992 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 28 Sep 2014 15:20:54 -0400 Subject: added proper support for renaming constructors --- src/cuchaz/enigma/Deobfuscator.java | 70 +++++++++++++--------- src/cuchaz/enigma/analysis/EntryReference.java | 43 +++++++++++-- src/cuchaz/enigma/analysis/JarIndex.java | 7 ++- src/cuchaz/enigma/analysis/SourceIndex.java | 29 ++++++--- .../analysis/SourceIndexBehaviorVisitor.java | 27 ++------- .../enigma/analysis/SourceIndexClassVisitor.java | 6 +- src/cuchaz/enigma/analysis/Token.java | 10 ++++ src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 2 +- src/cuchaz/enigma/gui/Gui.java | 15 ++--- src/cuchaz/enigma/gui/GuiController.java | 34 +++++++---- test/cuchaz/enigma/EntryFactory.java | 8 +-- 11 files changed, 159 insertions(+), 92 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 44845ba2..ff83d21a 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -40,6 +40,7 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.analysis.TreeDumpVisitor; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.BehaviorEntryFactory; @@ -337,7 +338,7 @@ public class Deobfuscator // DEBUG //sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); - + // resolve all the classes in the source references for( Token token : index.referenceTokens() ) { @@ -454,7 +455,8 @@ public class Deobfuscator } return new EntryReference( obfuscateEntry( deobfReference.entry ), - obfuscateEntry( deobfReference.context ) + obfuscateEntry( deobfReference.context ), + deobfReference ); } @@ -466,75 +468,83 @@ public class Deobfuscator } return new EntryReference( deobfuscateEntry( obfReference.entry ), - deobfuscateEntry( obfReference.context ) + deobfuscateEntry( obfReference.context ), + obfReference ); } + public boolean isObfuscatedIdentifier( Entry obfEntry ) + { + return m_jarIndex.containsObfEntry( obfEntry ); + } + + public boolean isRenameable( EntryReference obfReference ) + { + return obfReference.isNamed() && isObfuscatedIdentifier( obfReference.getNameableEntry() ); + } + + // NOTE: these methods are a bit messy... oh well - public void rename( Entry obfEntry, String newName ) + public boolean hasDeobfuscatedName( Entry obfEntry ) { + Translator translator = getTranslator( TranslationDirection.Deobfuscating ); if( obfEntry instanceof ClassEntry ) { - m_renamer.setClassName( (ClassEntry)obfEntry, Descriptor.toJvmName( newName ) ); + return translator.translate( (ClassEntry)obfEntry ) != null; } else if( obfEntry instanceof FieldEntry ) { - m_renamer.setFieldName( (FieldEntry)obfEntry, newName ); + return translator.translate( (FieldEntry)obfEntry ) != null; } else if( obfEntry instanceof MethodEntry ) { - m_renamer.setMethodTreeName( (MethodEntry)obfEntry, newName ); + return translator.translate( (MethodEntry)obfEntry ) != null; } else if( obfEntry instanceof ConstructorEntry ) { - m_renamer.setClassName( obfEntry.getClassEntry(), newName ); + // constructors have no names + return false; } else if( obfEntry instanceof ArgumentEntry ) { - m_renamer.setArgumentName( (ArgumentEntry)obfEntry, newName ); + return translator.translate( (ArgumentEntry)obfEntry ) != null; } else { throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); } - - // clear caches - m_translatorCache.clear(); } - - public boolean hasMapping( Entry obfEntry ) + + public void rename( Entry obfEntry, String newName ) { - Translator translator = getTranslator( TranslationDirection.Deobfuscating ); if( obfEntry instanceof ClassEntry ) { - return translator.translate( (ClassEntry)obfEntry ) != null; + m_renamer.setClassName( (ClassEntry)obfEntry, Descriptor.toJvmName( newName ) ); } else if( obfEntry instanceof FieldEntry ) { - return translator.translate( (FieldEntry)obfEntry ) != null; + m_renamer.setFieldName( (FieldEntry)obfEntry, newName ); } else if( obfEntry instanceof MethodEntry ) { - return translator.translate( (MethodEntry)obfEntry ) != null; + m_renamer.setMethodTreeName( (MethodEntry)obfEntry, newName ); } else if( obfEntry instanceof ConstructorEntry ) { - return translator.translate( obfEntry.getClassEntry() ) != null; + throw new IllegalArgumentException( "Cannot rename constructors" ); } else if( obfEntry instanceof ArgumentEntry ) { - return translator.translate( (ArgumentEntry)obfEntry ) != null; + m_renamer.setArgumentName( (ArgumentEntry)obfEntry, newName ); } else { throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); } - } - - public boolean isObfuscatedIdentifier( Entry obfEntry ) - { - return m_jarIndex.containsObfEntry( obfEntry ); + + // clear caches + m_translatorCache.clear(); } public void removeMapping( Entry obfEntry ) @@ -553,7 +563,7 @@ public class Deobfuscator } else if( obfEntry instanceof ConstructorEntry ) { - m_renamer.removeClassMapping( obfEntry.getClassEntry() ); + throw new IllegalArgumentException( "Cannot rename constructors" ); } else if( obfEntry instanceof ArgumentEntry ) { @@ -563,6 +573,9 @@ public class Deobfuscator { throw new Error( "Unknown entry type: " + obfEntry ); } + + // clear caches + m_translatorCache.clear(); } public void markAsDeobfuscated( Entry obfEntry ) @@ -581,7 +594,7 @@ public class Deobfuscator } else if( obfEntry instanceof ConstructorEntry ) { - m_renamer.markClassAsDeobfuscated( obfEntry.getClassEntry() ); + throw new IllegalArgumentException( "Cannot rename constructors" ); } else if( obfEntry instanceof ArgumentEntry ) { @@ -591,5 +604,8 @@ public class Deobfuscator { throw new Error( "Unknown entry type: " + obfEntry ); } + + // clear caches + m_translatorCache.clear(); } } diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java index 768c1132..df977fb5 100644 --- a/src/cuchaz/enigma/analysis/EntryReference.java +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -10,21 +10,28 @@ ******************************************************************************/ package cuchaz.enigma.analysis; +import java.util.Arrays; +import java.util.List; + import cuchaz.enigma.Util; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; public class EntryReference { + private static final List ConstructorNonNames = Arrays.asList( "this", "super" ); public E entry; public C context; - public EntryReference( E entry ) + private boolean m_isNamed; + + public EntryReference( E entry, String sourceName ) { - this( entry, null ); + this( entry, sourceName, null ); } - public EntryReference( E entry, C context ) + public EntryReference( E entry, String sourceName, C context ) { if( entry == null ) { @@ -33,9 +40,22 @@ public class EntryReference this.entry = entry; this.context = context; + + m_isNamed = sourceName != null && sourceName.length() > 0; + if( entry instanceof ConstructorEntry && ConstructorNonNames.contains( sourceName ) ) + { + m_isNamed = false; + } + } + + public EntryReference( E entry, C context, EntryReference other ) + { + this.entry = entry; + this.context = context; + m_isNamed = other.m_isNamed; } - public ClassEntry getClassEntry( ) + public ClassEntry getLocationClassEntry( ) { if( context != null ) { @@ -44,6 +64,21 @@ public class EntryReference return entry.getClassEntry(); } + public boolean isNamed( ) + { + return m_isNamed; + } + + public Entry getNameableEntry( ) + { + if( entry instanceof ConstructorEntry ) + { + // renaming a constructor really means renaming the class + return entry.getClassEntry(); + } + return entry; + } + @Override public int hashCode( ) { diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index a2f6bf34..604e4853 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -277,6 +277,7 @@ public class JarIndex } EntryReference reference = new EntryReference( calledMethodEntry, + call.getMethodName(), behaviorEntry ); m_behaviorReferences.put( calledMethodEntry, reference ); @@ -300,6 +301,7 @@ public class JarIndex } EntryReference reference = new EntryReference( calledFieldEntry, + call.getFieldName(), behaviorEntry ); m_fieldReferences.put( calledFieldEntry, reference ); @@ -308,9 +310,6 @@ public class JarIndex @Override public void edit( ConstructorCall call ) { - // TODO: save isSuper in the reference somehow - boolean isSuper = call.getMethodName().equals( "super" ); - String className = Descriptor.toJvmName( call.getClassName() ); ConstructorEntry calledConstructorEntry = new ConstructorEntry( new ClassEntry( className ), @@ -318,6 +317,7 @@ public class JarIndex ); EntryReference reference = new EntryReference( calledConstructorEntry, + call.getMethodName(), behaviorEntry ); m_behaviorReferences.put( calledConstructorEntry, reference ); @@ -333,6 +333,7 @@ public class JarIndex ); EntryReference reference = new EntryReference( calledConstructorEntry, + call.getClassName(), behaviorEntry ); m_behaviorReferences.put( calledConstructorEntry, reference ); diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 38d10daa..faae1a14 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -21,6 +21,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.strobel.decompiler.languages.Region; import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.Identifier; import cuchaz.enigma.mapping.Entry; @@ -58,40 +59,54 @@ public class SourceIndex public Token getToken( AstNode node ) { + // get the text of the node + String name = ""; + if( node instanceof Identifier ) + { + name = ((Identifier)node).getName(); + } + // get a token for this node's region Region region = node.getRegion(); if( region.getBeginLine() == 0 || region.getEndLine() == 0 ) { // DEBUG - //System.err.println( "WARNING: " + node.getNodeType() + " node has invalid region: " + region ); + System.err.println( String.format( "WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region ) ); return null; } Token token = new Token( toPos( region.getBeginLine(), region.getBeginColumn() ), - toPos( region.getEndLine(), region.getEndColumn() ) + toPos( region.getEndLine(), region.getEndColumn() ), + m_source ); if( token.start == 0 ) { // DEBUG - //System.err.println( "WARNING: " + node.getNodeType() + " node has invalid start: " + region ); + System.err.println( String.format( "WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region ) ); return null; } + // DEBUG + //System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); + + /* TODO: double check that we still need this // for tokens representing inner classes, make sure we only get the simple name - int pos = node.toString().lastIndexOf( '$' ); + int pos = node.getText().lastIndexOf( '$' ); if( pos >= 0 ) { token.end -= pos + 1; } + */ return token; } - public void addReference( AstNode node, EntryReference deobfReference ) + public void addReference( AstNode node, Entry deobfEntry, Entry deobfContext ) { Token token = getToken( node ); if( token != null ) { + EntryReference deobfReference = new EntryReference( deobfEntry, token.text, deobfContext ); m_tokenToReference.put( token, deobfReference ); m_referenceToTokens.put( deobfReference, token ); } @@ -102,7 +117,7 @@ public class SourceIndex Token token = getToken( node ); if( token != null ) { - EntryReference reference = new EntryReference( deobfEntry ); + EntryReference reference = new EntryReference( deobfEntry, token.text ); m_tokenToReference.put( token, reference ); m_referenceToTokens.put( reference, token ); m_declarationToToken.put( deobfEntry, token ); @@ -111,7 +126,7 @@ public class SourceIndex public Token getReferenceToken( int pos ) { - Token token = m_tokenToReference.floorKey( new Token( pos, pos ) ); + Token token = m_tokenToReference.floorKey( new Token( pos, pos, null ) ); if( token != null && token.contains( pos ) ) { return token; diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index f307c11d..b883087c 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -33,7 +33,6 @@ import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; -import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; @@ -100,10 +99,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor } if( tokenNode != null ) { - index.addReference( - tokenNode, - new EntryReference( behaviorEntry, m_behaviorEntry ) - ); + index.addReference( tokenNode, behaviorEntry, m_behaviorEntry ); } } @@ -124,10 +120,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); - index.addReference( - node.getMemberNameToken(), - new EntryReference( fieldEntry, m_behaviorEntry ) - ); + index.addReference( node.getMemberNameToken(), fieldEntry, m_behaviorEntry ); } return recurse( node, index ); @@ -140,10 +133,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor if( node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY ) { ClassEntry classEntry = new ClassEntry( ref.getInternalName() ); - index.addReference( - node.getIdentifierToken(), - new EntryReference( classEntry, m_behaviorEntry ) - ); + index.addReference( node.getIdentifierToken(), classEntry, m_behaviorEntry ); } return recurse( node, index ); @@ -178,10 +168,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); - index.addReference( - node.getIdentifierToken(), - new EntryReference( fieldEntry, m_behaviorEntry ) - ); + index.addReference( node.getIdentifierToken(), fieldEntry, m_behaviorEntry ); } return recurse( node, index ); @@ -197,10 +184,8 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, ref.getSignature() ); if( node.getType() instanceof SimpleType ) { - index.addReference( - ((SimpleType)node.getType()).getIdentifierToken(), - new EntryReference( constructorEntry, m_behaviorEntry ) - ); + SimpleType simpleTypeNode = (SimpleType)node.getType(); + index.addReference( simpleTypeNode.getIdentifierToken(), constructorEntry, m_behaviorEntry ); } } diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 5d8a3833..fc8cd665 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -28,7 +28,6 @@ import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; -import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; public class SourceIndexClassVisitor extends SourceIndexVisitor @@ -63,10 +62,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor if( node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY ) { ClassEntry classEntry = new ClassEntry( ref.getInternalName() ); - index.addReference( - node.getIdentifierToken(), - new EntryReference( classEntry, m_classEntry ) - ); + index.addReference( node.getIdentifierToken(), classEntry, m_classEntry ); } return recurse( node, index ); diff --git a/src/cuchaz/enigma/analysis/Token.java b/src/cuchaz/enigma/analysis/Token.java index d0f2b70b..5e70db71 100644 --- a/src/cuchaz/enigma/analysis/Token.java +++ b/src/cuchaz/enigma/analysis/Token.java @@ -14,11 +14,21 @@ public class Token implements Comparable { public int start; public int end; + public String text; public Token( int start, int end ) + { + this( start, end, null ); + } + + public Token( int start, int end, String source ) { this.start = start; this.end = end; + if( source != null ) + { + this.text = source.substring( start, end ); + } } public boolean contains( int pos ) diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java index 12febefd..e6ecb10e 100644 --- a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java +++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -141,7 +141,7 @@ public class TreeDumpVisitor implements IAstVisitor { if( node instanceof Identifier ) { - return "\"" + node.getText() + "\""; + return "\"" + ((Identifier)node).getName() + "\""; } return ""; } diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 8bf6ce95..920bc0b9 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -1038,6 +1038,7 @@ public class Gui boolean isMethodEntry = isToken && m_reference.entry instanceof MethodEntry; boolean isConstructorEntry = isToken && m_reference.entry instanceof ConstructorEntry; boolean isInJar = isToken && m_controller.entryIsInJar( m_reference.entry ); + boolean isRenameable = isToken && m_controller.referenceIsRenameable( m_reference ); if( isToken ) { @@ -1048,15 +1049,15 @@ public class Gui clearReference(); } - m_renameMenu.setEnabled( isInJar && isToken ); + m_renameMenu.setEnabled( isRenameable && isToken ); m_showInheritanceMenu.setEnabled( isClassEntry || isMethodEntry || isConstructorEntry ); m_showImplementationsMenu.setEnabled( isClassEntry || isMethodEntry ); m_showCallsMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ); m_openEntryMenu.setEnabled( isInJar && ( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ) ); m_openPreviousMenu.setEnabled( m_controller.hasPreviousLocation() ); - m_toggleMappingMenu.setEnabled( isInJar && isToken ); + m_toggleMappingMenu.setEnabled( isRenameable && isToken ); - if( isToken && m_controller.entryHasMapping( m_reference.entry ) ) + if( isToken && m_controller.entryHasDeobfuscatedName( m_reference.entry ) ) { m_toggleMappingMenu.setText( "Reset to obfuscated" ); } @@ -1082,7 +1083,7 @@ public class Gui private void navigateTo( EntryReference reference ) { - if( !m_controller.entryIsInJar( reference.getClassEntry() ) ) + if( !m_controller.entryIsInJar( reference.getLocationClassEntry() ) ) { // reference is not in the jar. Ignore it return; @@ -1098,7 +1099,7 @@ public class Gui { // init the text box final JTextField text = new JTextField(); - text.setText( m_reference.entry.getName() ); + text.setText( m_reference.getNameableEntry().getName() ); text.setPreferredSize( new Dimension( 360, text.getPreferredSize().height ) ); text.addKeyListener( new KeyAdapter( ) { @@ -1149,7 +1150,7 @@ public class Gui // abort the rename JPanel panel = (JPanel)m_infoPanel.getComponent( 0 ); panel.remove( panel.getComponentCount() - 1 ); - panel.add( GuiTricks.unboldLabel( new JLabel( m_reference.entry.getName(), JLabel.LEFT ) ) ); + panel.add( GuiTricks.unboldLabel( new JLabel( m_reference.getNameableEntry().getName(), JLabel.LEFT ) ) ); m_editor.grabFocus(); @@ -1268,7 +1269,7 @@ public class Gui private void toggleMapping() { - if( m_controller.entryHasMapping( m_reference.entry ) ) + if( m_controller.entryHasDeobfuscatedName( m_reference.entry ) ) { m_controller.removeMapping( m_reference ); } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 3adaf91d..c7efbce6 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -165,9 +165,9 @@ public class GuiController ); } - public boolean entryHasMapping( Entry deobfEntry ) + public boolean entryHasDeobfuscatedName( Entry deobfEntry ) { - return m_deobfuscator.hasMapping( m_deobfuscator.obfuscateEntry( deobfEntry ) ); + return m_deobfuscator.hasDeobfuscatedName( m_deobfuscator.obfuscateEntry( deobfEntry ) ); } public boolean entryIsInJar( Entry deobfEntry ) @@ -175,6 +175,11 @@ public class GuiController return m_deobfuscator.isObfuscatedIdentifier( m_deobfuscator.obfuscateEntry( deobfEntry ) ); } + public boolean referenceIsRenameable( EntryReference deobfReference ) + { + return m_deobfuscator.isRenameable( m_deobfuscator.obfuscateReference( deobfReference ) ); + } + public ClassInheritanceTreeNode getClassInheritance( ClassEntry deobfClassEntry ) { ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry( deobfClassEntry ); @@ -243,7 +248,7 @@ public class GuiController public void rename( EntryReference deobfReference, String newName ) { EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); - m_deobfuscator.rename( obfReference.entry, newName ); + m_deobfuscator.rename( obfReference.getNameableEntry(), newName ); m_isDirty = true; refreshClasses(); refreshCurrentClass( obfReference ); @@ -252,7 +257,7 @@ public class GuiController public void removeMapping( EntryReference deobfReference ) { EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); - m_deobfuscator.removeMapping( obfReference.entry ); + m_deobfuscator.removeMapping( obfReference.getNameableEntry() ); m_isDirty = true; refreshClasses(); refreshCurrentClass( obfReference ); @@ -261,7 +266,7 @@ public class GuiController public void markAsDeobfuscated( EntryReference deobfReference ) { EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); - m_deobfuscator.markAsDeobfuscated( obfReference.entry ); + m_deobfuscator.markAsDeobfuscated( obfReference.getNameableEntry() ); m_isDirty = true; refreshClasses(); refreshCurrentClass( obfReference ); @@ -273,7 +278,7 @@ public class GuiController { throw new IllegalArgumentException( "Entry cannot be null!" ); } - openReference( new EntryReference( deobfEntry ) ); + openReference( new EntryReference( deobfEntry, deobfEntry.getName() ) ); } public void openReference( EntryReference deobfReference ) @@ -285,7 +290,7 @@ public class GuiController // get the reference target class EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); - ClassEntry obfClassEntry = obfReference.getClassEntry().getOuterClassEntry(); + ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOuterClassEntry(); if( !m_deobfuscator.isObfuscatedIdentifier( obfClassEntry ) ) { throw new IllegalArgumentException( "Obfuscated class " + obfClassEntry + " was not found in the jar!" ); @@ -390,13 +395,16 @@ public class GuiController for( Token token : m_index.referenceTokens() ) { EntryReference reference = m_index.getDeobfReference( token ); - if( entryHasMapping( reference.entry ) ) - { - deobfuscatedTokens.add( token ); - } - else if( entryIsInJar( reference.entry ) ) + if( referenceIsRenameable( reference ) ) { - obfuscatedTokens.add( token ); + if( entryHasDeobfuscatedName( reference.getNameableEntry() ) ) + { + deobfuscatedTokens.add( token ); + } + else + { + obfuscatedTokens.add( token ); + } } else { diff --git a/test/cuchaz/enigma/EntryFactory.java b/test/cuchaz/enigma/EntryFactory.java index 66f83dfd..5a8a4270 100644 --- a/test/cuchaz/enigma/EntryFactory.java +++ b/test/cuchaz/enigma/EntryFactory.java @@ -42,21 +42,21 @@ public class EntryFactory public static EntryReference newFieldReferenceByMethod( FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature ) { - return new EntryReference( fieldEntry, newMethod( callerClassName, callerName, callerSignature ) ); + return new EntryReference( fieldEntry, "", newMethod( callerClassName, callerName, callerSignature ) ); } public static EntryReference newFieldReferenceByConstructor( FieldEntry fieldEntry, String callerClassName, String callerSignature ) { - return new EntryReference( fieldEntry, newConstructor( callerClassName, callerSignature ) ); + return new EntryReference( fieldEntry, "", newConstructor( callerClassName, callerSignature ) ); } public static EntryReference newBehaviorReferenceByMethod( BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature ) { - return new EntryReference( behaviorEntry, newMethod( callerClassName, callerName, callerSignature ) ); + return new EntryReference( behaviorEntry, "", newMethod( callerClassName, callerName, callerSignature ) ); } public static EntryReference newBehaviorReferenceByConstructor( BehaviorEntry behaviorEntry, String callerClassName, String callerSignature ) { - return new EntryReference( behaviorEntry, newConstructor( callerClassName, callerSignature ) ); + return new EntryReference( behaviorEntry, "", newConstructor( callerClassName, callerSignature ) ); } } -- cgit v1.2.3 From b71adf885f9d950cd8c138e332b4bfc7c77222a8 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 28 Sep 2014 15:35:54 -0400 Subject: argument names now default to the names chosen by Procyon --- src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index b883087c..7ffd1700 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -154,7 +154,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { behaviorEntry = new MethodEntry( classEntry, methodDef.getName(), methodDef.getSignature() ); } - ArgumentEntry argumentEntry = new ArgumentEntry( behaviorEntry, def.getPosition(), def.getName() ); + ArgumentEntry argumentEntry = new ArgumentEntry( behaviorEntry, def.getPosition(), node.getName() ); index.addDeclaration( node.getNameToken(), argumentEntry ); return recurse( node, index ); -- cgit v1.2.3 From 3e9960f8a712e8590b3ab3126d823504027516da Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 28 Sep 2014 16:48:54 -0400 Subject: added jar export --- src/cuchaz/enigma/Deobfuscator.java | 67 +++++++++++++++++++++++++--- src/cuchaz/enigma/TranslatingTypeLoader.java | 59 +++++++++++++----------- src/cuchaz/enigma/gui/Gui.java | 37 ++++++++++++--- src/cuchaz/enigma/gui/GuiController.java | 37 ++++++++------- src/cuchaz/enigma/gui/ProgressDialog.java | 36 ++++++++++++--- 5 files changed, 177 insertions(+), 59 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index ff83d21a..9235cf74 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -12,14 +12,18 @@ package cuchaz.enigma; import java.io.File; +import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import javassist.CtClass; import javassist.bytecode.Descriptor; import com.google.common.collect.Lists; @@ -36,11 +40,11 @@ import com.strobel.decompiler.languages.java.ast.CompilationUnit; import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarClassIterator; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; import cuchaz.enigma.analysis.Token; -import cuchaz.enigma.analysis.TreeDumpVisitor; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.BehaviorEntryFactory; @@ -61,7 +65,7 @@ public class Deobfuscator { public interface ProgressListener { - void init( int totalWork ); + void init( int totalWork, String title ); void onProgress( int numDone, String message ); } @@ -393,7 +397,7 @@ public class Deobfuscator if( progress != null ) { - progress.init( classEntries.size() ); + progress.init( classEntries.size(), "Decompiling classes..." ); } // DEOBFUSCATE ALL THE THINGS!! @_@ @@ -424,9 +428,60 @@ public class Deobfuscator throw new Error( "Unable to deobfuscate class " + deobfClassEntry.toString() + " (" + obfClassEntry.toString() + ")", t ); } } - - // done! - progress.onProgress( classEntries.size(), "Done!" ); + if( progress != null ) + { + progress.onProgress( i, "Done!" ); + } + } + + public void writeJar( File out, ProgressListener progress ) + { + try( JarOutputStream outJar = new JarOutputStream( new FileOutputStream( out ) ) ) + { + if( progress != null ) + { + progress.init( JarClassIterator.getClassEntries( m_jar ).size(), "Translating classes..." ); + } + + // prep the loader + TranslatingTypeLoader loader = new TranslatingTypeLoader( + m_jar, + m_jarIndex, + getTranslator( TranslationDirection.Obfuscating ), + getTranslator( TranslationDirection.Deobfuscating ) + ); + + int i = 0; + for( CtClass c : JarClassIterator.classes( m_jar ) ) + { + if( progress != null ) + { + progress.onProgress( i++, c.getName() ); + } + + try + { + c = loader.transformClass( c ); + outJar.putNextEntry( new JarEntry( c.getName().replace( '.', '/' ) + ".class" ) ); + outJar.write( c.toBytecode() ); + outJar.closeEntry(); + } + catch( Throwable t ) + { + throw new Error( "Unable to deobfuscate class " + c.getName(), t ); + } + } + if( progress != null ) + { + progress.onProgress( i, "Done!" ); + } + + outJar.close(); + } + catch( IOException ex ) + { + throw new Error( "Unable to write to Jar file!" ); + } } public T obfuscateEntry( T deobfEntry ) diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 8b969857..6179879a 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -13,7 +13,6 @@ package cuchaz.enigma; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -157,12 +156,13 @@ public class TranslatingTypeLoader implements ITypeLoader // otherwise, just use the class name (ie for classes in packages) classFileName = obfClassEntry.getName(); } + JarEntry entry = m_jar.getJarEntry( classFileName + ".class" ); if( entry == null ) { return null; } - + try { // read the class file into a buffer @@ -188,33 +188,11 @@ public class TranslatingTypeLoader implements ITypeLoader classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); CtClass c = classPool.get( javaClassFileName ); - // we moved a lot of classes out of the default package into the none package - // make sure all the class references are consistent - ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); - - // reconstruct inner classes - new InnerClassWriter( m_jarIndex ).write( c ); - - // re-get the javassist handle since we changed class names - String javaClassReconstructedName = Descriptor.toJavaName( obfClassEntry.getName() ); - classPool = new ClassPool(); - classPool.insertClassPath( new ByteArrayClassPath( javaClassReconstructedName, c.toBytecode() ) ); - c = classPool.get( javaClassReconstructedName ); - - // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong) - assertClassName( c, obfClassEntry ); - - // do all kinds of deobfuscating transformations on the class - new BridgeFixer( m_jarIndex ).fixBridges( c ); - new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); - new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); + c = transformClass( c ); // sanity checking assertClassName( c, deobfClassEntry ); - // DEBUG - //Util.writeClass( c ); - // we have a transformed class! return c.toBytecode(); } @@ -223,6 +201,37 @@ public class TranslatingTypeLoader implements ITypeLoader throw new Error( ex ); } } + + public CtClass transformClass( CtClass c ) + throws IOException, NotFoundException, CannotCompileException + { + // we moved a lot of classes out of the default package into the none package + // make sure all the class references are consistent + ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); + + // reconstruct inner classes + new InnerClassWriter( m_jarIndex ).write( c ); + + // re-get the javassist handle since we changed class names + ClassEntry obfClassEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); + String javaClassReconstructedName = Descriptor.toJavaName( obfClassEntry.getName() ); + ClassPool classPool = new ClassPool(); + classPool.insertClassPath( new ByteArrayClassPath( javaClassReconstructedName, c.toBytecode() ) ); + c = classPool.get( javaClassReconstructedName ); + + // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong) + assertClassName( c, obfClassEntry ); + + // do all kinds of deobfuscating transformations on the class + new BridgeFixer( m_jarIndex ).fixBridges( c ); + new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); + new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); + + // DEBUG + //Util.writeClass( c ); + + return c; + } private void assertClassName( CtClass c, ClassEntry obfClassEntry ) { diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 920bc0b9..dbfcba83 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -123,12 +123,15 @@ public class Gui private JMenuItem m_showCallsMenu; private JMenuItem m_showImplementationsMenu; private JMenuItem m_toggleMappingMenu; + private JMenuItem m_exportSourceMenu; + private JMenuItem m_exportJarMenu; // state private EntryReference m_reference; private JFileChooser m_jarFileChooser; private JFileChooser m_mappingsFileChooser; - private JFileChooser m_exportFileChooser; + private JFileChooser m_exportSourceFileChooser; + private JFileChooser m_exportJarFileChooser; public Gui( ) { @@ -157,8 +160,9 @@ public class Gui // init file choosers m_jarFileChooser = new JFileChooser(); m_mappingsFileChooser = new JFileChooser(); - m_exportFileChooser = new JFileChooser(); - m_exportFileChooser.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY ); + m_exportSourceFileChooser = new JFileChooser(); + m_exportSourceFileChooser.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY ); + m_exportJarFileChooser = new JFileChooser(); // init obfuscated classes list m_obfClasses = new ClassSelector( ClassSelector.ObfuscatedClassEntryComparator ); @@ -663,19 +667,36 @@ public class Gui } menu.addSeparator(); { - JMenuItem item = new JMenuItem( "Export..." ); + JMenuItem item = new JMenuItem( "Export Source..." ); menu.add( item ); item.addActionListener( new ActionListener( ) { @Override public void actionPerformed( ActionEvent event ) { - if( m_exportFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) + if( m_exportSourceFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) { - m_controller.export( m_exportFileChooser.getSelectedFile() ); + m_controller.exportSource( m_exportSourceFileChooser.getSelectedFile() ); } } } ); + m_exportSourceMenu = item; + } + { + JMenuItem item = new JMenuItem( "Export Jar..." ); + menu.add( item ); + item.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + if( m_exportJarFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) + { + m_controller.exportJar( m_exportJarFileChooser.getSelectedFile() ); + } + } + } ); + m_exportJarMenu = item; } menu.addSeparator(); { @@ -762,6 +783,8 @@ public class Gui m_saveMappingsMenu.setEnabled( false ); m_saveMappingsAsMenu.setEnabled( true ); m_closeMappingsMenu.setEnabled( true ); + m_exportSourceMenu.setEnabled( true ); + m_exportJarMenu.setEnabled( true ); redraw(); } @@ -781,6 +804,8 @@ public class Gui m_saveMappingsMenu.setEnabled( false ); m_saveMappingsAsMenu.setEnabled( false ); m_closeMappingsMenu.setEnabled( false ); + m_exportSourceMenu.setEnabled( false ); + m_exportJarMenu.setEnabled( false ); redraw(); } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index c7efbce6..2862ebed 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -23,6 +23,7 @@ import com.google.common.collect.Queues; import com.strobel.decompiler.languages.java.ast.CompilationUnit; import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.Deobfuscator.ProgressListener; import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; import cuchaz.enigma.analysis.ClassImplementationsTreeNode; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; @@ -32,6 +33,7 @@ import cuchaz.enigma.analysis.MethodImplementationsTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.gui.ProgressDialog.ProgressRunnable; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; @@ -110,28 +112,29 @@ public class GuiController refreshCurrentClass(); } - public void export( final File dirOut ) + public void exportSource( final File dirOut ) { - new Thread( ) + ProgressDialog.runInThread( m_gui.getFrame(), new ProgressRunnable( ) { @Override - public void run( ) + public void run( ProgressListener progress ) + throws Exception { - ProgressDialog progress = new ProgressDialog( m_gui.getFrame() ); - try - { - m_deobfuscator.writeSources( dirOut, progress ); - } - catch( Exception ex ) - { - throw new Error( ex ); - } - finally - { - progress.close(); - } + m_deobfuscator.writeSources( dirOut, progress ); } - }.start(); + } ); + } + + public void exportJar( final File fileOut ) + { + ProgressDialog.runInThread( m_gui.getFrame(), new ProgressRunnable( ) + { + @Override + public void run( ProgressListener progress ) + { + m_deobfuscator.writeJar( fileOut, progress ); + } + } ); } public Token getToken( int pos ) diff --git a/src/cuchaz/enigma/gui/ProgressDialog.java b/src/cuchaz/enigma/gui/ProgressDialog.java index 40ac6a69..7f954314 100644 --- a/src/cuchaz/enigma/gui/ProgressDialog.java +++ b/src/cuchaz/enigma/gui/ProgressDialog.java @@ -25,22 +25,24 @@ import javax.swing.WindowConstants; import cuchaz.enigma.Constants; import cuchaz.enigma.Deobfuscator.ProgressListener; -public class ProgressDialog implements ProgressListener +public class ProgressDialog implements ProgressListener, AutoCloseable { private JFrame m_frame; + private JLabel m_title; private JLabel m_text; private JProgressBar m_progress; public ProgressDialog( JFrame parent ) { // init frame - m_frame = new JFrame( Constants.Name + " - Export" ); + m_frame = new JFrame( Constants.Name + " - Operation in progress" ); final Container pane = m_frame.getContentPane(); FlowLayout layout = new FlowLayout(); layout.setAlignment( FlowLayout.LEFT ); pane.setLayout( layout ); - pane.add( new JLabel( "Decompiling classes..." ) ); + m_title = new JLabel(); + pane.add( m_title ); // set up the progress bar JPanel panel = new JPanel(); @@ -68,9 +70,9 @@ public class ProgressDialog implements ProgressListener } @Override - public void init( int totalWork ) + public void init( int totalWork, String title ) { - m_text.setText( "Decompiling " + totalWork + " classes..." ); + m_title.setText( title ); m_progress.setMinimum( 0 ); m_progress.setMaximum( totalWork ); m_progress.setValue( 0 ); @@ -86,4 +88,28 @@ public class ProgressDialog implements ProgressListener m_frame.validate(); m_frame.repaint(); } + + public static interface ProgressRunnable + { + void run( ProgressListener listener ) throws Exception; + } + + public static void runInThread( final JFrame parent, final ProgressRunnable runnable ) + { + new Thread( ) + { + @Override + public void run( ) + { + try( ProgressDialog progress = new ProgressDialog( parent ) ) + { + runnable.run( progress ); + } + catch( Exception ex ) + { + throw new Error( ex ); + } + } + }.start(); + } } -- cgit v1.2.3 From 8bfaa26a1483e7875b649e23f045abe9d0fe9f7c Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 28 Sep 2014 19:47:51 -0400 Subject: fixed a crash parsing method signatures with generics in them --- src/cuchaz/enigma/mapping/SignatureUpdater.java | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java index d1216bde..809473e5 100644 --- a/src/cuchaz/enigma/mapping/SignatureUpdater.java +++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java @@ -69,19 +69,32 @@ public class SignatureUpdater throws IOException { // read all the characters in the buffer until we hit a ';' + // remember to treat generics correctly StringBuilder buf = new StringBuilder(); + int depth = 0; int i = -1; while( ( i = reader.read() ) != -1 ) { char c = (char)i; - if( c == ';' ) + if( c == '<' ) { - return buf.toString(); + depth++; } - else + else if( c == '>' ) { - buf.append( c ); + depth--; + } + else if( depth == 0 ) + { + if( c == ';' ) + { + return buf.toString(); + } + else + { + buf.append( c ); + } } } -- cgit v1.2.3 From fd65d9727ec0f443fd9a1504f9b50bafba1fbd76 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 28 Sep 2014 20:27:49 -0400 Subject: fixed recognition of static initializer tokens --- src/cuchaz/enigma/Deobfuscator.java | 1 + src/cuchaz/enigma/analysis/EntryReference.java | 2 +- src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java | 12 +++++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 9235cf74..bd4345e7 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -45,6 +45,7 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.analysis.TreeDumpVisitor; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.BehaviorEntryFactory; diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java index df977fb5..4da2f589 100644 --- a/src/cuchaz/enigma/analysis/EntryReference.java +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -20,7 +20,7 @@ import cuchaz.enigma.mapping.Entry; public class EntryReference { - private static final List ConstructorNonNames = Arrays.asList( "this", "super" ); + private static final List ConstructorNonNames = Arrays.asList( "this", "super", "static" ); public E entry; public C context; diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index fc8cd665..24c48227 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -15,6 +15,7 @@ import com.strobel.assembler.metadata.MethodDefinition; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.assembler.metadata.TypeReference; import com.strobel.decompiler.languages.TextLocation; +import com.strobel.decompiler.languages.java.ast.AstNode; import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; import com.strobel.decompiler.languages.java.ast.FieldDeclaration; @@ -74,7 +75,16 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); BehaviorEntry behaviorEntry = BehaviorEntryFactory.create( classEntry, def.getName(), def.getSignature() ); - index.addDeclaration( node.getNameToken(), behaviorEntry ); + AstNode tokenNode = node.getNameToken(); + if( behaviorEntry instanceof ConstructorEntry ) + { + ConstructorEntry constructorEntry = (ConstructorEntry)behaviorEntry; + if( constructorEntry.isStatic() ) + { + tokenNode = node.getModifiers().firstOrNullObject(); + } + } + index.addDeclaration( tokenNode, behaviorEntry ); return node.acceptVisitor( new SourceIndexBehaviorVisitor( behaviorEntry ), index ); } -- cgit v1.2.3 From a83bbfd5c510367a194073b1db132022cacf65ed Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 30 Sep 2014 00:25:36 -0400 Subject: fixed nasty issue with renaming inner classes, but alas, more bugs remain --- src/cuchaz/enigma/Deobfuscator.java | 3 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 10 +++--- src/cuchaz/enigma/analysis/EntryReference.java | 15 +++++++++ src/cuchaz/enigma/analysis/JarIndex.java | 7 ++-- src/cuchaz/enigma/analysis/SourceIndex.java | 2 -- src/cuchaz/enigma/bytecode/ClassRenamer.java | 31 ++++-------------- src/cuchaz/enigma/bytecode/ClassTranslator.java | 6 +++- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 20 ++++++------ src/cuchaz/enigma/gui/Gui.java | 4 +-- src/cuchaz/enigma/mapping/ClassMapping.java | 18 +++++++++-- src/cuchaz/enigma/mapping/MappingsReader.java | 41 +++++++++++++----------- src/cuchaz/enigma/mapping/MappingsRenamer.java | 6 ++-- src/cuchaz/enigma/mapping/NameValidator.java | 6 ++-- src/cuchaz/enigma/mapping/Translator.java | 17 +++++++--- 14 files changed, 108 insertions(+), 78 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index bd4345e7..d45ffb41 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -45,7 +45,6 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; import cuchaz.enigma.analysis.Token; -import cuchaz.enigma.analysis.TreeDumpVisitor; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.BehaviorEntryFactory; @@ -221,7 +220,7 @@ public class Deobfuscator String outerClassName = m_jarIndex.getOuterClass( classMapping.getObfName() ); if( outerClassName != null ) { - classEntry = new ClassEntry( outerClassName + "$" + classEntry.getSimpleName() ); + classEntry = new ClassEntry( outerClassName + "$" + classMapping.getObfName() ); } if( !m_jarIndex.getObfClassEntries().contains( classEntry ) ) { diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 6179879a..86e52a34 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -13,6 +13,7 @@ package cuchaz.enigma; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -119,7 +120,6 @@ public class TranslatingTypeLoader implements ITypeLoader private byte[] loadType( String deobfClassName ) { - // what class file should we actually load? ClassEntry deobfClassEntry = new ClassEntry( deobfClassName ); ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry( deobfClassEntry ); @@ -143,7 +143,7 @@ public class TranslatingTypeLoader implements ITypeLoader String classFileName; if( obfClassEntry.isInnerClass() ) { - // use just the inner class simple name for inner classes + // use just the inner class name for inner classes classFileName = obfClassEntry.getInnerClassName(); } else if( obfClassEntry.getPackageName().equals( Constants.NonePackage ) ) @@ -193,6 +193,9 @@ public class TranslatingTypeLoader implements ITypeLoader // sanity checking assertClassName( c, deobfClassEntry ); + // DEBUG + //Util.writeClass( c ); + // we have a transformed class! return c.toBytecode(); } @@ -227,9 +230,6 @@ public class TranslatingTypeLoader implements ITypeLoader new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); - // DEBUG - //Util.writeClass( c ); - return c; } diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java index 4da2f589..0cde8759 100644 --- a/src/cuchaz/enigma/analysis/EntryReference.java +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -79,6 +79,21 @@ public class EntryReference return entry; } + public String getNamableName( ) + { + if( getNameableEntry() instanceof ClassEntry ) + { + ClassEntry classEntry = (ClassEntry)getNameableEntry(); + if( classEntry.isInnerClass() ) + { + // make sure we only rename the inner class name + return classEntry.getInnerClassName(); + } + } + + return getNameableEntry().getName(); + } + @Override public int hashCode( ) { diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 604e4853..ba082064 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -164,9 +164,10 @@ public class JarIndex String outerClassName = findOuterClass( c ); if( outerClassName != null ) { - String innerClassName = Descriptor.toJvmName( c.getName() ); + String innerClassName = c.getSimpleName(); m_innerClasses.put( outerClassName, innerClassName ); - m_outerClasses.put( innerClassName, outerClassName ); + boolean innerWasAdded = m_outerClasses.put( innerClassName, outerClassName ) == null; + assert( innerWasAdded ); BehaviorEntry enclosingBehavior = isAnonymousClass( c, outerClassName ); if( enclosingBehavior != null ) @@ -188,7 +189,7 @@ public class JarIndex Map renames = Maps.newHashMap(); for( Map.Entry entry : m_outerClasses.entrySet() ) { - renames.put( entry.getKey(), entry.getValue() + "$" + new ClassEntry( entry.getKey() ).getSimpleName() ); + renames.put( Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey() ); } EntryRenamer.renameClassesInSet( renames, m_obfClassEntries ); m_translationIndex.renameClasses( renames ); diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index faae1a14..21a499e8 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -89,14 +89,12 @@ public class SourceIndex // DEBUG //System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); - /* TODO: double check that we still need this // for tokens representing inner classes, make sure we only get the simple name int pos = node.getText().lastIndexOf( '$' ); if( pos >= 0 ) { token.end -= pos + 1; } - */ return token; } diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index 9f0845da..849a3233 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -37,34 +37,25 @@ public class ClassRenamer { nameMap.put( entry.getKey().getName(), entry.getValue().getName() ); } + c.replaceClassName( nameMap ); - // translate the names in the InnerClasses attribute + // replace simple names in the InnerClasses attribute too ConstPool constants = c.getClassFile().getConstPool(); InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); if( attr != null ) { for( int i=0; i ATTR: %s,%s,%s", - obfClassEntry, deobfClassEntry, + System.out.println( String.format( "\tDEOBF: %s-> ATTR: %s,%s,%s", + classEntry, attr.outerClass( i ), attr.innerClass( i ), attr.innerName( i ) @@ -109,16 +100,6 @@ public class ClassRenamer }; c.replaceClassName( map ); - // also check InnerClassesAttribute - InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); - if( attr != null ) - { - for( int i=0; i map = Maps.newHashMap(); for( ClassEntry obfClassEntry : ClassRenamer.getAllClassEntries( c ) ) { - map.put( obfClassEntry, m_translator.translateEntry( obfClassEntry ) ); + ClassEntry deobfClassEntry = m_translator.translateEntry( obfClassEntry ); + if( !obfClassEntry.equals( deobfClassEntry ) ) + { + map.put( obfClassEntry, deobfClassEntry ); + } } ClassRenamer.renameClasses( c, map ); } diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index a0617925..5e593078 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -18,6 +18,7 @@ import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.EnclosingMethodAttribute; import javassist.bytecode.InnerClassesAttribute; +import cuchaz.enigma.Constants; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; @@ -33,21 +34,21 @@ public class InnerClassWriter public void write( CtClass c ) { - // get the outer class name - String obfClassName = Descriptor.toJvmName( c.getName() ); - String obfOuterClassName = m_jarIndex.getOuterClass( obfClassName ); + // is this an inner or outer class? + String obfInnerClassName = new ClassEntry( Descriptor.toJvmName( c.getName() ) ).getSimpleName(); + String obfOuterClassName = m_jarIndex.getOuterClass( obfInnerClassName ); if( obfOuterClassName == null ) { // this is an outer class - obfOuterClassName = obfClassName; + obfOuterClassName = Descriptor.toJvmName( c.getName() ); } else { // this is an inner class, rename it to outer$inner - ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + new ClassEntry( obfClassName ).getSimpleName() ); + ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfInnerClassName ); c.setName( obfClassEntry.getName() ); - BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller( obfClassName ); + BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller( obfInnerClassName ); if( caller != null ) { // write the enclosing method attribute @@ -85,7 +86,7 @@ public class InnerClassWriter for( String obfInnerClassName : obfInnerClassNames ) { // get the new inner class name - ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + new ClassEntry( obfInnerClassName ).getSimpleName() ); + ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfInnerClassName ); // here's what the JVM spec says about the InnerClasses attribute // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); @@ -114,12 +115,13 @@ public class InnerClassWriter attr.outerClass( attr.tableLength() - 1 ), attr.innerClass( attr.tableLength() - 1 ), attr.innerName( attr.tableLength() - 1 ), - obfInnerClassName, obfClassEntry.getName() + Constants.NonePackage + "/" + obfInnerClassName, + obfClassEntry.getName() ) ); */ // make sure the outer class references only the new inner class names - c.replaceClassName( obfInnerClassName, obfClassEntry.getName() ); + c.replaceClassName( Constants.NonePackage + "/" + obfInnerClassName, obfClassEntry.getName() ); } } } diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index dbfcba83..faa9b7b1 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -1124,7 +1124,7 @@ public class Gui { // init the text box final JTextField text = new JTextField(); - text.setText( m_reference.getNameableEntry().getName() ); + text.setText( m_reference.getNamableName() ); text.setPreferredSize( new Dimension( 360, text.getPreferredSize().height ) ); text.addKeyListener( new KeyAdapter( ) { @@ -1175,7 +1175,7 @@ public class Gui // abort the rename JPanel panel = (JPanel)m_infoPanel.getComponent( 0 ); panel.remove( panel.getComponentCount() - 1 ); - panel.add( GuiTricks.unboldLabel( new JLabel( m_reference.getNameableEntry().getName(), JLabel.LEFT ) ) ); + panel.add( GuiTricks.unboldLabel( new JLabel( m_reference.getNamableName(), JLabel.LEFT ) ) ); m_editor.grabFocus(); diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 88106dfa..ee02781e 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -37,7 +37,7 @@ public class ClassMapping implements Serializable, Comparable public ClassMapping( String obfName, String deobfName ) { m_obfName = obfName; - m_deobfName = NameValidator.validateClassName( deobfName ); + m_deobfName = NameValidator.validateClassName( deobfName, false ); m_innerClassesByObf = Maps.newHashMap(); m_innerClassesByDeobf = Maps.newHashMap(); m_fieldsByObf = Maps.newHashMap(); @@ -57,7 +57,7 @@ public class ClassMapping implements Serializable, Comparable } public void setDeobfName( String val ) { - m_deobfName = NameValidator.validateClassName( val ); + m_deobfName = NameValidator.validateClassName( val, false ); } //// INNER CLASSES //////// @@ -70,10 +70,12 @@ public class ClassMapping implements Serializable, Comparable public void addInnerClassMapping( ClassMapping classMapping ) { + assert( isSimpleClassName( classMapping.getObfName() ) ); boolean obfWasAdded = m_innerClassesByObf.put( classMapping.getObfName(), classMapping ) == null; assert( obfWasAdded ); if( classMapping.getDeobfName() != null ) { + assert( isSimpleClassName( classMapping.getDeobfName() ) ); boolean deobfWasAdded = m_innerClassesByDeobf.put( classMapping.getDeobfName(), classMapping ) == null; assert( deobfWasAdded ); } @@ -92,6 +94,7 @@ public class ClassMapping implements Serializable, Comparable public ClassMapping getOrCreateInnerClass( String obfName ) { + assert( isSimpleClassName( obfName ) ); ClassMapping classMapping = m_innerClassesByObf.get( obfName ); if( classMapping == null ) { @@ -104,16 +107,19 @@ public class ClassMapping implements Serializable, Comparable public ClassMapping getInnerClassByObf( String obfName ) { + assert( isSimpleClassName( obfName ) ); return m_innerClassesByObf.get( obfName ); } public ClassMapping getInnerClassByDeobf( String deobfName ) { + assert( isSimpleClassName( deobfName ) ); return m_innerClassesByDeobf.get( deobfName ); } public String getObfInnerClassName( String deobfName ) { + assert( isSimpleClassName( deobfName ) ); ClassMapping classMapping = m_innerClassesByDeobf.get( deobfName ); if( classMapping != null ) { @@ -124,6 +130,7 @@ public class ClassMapping implements Serializable, Comparable public String getDeobfInnerClassName( String obfName ) { + assert( isSimpleClassName( obfName ) ); ClassMapping classMapping = m_innerClassesByObf.get( obfName ); if( classMapping != null ) { @@ -134,6 +141,7 @@ public class ClassMapping implements Serializable, Comparable public void setInnerClassName( String obfName, String deobfName ) { + assert( isSimpleClassName( obfName ) ); ClassMapping classMapping = getOrCreateInnerClass( obfName ); if( classMapping.getDeobfName() != null ) { @@ -143,6 +151,7 @@ public class ClassMapping implements Serializable, Comparable classMapping.setDeobfName( deobfName ); if( deobfName != null ) { + assert( isSimpleClassName( deobfName ) ); boolean wasAdded = m_innerClassesByDeobf.put( deobfName, classMapping ) == null; assert( wasAdded ); } @@ -442,4 +451,9 @@ public class ClassMapping implements Serializable, Comparable } return false; } + + public static boolean isSimpleClassName( String name ) + { + return name.indexOf( '/' ) < 0 && name.indexOf( '$' ) < 0; + } } diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index 5cbad59c..4bd9f121 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -78,10 +78,11 @@ public class MappingsReader if( token.equalsIgnoreCase( "CLASS" ) ) { - ClassMapping classMapping = readClass( parts ); + ClassMapping classMapping; if( indent == 0 ) { // outer class + classMapping = readClass( parts, false ); mappings.addClassMapping( classMapping ); } else if( indent == 1 ) @@ -91,11 +92,13 @@ public class MappingsReader { throw new MappingParseException( lineNumber, "Unexpected CLASS entry here!" ); } + + classMapping = readClass( parts, true ); ((ClassMapping)mappingStack.getFirst()).addInnerClassMapping( classMapping ); } else { - throw new MappingParseException( lineNumber, "Unexpected CLASS entry here!" ); + throw new MappingParseException( lineNumber, "Unexpected CLASS entry nesting!" ); } mappingStack.push( classMapping ); } @@ -140,28 +143,30 @@ public class MappingsReader return new ArgumentMapping( Integer.parseInt( parts[1] ), parts[2] ); } - private ClassMapping readClass( String[] parts ) + private ClassMapping readClass( String[] parts, boolean makeSimple ) { if( parts.length == 2 ) { - String obfName = parts[1]; - return new ClassMapping( moveClassOutOfDefaultPackage( obfName, Constants.NonePackage ) ); + String obfName = processName( parts[1], makeSimple ); + return new ClassMapping( obfName ); } else { - String obfName = parts[1]; - String deobfName = parts[2]; - if( obfName.equals( deobfName ) ) - { - return new ClassMapping( moveClassOutOfDefaultPackage( obfName, Constants.NonePackage ) ); - } - else - { - return new ClassMapping( - moveClassOutOfDefaultPackage( parts[1], Constants.NonePackage ), - moveClassOutOfDefaultPackage( parts[2], Constants.NonePackage ) - ); - } + String obfName = processName( parts[1], makeSimple ); + String deobfName = processName( parts[2], makeSimple ); + return new ClassMapping( obfName, deobfName ); + } + } + + private String processName( String name, boolean makeSimple ) + { + if( makeSimple ) + { + return new ClassEntry( name ).getSimpleName(); + } + else + { + return moveClassOutOfDefaultPackage( name, Constants.NonePackage ); } } diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index 957b6d68..24ec7318 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -16,6 +16,7 @@ import java.io.OutputStream; import java.util.Set; import java.util.zip.GZIPOutputStream; +import cuchaz.enigma.Constants; import cuchaz.enigma.analysis.JarIndex; public class MappingsRenamer @@ -31,7 +32,7 @@ public class MappingsRenamer public void setClassName( ClassEntry obf, String deobfName ) { - deobfName = NameValidator.validateClassName( deobfName ); + deobfName = NameValidator.validateClassName( deobfName, !obf.isInnerClass() ); ClassEntry targetEntry = new ClassEntry( deobfName ); if( m_mappings.containsDeobfClass( deobfName ) || m_index.containsObfClass( targetEntry ) ) { @@ -77,7 +78,8 @@ public class MappingsRenamer ClassMapping classMapping = getOrCreateClassMapping( obf ); if( obf.isInnerClass() ) { - classMapping.setInnerClassName( obf.getName(), obf.getName() ); + String innerClassName = Constants.NonePackage + "/" + obf.getInnerClassName(); + classMapping.setInnerClassName( innerClassName, innerClassName ); } else { diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java index 9adf1ac1..c6ae5969 100644 --- a/src/cuchaz/enigma/mapping/NameValidator.java +++ b/src/cuchaz/enigma/mapping/NameValidator.java @@ -55,7 +55,7 @@ public class NameValidator ClassPattern = Pattern.compile( String.format( "^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex ) ); } - public static String validateClassName( String name ) + public static String validateClassName( String name, boolean packageRequired ) { if( name == null ) { @@ -65,9 +65,9 @@ public class NameValidator { throw new IllegalNameException( name, "This doesn't look like a legal class name" ); } - if( new ClassEntry( name ).getPackageName() == null ) + if( packageRequired && new ClassEntry( name ).getPackageName() == null ) { - throw new IllegalNameException( name, "Classes must be in a package" ); + throw new IllegalNameException( name, "Class must be in a package" ); } return Descriptor.toJvmName( name ); } diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 7904ef53..1c69b2f4 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -80,19 +80,26 @@ public class Translator { if( in.isInnerClass() ) { - // look for the inner class + // translate the inner class String translatedInnerClassName = m_direction.choose( classMapping.getDeobfInnerClassName( in.getInnerClassName() ), classMapping.getObfInnerClassName( in.getInnerClassName() ) ); if( translatedInnerClassName != null ) { - // return outer$inner + // try to translate the outer name String translatedOuterClassName = m_direction.choose( classMapping.getDeobfName(), classMapping.getObfName() ); - return translatedOuterClassName + "$" + translatedInnerClassName; + if( translatedOuterClassName != null ) + { + return translatedOuterClassName + "$" + translatedInnerClassName; + } + else + { + return in.getOuterClassName() + "$" + translatedInnerClassName; + } } } else @@ -109,6 +116,7 @@ public class Translator public ClassEntry translateEntry( ClassEntry in ) { + // can we translate the inner class? String name = translate( in ); if( name != null ) { @@ -117,13 +125,14 @@ public class Translator if( in.isInnerClass() ) { - // just translate the outer class name + // guess not. just translate the outer class name then String outerClassName = translate( in.getOuterClassEntry() ); if( outerClassName != null ) { return new ClassEntry( outerClassName + "$" + in.getInnerClassName() ); } } + return in; } -- cgit v1.2.3 From 5eeee98418bb39367258442a82b75a081a6f91e0 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 1 Oct 2014 00:04:18 -0400 Subject: fixed inner class renaming bug also added smarter sorting to class mappings --- src/cuchaz/enigma/Deobfuscator.java | 2 +- src/cuchaz/enigma/Main.java | 3 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 1 - src/cuchaz/enigma/analysis/SourceIndex.java | 2 +- src/cuchaz/enigma/gui/GuiController.java | 1 + src/cuchaz/enigma/mapping/ClassMapping.java | 28 ++++++- src/cuchaz/enigma/mapping/Mappings.java | 26 +------ src/cuchaz/enigma/mapping/MappingsRenamer.java | 11 +-- src/cuchaz/enigma/mapping/Translator.java | 104 +++++++++---------------- 9 files changed, 73 insertions(+), 105 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index d45ffb41..7e0f0927 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -261,7 +261,7 @@ public class Deobfuscator Translator translator = m_translatorCache.get( direction ); if( translator == null ) { - translator = m_mappings.getTranslator( m_jarIndex.getTranslationIndex(), direction ); + translator = m_mappings.getTranslator( direction ); m_translatorCache.put( direction, translator ); } return translator; diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index bbd734c6..371662bc 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -13,6 +13,7 @@ package cuchaz.enigma; import java.io.File; import cuchaz.enigma.gui.Gui; +import cuchaz.enigma.mapping.ClassEntry; public class Main { @@ -32,7 +33,7 @@ public class Main } // DEBUG - //gui.getController().openDeclaration( new ClassEntry( "none/bub" ) ); + gui.getController().openDeclaration( new ClassEntry( "none/ry" ) ); } private static File getFile( String path ) diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 86e52a34..939e342d 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -13,7 +13,6 @@ package cuchaz.enigma; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 21a499e8..0e33de00 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -90,7 +90,7 @@ public class SourceIndex //System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); // for tokens representing inner classes, make sure we only get the simple name - int pos = node.getText().lastIndexOf( '$' ); + int pos = name.lastIndexOf( '$' ); if( pos >= 0 ) { token.end -= pos + 1; diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 2862ebed..646bb87d 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -43,6 +43,7 @@ import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.TranslationDirection; +import cuchaz.enigma.mapping.Translator; public class GuiController { diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index ee02781e..e084d4df 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -117,6 +117,16 @@ public class ClassMapping implements Serializable, Comparable return m_innerClassesByDeobf.get( deobfName ); } + public ClassMapping getInnerClassByDeobfThenObf( String name ) + { + ClassMapping classMapping = getInnerClassByDeobf( name ); + if( classMapping == null ) + { + classMapping = getInnerClassByObf( name ); + } + return classMapping; + } + public String getObfInnerClassName( String deobfName ) { assert( isSimpleClassName( deobfName ) ); @@ -392,9 +402,18 @@ public class ClassMapping implements Serializable, Comparable buf.append( "\n" ); } buf.append( "Methods:\n" ); - for( MethodMapping methodIndex : m_methodsByObf.values() ) + for( MethodMapping methodMapping : m_methodsByObf.values() ) { - buf.append( methodIndex.toString() ); + buf.append( methodMapping.toString() ); + buf.append( "\n" ); + } + buf.append( "Inner Classes:\n" ); + for( ClassMapping classMapping : m_innerClassesByObf.values() ) + { + buf.append( "\t" ); + buf.append( classMapping.getObfName() ); + buf.append( " <-> " ); + buf.append( classMapping.getDeobfName() ); buf.append( "\n" ); } return buf.toString(); @@ -403,6 +422,11 @@ public class ClassMapping implements Serializable, Comparable @Override public int compareTo( ClassMapping other ) { + // sort by a, b, c, ... aa, ab, etc + if( m_obfName.length() != other.m_obfName.length() ) + { + return m_obfName.length() - other.m_obfName.length(); + } return m_obfName.compareTo( other.m_obfName ); } diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 45b41bcd..3a39d100 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -24,7 +24,6 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import cuchaz.enigma.Util; -import cuchaz.enigma.analysis.TranslationIndex; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class Mappings implements Serializable @@ -125,35 +124,16 @@ public class Mappings implements Serializable return m_classesByDeobf.get( deobfName ); } - public Translator getTranslator( TranslationIndex index, TranslationDirection direction ) + public Translator getTranslator( TranslationDirection direction ) { switch( direction ) { case Deobfuscating: - return new Translator( direction, m_classesByObf, index ); + return new Translator( direction, m_classesByObf ); case Obfuscating: - // deobfuscate the index - index = new TranslationIndex( index ); - Map renames = Maps.newHashMap(); - for( ClassMapping classMapping : classes() ) - { - if( classMapping.getDeobfName() != null ) - { - renames.put( classMapping.getObfName(), classMapping.getDeobfName() ); - } - for( ClassMapping innerClassMapping : classMapping.innerClasses() ) - { - if( innerClassMapping.getDeobfName() != null ) - { - renames.put( innerClassMapping.getObfName(), innerClassMapping.getDeobfName() ); - } - } - } - index.renameClasses( renames ); - // fill in the missing deobf class entries with obf entries Map classes = Maps.newHashMap(); for( ClassMapping classMapping : classes() ) @@ -168,7 +148,7 @@ public class Mappings implements Serializable } } - return new Translator( direction, classes, index ); + return new Translator( direction, classes ); default: throw new Error( "Invalid translation direction!" ); diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index 24ec7318..3e5f1a4d 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -121,11 +121,11 @@ public class MappingsRenamer deobfName = NameValidator.validateMethodName( deobfName ); for( MethodEntry entry : implementations ) { - String deobfSignature = getTranslator( TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); + String deobfSignature = m_mappings.getTranslator( TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); MethodEntry targetEntry = new MethodEntry( entry.getClassEntry(), deobfName, deobfSignature ); if( m_mappings.containsDeobfMethod( entry.getClassEntry(), deobfName, entry.getSignature() ) || m_index.containsObfBehavior( targetEntry ) ) { - String deobfClassName = getTranslator( TranslationDirection.Deobfuscating ).translateClass( entry.getClassName() ); + String deobfClassName = m_mappings.getTranslator( TranslationDirection.Deobfuscating ).translateClass( entry.getClassName() ); throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + deobfClassName ); } } @@ -142,7 +142,7 @@ public class MappingsRenamer MethodEntry targetEntry = new MethodEntry( obf.getClassEntry(), deobfName, obf.getSignature() ); if( m_mappings.containsDeobfMethod( obf.getClassEntry(), deobfName, obf.getSignature() ) || m_index.containsObfBehavior( targetEntry ) ) { - String deobfClassName = getTranslator( TranslationDirection.Deobfuscating ).translateClass( obf.getClassName() ); + String deobfClassName = m_mappings.getTranslator( TranslationDirection.Deobfuscating ).translateClass( obf.getClassName() ); throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + deobfClassName ); } @@ -288,9 +288,4 @@ public class MappingsRenamer } return classMapping; } - - private Translator getTranslator( TranslationDirection direction ) - { - return m_mappings.getTranslator( m_index.getTranslationIndex(), direction ); - } } diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 1c69b2f4..6cb52402 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -10,33 +10,27 @@ ******************************************************************************/ package cuchaz.enigma.mapping; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import com.google.common.collect.Maps; -import cuchaz.enigma.analysis.TranslationIndex; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class Translator { private TranslationDirection m_direction; private Map m_classes; - private TranslationIndex m_index; public Translator( ) { m_direction = null; m_classes = Maps.newHashMap(); - m_index = new TranslationIndex(); } - public Translator( TranslationDirection direction, Map classes, TranslationIndex index ) + public Translator( TranslationDirection direction, Map classes ) { m_direction = direction; m_classes = classes; - m_index = index; } @SuppressWarnings( "unchecked" ) @@ -138,28 +132,18 @@ public class Translator public String translate( FieldEntry in ) { - for( String className : getSelfAndAncestors( in.getClassName() ) ) + // look for the class + ClassMapping classMapping = findClassMapping( in.getClassEntry() ); + if( classMapping != null ) { - // look for the class - ClassMapping classMapping = findClassMapping( new ClassEntry( className ) ); - if( classMapping != null ) - { - // look for the field - String translatedName = m_direction.choose( - classMapping.getDeobfFieldName( in.getName() ), - classMapping.getObfFieldName( in.getName() ) - ); - if( translatedName != null ) - { - return translatedName; - } - } - - // is the field implemented in this class? - if( m_index.containsField( className, in.getName() ) ) + // look for the field + String translatedName = m_direction.choose( + classMapping.getDeobfFieldName( in.getName() ), + classMapping.getObfFieldName( in.getName() ) + ); + if( translatedName != null ) { - // stop traversing the superclass chain - break; + return translatedName; } } return null; @@ -180,27 +164,23 @@ public class Translator public String translate( MethodEntry in ) { - for( String className : getSelfAndAncestors( in.getClassName() ) ) + // look for class + ClassMapping classMapping = findClassMapping( in.getClassEntry() ); + if( classMapping != null ) { - // look for class - ClassMapping classMapping = findClassMapping( new ClassEntry( className ) ); - if( classMapping != null ) + // look for the method + MethodMapping methodMapping = m_direction.choose( + classMapping.getMethodByObf( in.getName(), in.getSignature() ), + classMapping.getMethodByDeobf( in.getName(), translateSignature( in.getSignature() ) ) + ); + if( methodMapping != null ) { - // look for the method - MethodMapping methodMapping = m_direction.choose( - classMapping.getMethodByObf( in.getName(), in.getSignature() ), - classMapping.getMethodByDeobf( in.getName(), translateSignature( in.getSignature() ) ) + return m_direction.choose( + methodMapping.getDeobfName(), + methodMapping.getObfName() ); - if( methodMapping != null ) - { - return m_direction.choose( - methodMapping.getDeobfName(), - methodMapping.getObfName() - ); - } } } - return null; } @@ -248,27 +228,23 @@ public class Translator public String translate( ArgumentEntry in ) { - for( String className : getSelfAndAncestors( in.getClassName() ) ) + // look for the class + ClassMapping classMapping = findClassMapping( in.getClassEntry() ); + if( classMapping != null ) { - // look for the class - ClassMapping classMapping = findClassMapping( new ClassEntry( className ) ); - if( classMapping != null ) + // look for the method + MethodMapping methodMapping = m_direction.choose( + classMapping.getMethodByObf( in.getMethodName(), in.getMethodSignature() ), + classMapping.getMethodByDeobf( in.getMethodName(), translateSignature( in.getMethodSignature() ) ) + ); + if( methodMapping != null ) { - // look for the method - MethodMapping methodMapping = m_direction.choose( - classMapping.getMethodByObf( in.getMethodName(), in.getMethodSignature() ), - classMapping.getMethodByDeobf( in.getMethodName(), translateSignature( in.getMethodSignature() ) ) + return m_direction.choose( + methodMapping.getDeobfArgumentName( in.getIndex() ), + methodMapping.getObfArgumentName( in.getIndex() ) ); - if( methodMapping != null ) - { - return m_direction.choose( - methodMapping.getDeobfArgumentName( in.getIndex() ), - methodMapping.getObfArgumentName( in.getIndex() ) - ); - } } } - return null; } @@ -303,14 +279,6 @@ public class Translator } ); } - private List getSelfAndAncestors( String className ) - { - List ancestry = new ArrayList(); - ancestry.add( className ); - ancestry.addAll( m_index.getAncestry( className ) ); - return ancestry; - } - private ClassMapping findClassMapping( ClassEntry classEntry ) { ClassMapping classMapping = m_classes.get( classEntry.getOuterClassName() ); @@ -318,7 +286,7 @@ public class Translator { classMapping = m_direction.choose( classMapping.getInnerClassByObf( classEntry.getInnerClassName() ), - classMapping.getInnerClassByDeobf( classEntry.getInnerClassName() ) + classMapping.getInnerClassByDeobfThenObf( classEntry.getInnerClassName() ) ); } return classMapping; -- cgit v1.2.3 From 056f388494337a0f3cec10fab8e207017757bf6d Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 1 Oct 2014 00:21:29 -0400 Subject: fix inner classes test --- src/cuchaz/enigma/gui/GuiController.java | 1 - test/cuchaz/enigma/TestInnerClasses.java | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 646bb87d..2862ebed 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -43,7 +43,6 @@ import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.TranslationDirection; -import cuchaz.enigma.mapping.Translator; public class GuiController { diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java index e555d920..a7ee0b6d 100644 --- a/test/cuchaz/enigma/TestInnerClasses.java +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -25,13 +25,13 @@ public class TestInnerClasses private JarIndex m_index; private static final String AnonymousOuter = "none/a"; - private static final String AnonymousInner = "none/b"; + private static final String AnonymousInner = "b"; private static final String SimpleOuter = "none/g"; - private static final String SimpleInner = "none/h"; + private static final String SimpleInner = "h"; private static final String ConstructorArgsOuter = "none/e"; - private static final String ConstructorArgsInner = "none/f"; + private static final String ConstructorArgsInner = "f"; private static final String AnonymousWithScopeArgsOuter = "none/c"; - private static final String AnonymousWithScopeArgsInner = "none/d"; + private static final String AnonymousWithScopeArgsInner = "d"; public TestInnerClasses( ) throws Exception -- cgit v1.2.3 From 42e3b23f03a98a81ce7238bda96c4f046eab30be Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 2 Oct 2014 22:18:23 -0400 Subject: fixed issue with decompiling inner classes --- src/cuchaz/enigma/Deobfuscator.java | 8 +++++--- src/cuchaz/enigma/Main.java | 3 +-- src/cuchaz/enigma/TranslatingTypeLoader.java | 2 +- src/cuchaz/enigma/analysis/JarIndex.java | 5 +++++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 7e0f0927..82c786cb 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -127,11 +127,13 @@ public class Deobfuscator Map renames = Maps.newHashMap(); for( ClassMapping classMapping : val.classes() ) { - String outerClassName = m_jarIndex.getOuterClass( classMapping.getObfName() ); + // make sure we strip the packages off of obfuscated inner classes + String innerClassName = new ClassEntry( classMapping.getObfName() ).getSimpleName(); + String outerClassName = m_jarIndex.getOuterClass( innerClassName ); if( outerClassName != null ) { // build the composite class name - String newName = outerClassName + "$" + new ClassEntry( classMapping.getObfName() ).getSimpleName(); + String newName = outerClassName + "$" + innerClassName; // add a rename renames.put( classMapping.getObfName(), newName ); @@ -217,7 +219,7 @@ public class Deobfuscator { // check the class ClassEntry classEntry = new ClassEntry( classMapping.getObfName() ); - String outerClassName = m_jarIndex.getOuterClass( classMapping.getObfName() ); + String outerClassName = m_jarIndex.getOuterClass( classEntry.getSimpleName() ); if( outerClassName != null ) { classEntry = new ClassEntry( outerClassName + "$" + classMapping.getObfName() ); diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index 371662bc..73a12db5 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -13,7 +13,6 @@ package cuchaz.enigma; import java.io.File; import cuchaz.enigma.gui.Gui; -import cuchaz.enigma.mapping.ClassEntry; public class Main { @@ -33,7 +32,7 @@ public class Main } // DEBUG - gui.getController().openDeclaration( new ClassEntry( "none/ry" ) ); + //gui.getController().openDeclaration( new ClassEntry( "none/ces" ) ); } private static File getFile( String path ) diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 939e342d..e69e5cfe 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -123,7 +123,7 @@ public class TranslatingTypeLoader implements ITypeLoader ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry( deobfClassEntry ); // is this an inner class referenced directly? - String obfOuterClassName = m_jarIndex.getOuterClass( obfClassEntry.getName() ); + String obfOuterClassName = m_jarIndex.getOuterClass( obfClassEntry.getSimpleName() ); if( obfOuterClassName != null ) { // this class doesn't really exist. Reference it by outer$inner instead diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index ba082064..0954564e 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -885,6 +885,11 @@ public class JarIndex public String getOuterClass( String obfInnerClassName ) { + // make sure we use the right name + if( new ClassEntry( obfInnerClassName ).getPackageName() != null ) + { + throw new IllegalArgumentException( "Don't reference obfuscated inner classes using packages: " + obfInnerClassName ); + } return m_outerClasses.get( obfInnerClassName ); } -- cgit v1.2.3 From 22cc7f91d61e32c751b74dc6662bef616ab5ddb7 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 2 Oct 2014 22:47:30 -0400 Subject: fixed test case --- test/cuchaz/enigma/TestJarIndexLoneClass.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index f77d86a7..e2a87d09 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -140,7 +140,7 @@ public class TestJarIndexLoneClass @Test public void outerClass( ) { - assertThat( m_index.getOuterClass( "none/a" ), is( nullValue() ) ); + assertThat( m_index.getOuterClass( "a" ), is( nullValue() ) ); } @Test -- cgit v1.2.3 From a1d72afcd4a2f462ba72fdd7a0f2ab82bb795494 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 2 Oct 2014 22:54:30 -0400 Subject: package for 0.6 beta --- build.gradle | 2 +- readme.txt | 2 +- src/cuchaz/enigma/Constants.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 6b89d32e..ce4b65b8 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ targetCompatibility = 1.7 group = "com.cuchazinteractive" archivesBaseName = "enigma" -version = "0.5.1b" +version = "0.6b" sourceSets { main { diff --git a/readme.txt b/readme.txt index 1c5c386f..81f985a0 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ -Enigma v0.5 beta +Enigma v0.6 beta A tool for deobfuscation of Java bytecode Copyright Jeff Martin, 2014 diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index bf6ab846..29a08b73 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -14,7 +14,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.5 beta"; + public static final String Version = "0.6 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024*1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte -- cgit v1.2.3 -- cgit v1.2.3 From 035e73fba69ab06172ae9d784b9e0e4fffeb8388 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 8 Oct 2014 23:54:08 -0400 Subject: relicense as LGPL --- license.GPL3.txt | 674 --------------------- license.LGPL3.txt | 165 +++++ readme.txt | 4 +- src/cuchaz/enigma/Constants.java | 6 +- src/cuchaz/enigma/Deobfuscator.java | 9 +- src/cuchaz/enigma/Main.java | 6 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 6 +- src/cuchaz/enigma/Util.java | 6 +- src/cuchaz/enigma/analysis/Access.java | 8 +- .../enigma/analysis/BehaviorReferenceTreeNode.java | 6 +- src/cuchaz/enigma/analysis/BridgeFixer.java | 6 +- .../analysis/ClassImplementationsTreeNode.java | 6 +- .../enigma/analysis/ClassInheritanceTreeNode.java | 6 +- src/cuchaz/enigma/analysis/EntryReference.java | 6 +- src/cuchaz/enigma/analysis/EntryRenamer.java | 6 +- .../enigma/analysis/FieldReferenceTreeNode.java | 6 +- src/cuchaz/enigma/analysis/JarClassIterator.java | 6 +- src/cuchaz/enigma/analysis/JarIndex.java | 6 +- .../analysis/MethodImplementationsTreeNode.java | 6 +- .../enigma/analysis/MethodInheritanceTreeNode.java | 6 +- src/cuchaz/enigma/analysis/ReferenceTreeNode.java | 6 +- src/cuchaz/enigma/analysis/SourceIndex.java | 6 +- .../analysis/SourceIndexBehaviorVisitor.java | 6 +- .../enigma/analysis/SourceIndexClassVisitor.java | 6 +- src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 6 +- src/cuchaz/enigma/analysis/Token.java | 6 +- src/cuchaz/enigma/analysis/TranslationIndex.java | 6 +- src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 6 +- .../enigma/bytecode/BytecodeIndexIterator.java | 6 +- src/cuchaz/enigma/bytecode/BytecodeTools.java | 6 +- src/cuchaz/enigma/bytecode/CheckCastIterator.java | 6 +- src/cuchaz/enigma/bytecode/ClassRenamer.java | 6 +- src/cuchaz/enigma/bytecode/ClassTranslator.java | 6 +- src/cuchaz/enigma/bytecode/ConstPoolEditor.java | 6 +- src/cuchaz/enigma/bytecode/InfoType.java | 6 +- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 6 +- .../enigma/bytecode/MethodParameterWriter.java | 6 +- .../enigma/bytecode/MethodParametersAttribute.java | 6 +- .../bytecode/accessors/ClassInfoAccessor.java | 6 +- .../bytecode/accessors/ConstInfoAccessor.java | 6 +- .../accessors/InvokeDynamicInfoAccessor.java | 6 +- .../bytecode/accessors/MemberRefInfoAccessor.java | 6 +- .../accessors/MethodHandleInfoAccessor.java | 6 +- .../bytecode/accessors/MethodTypeInfoAccessor.java | 6 +- .../accessors/NameAndTypeInfoAccessor.java | 6 +- .../bytecode/accessors/StringInfoAccessor.java | 6 +- .../bytecode/accessors/Utf8InfoAccessor.java | 6 +- src/cuchaz/enigma/convert/ClassIdentity.java | 6 +- src/cuchaz/enigma/convert/ClassMatcher.java | 6 +- src/cuchaz/enigma/convert/ClassMatching.java | 6 +- src/cuchaz/enigma/convert/ClassNamer.java | 6 +- src/cuchaz/enigma/gui/AboutDialog.java | 6 +- src/cuchaz/enigma/gui/BoxHighlightPainter.java | 6 +- src/cuchaz/enigma/gui/BrowserCaret.java | 6 +- src/cuchaz/enigma/gui/ClassListCellRenderer.java | 6 +- src/cuchaz/enigma/gui/ClassSelector.java | 6 +- src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 6 +- .../enigma/gui/ClassSelectorPackageNode.java | 6 +- src/cuchaz/enigma/gui/CrashDialog.java | 6 +- .../enigma/gui/DeobfuscatedHighlightPainter.java | 6 +- src/cuchaz/enigma/gui/Gui.java | 6 +- src/cuchaz/enigma/gui/GuiController.java | 6 +- src/cuchaz/enigma/gui/GuiTricks.java | 6 +- .../enigma/gui/ObfuscatedHighlightPainter.java | 6 +- src/cuchaz/enigma/gui/OtherHighlightPainter.java | 6 +- src/cuchaz/enigma/gui/ProgressDialog.java | 6 +- src/cuchaz/enigma/gui/ReadableToken.java | 6 +- src/cuchaz/enigma/gui/RenameListener.java | 6 +- .../enigma/gui/SelectionHighlightPainter.java | 6 +- src/cuchaz/enigma/gui/TokenListCellRenderer.java | 6 +- src/cuchaz/enigma/mapping/ArgumentEntry.java | 6 +- src/cuchaz/enigma/mapping/ArgumentMapping.java | 6 +- src/cuchaz/enigma/mapping/BehaviorEntry.java | 6 +- .../enigma/mapping/BehaviorEntryFactory.java | 6 +- src/cuchaz/enigma/mapping/ClassEntry.java | 6 +- src/cuchaz/enigma/mapping/ClassMapping.java | 6 +- src/cuchaz/enigma/mapping/ConstructorEntry.java | 6 +- src/cuchaz/enigma/mapping/Entry.java | 6 +- src/cuchaz/enigma/mapping/EntryPair.java | 6 +- src/cuchaz/enigma/mapping/FieldEntry.java | 6 +- src/cuchaz/enigma/mapping/FieldMapping.java | 6 +- .../enigma/mapping/IllegalNameException.java | 6 +- .../enigma/mapping/MappingParseException.java | 6 +- src/cuchaz/enigma/mapping/Mappings.java | 6 +- src/cuchaz/enigma/mapping/MappingsReader.java | 6 +- src/cuchaz/enigma/mapping/MappingsRenamer.java | 6 +- src/cuchaz/enigma/mapping/MappingsWriter.java | 6 +- src/cuchaz/enigma/mapping/MethodEntry.java | 6 +- src/cuchaz/enigma/mapping/MethodMapping.java | 6 +- src/cuchaz/enigma/mapping/NameValidator.java | 6 +- src/cuchaz/enigma/mapping/SignatureUpdater.java | 6 +- .../enigma/mapping/TranslationDirection.java | 6 +- src/cuchaz/enigma/mapping/Translator.java | 6 +- test/cuchaz/enigma/EntryFactory.java | 9 +- test/cuchaz/enigma/TestDeobfuscator.java | 9 +- test/cuchaz/enigma/TestInnerClasses.java | 7 +- .../enigma/TestJarIndexConstructorReferences.java | 6 +- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 6 +- test/cuchaz/enigma/TestJarIndexLoneClass.java | 7 +- test/cuchaz/enigma/TestSourceIndex.java | 7 +- test/cuchaz/enigma/TestTokensConstructors.java | 6 +- test/cuchaz/enigma/TokenChecker.java | 6 +- test/cuchaz/enigma/inputs/Keep.java | 10 + .../enigma/inputs/constructors/BaseClass.java | 10 + test/cuchaz/enigma/inputs/constructors/Caller.java | 10 + .../inputs/constructors/DefaultConstructable.java | 10 + .../enigma/inputs/constructors/SubClass.java | 10 + .../enigma/inputs/constructors/SubSubClass.java | 10 + .../enigma/inputs/inheritanceTree/BaseClass.java | 10 + .../enigma/inputs/inheritanceTree/SubclassA.java | 10 + .../enigma/inputs/inheritanceTree/SubclassB.java | 10 + .../inputs/inheritanceTree/SubsubclassAA.java | 10 + .../enigma/inputs/innerClasses/Anonymous.java | 10 + .../innerClasses/AnonymousWithScopeArgs.java | 10 + .../inputs/innerClasses/ConstructorArgs.java | 10 + test/cuchaz/enigma/inputs/innerClasses/Simple.java | 10 + test/cuchaz/enigma/inputs/loneClass/LoneClass.java | 10 + 117 files changed, 618 insertions(+), 983 deletions(-) delete mode 100644 license.GPL3.txt create mode 100644 license.LGPL3.txt diff --git a/license.GPL3.txt b/license.GPL3.txt deleted file mode 100644 index 20d40b6b..00000000 --- a/license.GPL3.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. \ No newline at end of file diff --git a/license.LGPL3.txt b/license.LGPL3.txt new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/license.LGPL3.txt @@ -0,0 +1,165 @@ + GNU LESSER 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. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser 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 +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/readme.txt b/readme.txt index 81f985a0..8c217c6d 100644 --- a/readme.txt +++ b/readme.txt @@ -7,7 +7,7 @@ Copyright Jeff Martin, 2014 LICENSE -Enigma is distributed under the GNU General Public license version 3 +Enigma is distributed under the GNU Lesser General Public license version 3 Enigma includes a modified version of Procyon which is distributed under the Apache license version 2. Procyon is copyrighted by Mike Strobel, 2013 @@ -16,4 +16,4 @@ Enigma includes unmodified versions of the following libraries which are also re Javassist JSyntaxPane -Copies of the GNU General Public license verion 3 and the Apache license v2 have been included in this distribution. +Copies of the GNU Lesser General Public license verion 3 and the Apache license v2 have been included in this distribution. diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 29a08b73..065e9f0c 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 82c786cb..186a9934 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -1,10 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin.\ - * + * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index 73a12db5..c9b2761c 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index e69e5cfe..0f2a1c13 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/Util.java b/src/cuchaz/enigma/Util.java index 678de546..fad92d7a 100644 --- a/src/cuchaz/enigma/Util.java +++ b/src/cuchaz/enigma/Util.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/Access.java b/src/cuchaz/enigma/analysis/Access.java index e35bb21b..7ef4dcdd 100644 --- a/src/cuchaz/enigma/analysis/Access.java +++ b/src/cuchaz/enigma/analysis/Access.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation @@ -48,4 +48,4 @@ public enum Access // assume public by default return Public; } -} \ No newline at end of file +} diff --git a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java index 20f1d472..2a5dfe5f 100644 --- a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/BridgeFixer.java b/src/cuchaz/enigma/analysis/BridgeFixer.java index 112b864a..50b0315c 100644 --- a/src/cuchaz/enigma/analysis/BridgeFixer.java +++ b/src/cuchaz/enigma/analysis/BridgeFixer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java index 4e9dd523..bf563f1f 100644 --- a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java index d3fc9dc8..5e080e82 100644 --- a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java index 0cde8759..001ea764 100644 --- a/src/cuchaz/enigma/analysis/EntryReference.java +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java index 2d59fe9d..a9447e66 100644 --- a/src/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java index 2652f64a..a0ad8076 100644 --- a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java index f65b8e79..c1a1c6cd 100644 --- a/src/cuchaz/enigma/analysis/JarClassIterator.java +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 0954564e..d124a241 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index a050282b..fa10c333 100644 --- a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index bd919518..484f81b0 100644 --- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java index e0a0a747..11ff8502 100644 --- a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 0e33de00..95a06020 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index 7ffd1700..e2742544 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 24c48227..81d1568a 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java index 4e98989e..b6bf9d0c 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/Token.java b/src/cuchaz/enigma/analysis/Token.java index 5e70db71..388acd4c 100644 --- a/src/cuchaz/enigma/analysis/Token.java +++ b/src/cuchaz/enigma/analysis/Token.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index 5311ec70..f2f6e0ad 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java index e6ecb10e..4ad234b6 100644 --- a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java +++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java index aadbeb25..fb171994 100644 --- a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java +++ b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/BytecodeTools.java b/src/cuchaz/enigma/bytecode/BytecodeTools.java index 4407a904..eaf5de53 100644 --- a/src/cuchaz/enigma/bytecode/BytecodeTools.java +++ b/src/cuchaz/enigma/bytecode/BytecodeTools.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/CheckCastIterator.java b/src/cuchaz/enigma/bytecode/CheckCastIterator.java index 7ed5d7fb..ba77dbb7 100644 --- a/src/cuchaz/enigma/bytecode/CheckCastIterator.java +++ b/src/cuchaz/enigma/bytecode/CheckCastIterator.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index 849a3233..f2f9e2d5 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 181fadb1..d375b154 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java index aa6149c9..9b1f7915 100644 --- a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java +++ b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/InfoType.java b/src/cuchaz/enigma/bytecode/InfoType.java index fe030066..eba3bec4 100644 --- a/src/cuchaz/enigma/bytecode/InfoType.java +++ b/src/cuchaz/enigma/bytecode/InfoType.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 5e593078..027c4680 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java index adea7eae..867599c6 100644 --- a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java index baf1ac1e..3399cf1e 100644 --- a/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java +++ b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java index 41e1d047..7269f2ab 100644 --- a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java index 3c3d3fa4..a2d21ca2 100644 --- a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java index 169306a4..8ae2ad40 100644 --- a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java index 2ee3aff8..7b83ca2b 100644 --- a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java index 27b7aee8..5503d867 100644 --- a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java index 4cba6a2a..3df8ca1c 100644 --- a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java index 03b4de3c..4d4c50a5 100644 --- a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java index 5cdfce4d..6f65a417 100644 --- a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java index 1cadd836..f532137e 100644 --- a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index 1de345ff..6b865f80 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index 290d90a7..168bda67 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java index e45c0e1a..6856fc06 100644 --- a/src/cuchaz/enigma/convert/ClassMatching.java +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/ClassNamer.java b/src/cuchaz/enigma/convert/ClassNamer.java index a01aec5c..3401a8bc 100644 --- a/src/cuchaz/enigma/convert/ClassNamer.java +++ b/src/cuchaz/enigma/convert/ClassNamer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/AboutDialog.java b/src/cuchaz/enigma/gui/AboutDialog.java index a245956e..c2716315 100644 --- a/src/cuchaz/enigma/gui/AboutDialog.java +++ b/src/cuchaz/enigma/gui/AboutDialog.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/cuchaz/enigma/gui/BoxHighlightPainter.java index df63f5a8..fee8776f 100644 --- a/src/cuchaz/enigma/gui/BoxHighlightPainter.java +++ b/src/cuchaz/enigma/gui/BoxHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/BrowserCaret.java b/src/cuchaz/enigma/gui/BrowserCaret.java index f7e608bb..5f17ef0d 100644 --- a/src/cuchaz/enigma/gui/BrowserCaret.java +++ b/src/cuchaz/enigma/gui/BrowserCaret.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ClassListCellRenderer.java b/src/cuchaz/enigma/gui/ClassListCellRenderer.java index d9d65788..610aa15a 100644 --- a/src/cuchaz/enigma/gui/ClassListCellRenderer.java +++ b/src/cuchaz/enigma/gui/ClassListCellRenderer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java index 8365def1..094b5380 100644 --- a/src/cuchaz/enigma/gui/ClassSelector.java +++ b/src/cuchaz/enigma/gui/ClassSelector.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java index cffa7952..791e3c4b 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java index ad88fb44..6215188c 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/CrashDialog.java b/src/cuchaz/enigma/gui/CrashDialog.java index 0eb9830c..178342cd 100644 --- a/src/cuchaz/enigma/gui/CrashDialog.java +++ b/src/cuchaz/enigma/gui/CrashDialog.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java index 6a428842..c8bce3d0 100644 --- a/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java +++ b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index faa9b7b1..87c14200 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 2862ebed..7da79c6c 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java index 9b889ef4..a276753b 100644 --- a/src/cuchaz/enigma/gui/GuiTricks.java +++ b/src/cuchaz/enigma/gui/GuiTricks.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java index 724be34e..d586bab9 100644 --- a/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java +++ b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/OtherHighlightPainter.java b/src/cuchaz/enigma/gui/OtherHighlightPainter.java index 78de7325..782bb243 100644 --- a/src/cuchaz/enigma/gui/OtherHighlightPainter.java +++ b/src/cuchaz/enigma/gui/OtherHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ProgressDialog.java b/src/cuchaz/enigma/gui/ProgressDialog.java index 7f954314..be2c1792 100644 --- a/src/cuchaz/enigma/gui/ProgressDialog.java +++ b/src/cuchaz/enigma/gui/ProgressDialog.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ReadableToken.java b/src/cuchaz/enigma/gui/ReadableToken.java index 3f430453..f4156edf 100644 --- a/src/cuchaz/enigma/gui/ReadableToken.java +++ b/src/cuchaz/enigma/gui/ReadableToken.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/RenameListener.java b/src/cuchaz/enigma/gui/RenameListener.java index 7d45505b..3d824b8d 100644 --- a/src/cuchaz/enigma/gui/RenameListener.java +++ b/src/cuchaz/enigma/gui/RenameListener.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/SelectionHighlightPainter.java b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java index 35f94518..9d8d38a3 100644 --- a/src/cuchaz/enigma/gui/SelectionHighlightPainter.java +++ b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/TokenListCellRenderer.java b/src/cuchaz/enigma/gui/TokenListCellRenderer.java index 9247c066..4561dcb2 100644 --- a/src/cuchaz/enigma/gui/TokenListCellRenderer.java +++ b/src/cuchaz/enigma/gui/TokenListCellRenderer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java index 7ed3d328..96d7e71e 100644 --- a/src/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ArgumentMapping.java b/src/cuchaz/enigma/mapping/ArgumentMapping.java index 168306a2..12f28828 100644 --- a/src/cuchaz/enigma/mapping/ArgumentMapping.java +++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/BehaviorEntry.java b/src/cuchaz/enigma/mapping/BehaviorEntry.java index 8fc4eaf0..0eac62bf 100644 --- a/src/cuchaz/enigma/mapping/BehaviorEntry.java +++ b/src/cuchaz/enigma/mapping/BehaviorEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java index d3cfb938..95352f04 100644 --- a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java +++ b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 2c708f2a..cad2c527 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index e084d4df..14f46a4b 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ConstructorEntry.java b/src/cuchaz/enigma/mapping/ConstructorEntry.java index d99d1c35..befb0f6e 100644 --- a/src/cuchaz/enigma/mapping/ConstructorEntry.java +++ b/src/cuchaz/enigma/mapping/ConstructorEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/Entry.java b/src/cuchaz/enigma/mapping/Entry.java index 8524834c..a948e1dc 100644 --- a/src/cuchaz/enigma/mapping/Entry.java +++ b/src/cuchaz/enigma/mapping/Entry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java index f94d77ea..3b2e9ed8 100644 --- a/src/cuchaz/enigma/mapping/EntryPair.java +++ b/src/cuchaz/enigma/mapping/EntryPair.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java index 626af576..76a54329 100644 --- a/src/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java index ae0855a8..83c56efa 100644 --- a/src/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java index 830f05c4..4cab5945 100644 --- a/src/cuchaz/enigma/mapping/IllegalNameException.java +++ b/src/cuchaz/enigma/mapping/IllegalNameException.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MappingParseException.java b/src/cuchaz/enigma/mapping/MappingParseException.java index 4fcc1f18..c0d21d20 100644 --- a/src/cuchaz/enigma/mapping/MappingParseException.java +++ b/src/cuchaz/enigma/mapping/MappingParseException.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 3a39d100..dfe06ddf 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index 4bd9f121..058a666d 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index 3e5f1a4d..98ed51df 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java index 3c86dfc0..306d9a63 100644 --- a/src/cuchaz/enigma/mapping/MappingsWriter.java +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java index dbc18855..4aafa0f8 100644 --- a/src/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index c51b0110..eb45331f 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java index c6ae5969..d337d94d 100644 --- a/src/cuchaz/enigma/mapping/NameValidator.java +++ b/src/cuchaz/enigma/mapping/NameValidator.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java index 809473e5..1b56a09f 100644 --- a/src/cuchaz/enigma/mapping/SignatureUpdater.java +++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/TranslationDirection.java b/src/cuchaz/enigma/mapping/TranslationDirection.java index 79ae0d32..87b79dfe 100644 --- a/src/cuchaz/enigma/mapping/TranslationDirection.java +++ b/src/cuchaz/enigma/mapping/TranslationDirection.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 6cb52402..d239b5e8 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/EntryFactory.java b/test/cuchaz/enigma/EntryFactory.java index 5a8a4270..f66149fc 100644 --- a/test/cuchaz/enigma/EntryFactory.java +++ b/test/cuchaz/enigma/EntryFactory.java @@ -1,10 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin.\ - * + * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java index 71de24ae..1f193292 100644 --- a/test/cuchaz/enigma/TestDeobfuscator.java +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -1,10 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin.\ - * + * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java index a7ee0b6d..81626ff3 100644 --- a/test/cuchaz/enigma/TestInnerClasses.java +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -1,10 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. - * * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java index 02381718..f2c6525f 100644 --- a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 50c22825..dc7900c8 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index e2a87d09..defec460 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -1,10 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. - * * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java index dc6ca7e2..408dcde7 100644 --- a/test/cuchaz/enigma/TestSourceIndex.java +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -1,10 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. - * * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java index 24091532..449d70eb 100644 --- a/test/cuchaz/enigma/TestTokensConstructors.java +++ b/test/cuchaz/enigma/TestTokensConstructors.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TokenChecker.java b/test/cuchaz/enigma/TokenChecker.java index c0852f32..6bcd2bb7 100644 --- a/test/cuchaz/enigma/TokenChecker.java +++ b/test/cuchaz/enigma/TokenChecker.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/inputs/Keep.java b/test/cuchaz/enigma/inputs/Keep.java index 3c12baea..3fb14573 100644 --- a/test/cuchaz/enigma/inputs/Keep.java +++ b/test/cuchaz/enigma/inputs/Keep.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs; public class Keep diff --git a/test/cuchaz/enigma/inputs/constructors/BaseClass.java b/test/cuchaz/enigma/inputs/constructors/BaseClass.java index e6d87681..e4e273d3 100644 --- a/test/cuchaz/enigma/inputs/constructors/BaseClass.java +++ b/test/cuchaz/enigma/inputs/constructors/BaseClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.constructors; // none/a diff --git a/test/cuchaz/enigma/inputs/constructors/Caller.java b/test/cuchaz/enigma/inputs/constructors/Caller.java index b2186198..a7b6f1f7 100644 --- a/test/cuchaz/enigma/inputs/constructors/Caller.java +++ b/test/cuchaz/enigma/inputs/constructors/Caller.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.constructors; // none/b diff --git a/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java index 6cfd35e6..e89a32e2 100644 --- a/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java +++ b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.constructors; public class DefaultConstructable diff --git a/test/cuchaz/enigma/inputs/constructors/SubClass.java b/test/cuchaz/enigma/inputs/constructors/SubClass.java index 6ef77323..03589892 100644 --- a/test/cuchaz/enigma/inputs/constructors/SubClass.java +++ b/test/cuchaz/enigma/inputs/constructors/SubClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.constructors; // none/d extends none/a diff --git a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java index 76a0f1f3..76e69c1a 100644 --- a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java +++ b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.constructors; // none/e extends none/d diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java index 8402dde3..9d3807b1 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.inheritanceTree; // none/a diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java index ed507093..98a9570d 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.inheritanceTree; // none/b extends none/a diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java index fc4c8eed..29a29b84 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.inheritanceTree; // none/c extends none/a diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java index b3b83429..4aa0af03 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.inheritanceTree; // none/d extends none/b diff --git a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java index d36a514c..44b529ea 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java +++ b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; public class Anonymous diff --git a/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java b/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java index e0a65e25..4924d09f 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java +++ b/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; public class AnonymousWithScopeArgs diff --git a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java index e24395c7..f7d06d44 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java +++ b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; @SuppressWarnings( "unused" ) diff --git a/test/cuchaz/enigma/inputs/innerClasses/Simple.java b/test/cuchaz/enigma/inputs/innerClasses/Simple.java index 405c6399..9fc9b0c8 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/Simple.java +++ b/test/cuchaz/enigma/inputs/innerClasses/Simple.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; public class Simple diff --git a/test/cuchaz/enigma/inputs/loneClass/LoneClass.java b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java index 961b012e..1022aa2b 100644 --- a/test/cuchaz/enigma/inputs/loneClass/LoneClass.java +++ b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.loneClass; public class LoneClass -- cgit v1.2.3 From 812e2a4630ef01463ff153ba5ffae675e8ac24ac Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 9 Oct 2014 19:37:19 -0400 Subject: reverting to GPL license --- license.GPL3.txt | 674 +++++++++++++++++++++ license.LGPL3.txt | 165 ----- readme.txt | 4 +- src/cuchaz/enigma/Constants.java | 6 +- src/cuchaz/enigma/Deobfuscator.java | 9 +- src/cuchaz/enigma/Main.java | 6 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 6 +- src/cuchaz/enigma/Util.java | 6 +- src/cuchaz/enigma/analysis/Access.java | 8 +- .../enigma/analysis/BehaviorReferenceTreeNode.java | 6 +- src/cuchaz/enigma/analysis/BridgeFixer.java | 6 +- .../analysis/ClassImplementationsTreeNode.java | 6 +- .../enigma/analysis/ClassInheritanceTreeNode.java | 6 +- src/cuchaz/enigma/analysis/EntryReference.java | 6 +- src/cuchaz/enigma/analysis/EntryRenamer.java | 6 +- .../enigma/analysis/FieldReferenceTreeNode.java | 6 +- src/cuchaz/enigma/analysis/JarClassIterator.java | 6 +- src/cuchaz/enigma/analysis/JarIndex.java | 6 +- .../analysis/MethodImplementationsTreeNode.java | 6 +- .../enigma/analysis/MethodInheritanceTreeNode.java | 6 +- src/cuchaz/enigma/analysis/ReferenceTreeNode.java | 6 +- src/cuchaz/enigma/analysis/SourceIndex.java | 6 +- .../analysis/SourceIndexBehaviorVisitor.java | 6 +- .../enigma/analysis/SourceIndexClassVisitor.java | 6 +- src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 6 +- src/cuchaz/enigma/analysis/Token.java | 6 +- src/cuchaz/enigma/analysis/TranslationIndex.java | 6 +- src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 6 +- .../enigma/bytecode/BytecodeIndexIterator.java | 6 +- src/cuchaz/enigma/bytecode/BytecodeTools.java | 6 +- src/cuchaz/enigma/bytecode/CheckCastIterator.java | 6 +- src/cuchaz/enigma/bytecode/ClassRenamer.java | 6 +- src/cuchaz/enigma/bytecode/ClassTranslator.java | 6 +- src/cuchaz/enigma/bytecode/ConstPoolEditor.java | 6 +- src/cuchaz/enigma/bytecode/InfoType.java | 6 +- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 6 +- .../enigma/bytecode/MethodParameterWriter.java | 6 +- .../enigma/bytecode/MethodParametersAttribute.java | 6 +- .../bytecode/accessors/ClassInfoAccessor.java | 6 +- .../bytecode/accessors/ConstInfoAccessor.java | 6 +- .../accessors/InvokeDynamicInfoAccessor.java | 6 +- .../bytecode/accessors/MemberRefInfoAccessor.java | 6 +- .../accessors/MethodHandleInfoAccessor.java | 6 +- .../bytecode/accessors/MethodTypeInfoAccessor.java | 6 +- .../accessors/NameAndTypeInfoAccessor.java | 6 +- .../bytecode/accessors/StringInfoAccessor.java | 6 +- .../bytecode/accessors/Utf8InfoAccessor.java | 6 +- src/cuchaz/enigma/convert/ClassIdentity.java | 6 +- src/cuchaz/enigma/convert/ClassMatcher.java | 6 +- src/cuchaz/enigma/convert/ClassMatching.java | 6 +- src/cuchaz/enigma/convert/ClassNamer.java | 6 +- src/cuchaz/enigma/gui/AboutDialog.java | 6 +- src/cuchaz/enigma/gui/BoxHighlightPainter.java | 6 +- src/cuchaz/enigma/gui/BrowserCaret.java | 6 +- src/cuchaz/enigma/gui/ClassListCellRenderer.java | 6 +- src/cuchaz/enigma/gui/ClassSelector.java | 6 +- src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 6 +- .../enigma/gui/ClassSelectorPackageNode.java | 6 +- src/cuchaz/enigma/gui/CrashDialog.java | 6 +- .../enigma/gui/DeobfuscatedHighlightPainter.java | 6 +- src/cuchaz/enigma/gui/Gui.java | 6 +- src/cuchaz/enigma/gui/GuiController.java | 6 +- src/cuchaz/enigma/gui/GuiTricks.java | 6 +- .../enigma/gui/ObfuscatedHighlightPainter.java | 6 +- src/cuchaz/enigma/gui/OtherHighlightPainter.java | 6 +- src/cuchaz/enigma/gui/ProgressDialog.java | 6 +- src/cuchaz/enigma/gui/ReadableToken.java | 6 +- src/cuchaz/enigma/gui/RenameListener.java | 6 +- .../enigma/gui/SelectionHighlightPainter.java | 6 +- src/cuchaz/enigma/gui/TokenListCellRenderer.java | 6 +- src/cuchaz/enigma/mapping/ArgumentEntry.java | 6 +- src/cuchaz/enigma/mapping/ArgumentMapping.java | 6 +- src/cuchaz/enigma/mapping/BehaviorEntry.java | 6 +- .../enigma/mapping/BehaviorEntryFactory.java | 6 +- src/cuchaz/enigma/mapping/ClassEntry.java | 6 +- src/cuchaz/enigma/mapping/ClassMapping.java | 6 +- src/cuchaz/enigma/mapping/ConstructorEntry.java | 6 +- src/cuchaz/enigma/mapping/Entry.java | 6 +- src/cuchaz/enigma/mapping/EntryPair.java | 6 +- src/cuchaz/enigma/mapping/FieldEntry.java | 6 +- src/cuchaz/enigma/mapping/FieldMapping.java | 6 +- .../enigma/mapping/IllegalNameException.java | 6 +- .../enigma/mapping/MappingParseException.java | 6 +- src/cuchaz/enigma/mapping/Mappings.java | 6 +- src/cuchaz/enigma/mapping/MappingsReader.java | 6 +- src/cuchaz/enigma/mapping/MappingsRenamer.java | 6 +- src/cuchaz/enigma/mapping/MappingsWriter.java | 6 +- src/cuchaz/enigma/mapping/MethodEntry.java | 6 +- src/cuchaz/enigma/mapping/MethodMapping.java | 6 +- src/cuchaz/enigma/mapping/NameValidator.java | 6 +- src/cuchaz/enigma/mapping/SignatureUpdater.java | 6 +- .../enigma/mapping/TranslationDirection.java | 6 +- src/cuchaz/enigma/mapping/Translator.java | 6 +- test/cuchaz/enigma/EntryFactory.java | 9 +- test/cuchaz/enigma/TestDeobfuscator.java | 9 +- test/cuchaz/enigma/TestInnerClasses.java | 7 +- .../enigma/TestJarIndexConstructorReferences.java | 6 +- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 6 +- test/cuchaz/enigma/TestJarIndexLoneClass.java | 7 +- test/cuchaz/enigma/TestSourceIndex.java | 7 +- test/cuchaz/enigma/TestTokensConstructors.java | 6 +- test/cuchaz/enigma/TokenChecker.java | 6 +- test/cuchaz/enigma/inputs/Keep.java | 10 - .../enigma/inputs/constructors/BaseClass.java | 10 - test/cuchaz/enigma/inputs/constructors/Caller.java | 10 - .../inputs/constructors/DefaultConstructable.java | 10 - .../enigma/inputs/constructors/SubClass.java | 10 - .../enigma/inputs/constructors/SubSubClass.java | 10 - .../enigma/inputs/inheritanceTree/BaseClass.java | 10 - .../enigma/inputs/inheritanceTree/SubclassA.java | 10 - .../enigma/inputs/inheritanceTree/SubclassB.java | 10 - .../inputs/inheritanceTree/SubsubclassAA.java | 10 - .../enigma/inputs/innerClasses/Anonymous.java | 10 - .../innerClasses/AnonymousWithScopeArgs.java | 10 - .../inputs/innerClasses/ConstructorArgs.java | 10 - test/cuchaz/enigma/inputs/innerClasses/Simple.java | 10 - test/cuchaz/enigma/inputs/loneClass/LoneClass.java | 10 - 117 files changed, 983 insertions(+), 618 deletions(-) create mode 100644 license.GPL3.txt delete mode 100644 license.LGPL3.txt diff --git a/license.GPL3.txt b/license.GPL3.txt new file mode 100644 index 00000000..20d40b6b --- /dev/null +++ b/license.GPL3.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/license.LGPL3.txt b/license.LGPL3.txt deleted file mode 100644 index 65c5ca88..00000000 --- a/license.LGPL3.txt +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER 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. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser 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 -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/readme.txt b/readme.txt index 8c217c6d..81f985a0 100644 --- a/readme.txt +++ b/readme.txt @@ -7,7 +7,7 @@ Copyright Jeff Martin, 2014 LICENSE -Enigma is distributed under the GNU Lesser General Public license version 3 +Enigma is distributed under the GNU General Public license version 3 Enigma includes a modified version of Procyon which is distributed under the Apache license version 2. Procyon is copyrighted by Mike Strobel, 2013 @@ -16,4 +16,4 @@ Enigma includes unmodified versions of the following libraries which are also re Javassist JSyntaxPane -Copies of the GNU Lesser General Public license verion 3 and the Apache license v2 have been included in this distribution. +Copies of the GNU General Public license verion 3 and the Apache license v2 have been included in this distribution. diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 065e9f0c..29a08b73 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 186a9934..82c786cb 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -1,9 +1,10 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2014 Jeff Martin.\ + * * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index c9b2761c..73a12db5 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 0f2a1c13..e69e5cfe 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/Util.java b/src/cuchaz/enigma/Util.java index fad92d7a..678de546 100644 --- a/src/cuchaz/enigma/Util.java +++ b/src/cuchaz/enigma/Util.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/Access.java b/src/cuchaz/enigma/analysis/Access.java index 7ef4dcdd..e35bb21b 100644 --- a/src/cuchaz/enigma/analysis/Access.java +++ b/src/cuchaz/enigma/analysis/Access.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation @@ -48,4 +48,4 @@ public enum Access // assume public by default return Public; } -} +} \ No newline at end of file diff --git a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java index 2a5dfe5f..20f1d472 100644 --- a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/BridgeFixer.java b/src/cuchaz/enigma/analysis/BridgeFixer.java index 50b0315c..112b864a 100644 --- a/src/cuchaz/enigma/analysis/BridgeFixer.java +++ b/src/cuchaz/enigma/analysis/BridgeFixer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java index bf563f1f..4e9dd523 100644 --- a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java index 5e080e82..d3fc9dc8 100644 --- a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java index 001ea764..0cde8759 100644 --- a/src/cuchaz/enigma/analysis/EntryReference.java +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java index a9447e66..2d59fe9d 100644 --- a/src/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java index a0ad8076..2652f64a 100644 --- a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java index c1a1c6cd..f65b8e79 100644 --- a/src/cuchaz/enigma/analysis/JarClassIterator.java +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index d124a241..0954564e 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index fa10c333..a050282b 100644 --- a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index 484f81b0..bd919518 100644 --- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java index 11ff8502..e0a0a747 100644 --- a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 95a06020..0e33de00 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index e2742544..7ffd1700 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 81d1568a..24c48227 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java index b6bf9d0c..4e98989e 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/Token.java b/src/cuchaz/enigma/analysis/Token.java index 388acd4c..5e70db71 100644 --- a/src/cuchaz/enigma/analysis/Token.java +++ b/src/cuchaz/enigma/analysis/Token.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index f2f6e0ad..5311ec70 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java index 4ad234b6..e6ecb10e 100644 --- a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java +++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java index fb171994..aadbeb25 100644 --- a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java +++ b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/BytecodeTools.java b/src/cuchaz/enigma/bytecode/BytecodeTools.java index eaf5de53..4407a904 100644 --- a/src/cuchaz/enigma/bytecode/BytecodeTools.java +++ b/src/cuchaz/enigma/bytecode/BytecodeTools.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/CheckCastIterator.java b/src/cuchaz/enigma/bytecode/CheckCastIterator.java index ba77dbb7..7ed5d7fb 100644 --- a/src/cuchaz/enigma/bytecode/CheckCastIterator.java +++ b/src/cuchaz/enigma/bytecode/CheckCastIterator.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index f2f9e2d5..849a3233 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index d375b154..181fadb1 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java index 9b1f7915..aa6149c9 100644 --- a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java +++ b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/InfoType.java b/src/cuchaz/enigma/bytecode/InfoType.java index eba3bec4..fe030066 100644 --- a/src/cuchaz/enigma/bytecode/InfoType.java +++ b/src/cuchaz/enigma/bytecode/InfoType.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 027c4680..5e593078 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java index 867599c6..adea7eae 100644 --- a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java index 3399cf1e..baf1ac1e 100644 --- a/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java +++ b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java index 7269f2ab..41e1d047 100644 --- a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java index a2d21ca2..3c3d3fa4 100644 --- a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java index 8ae2ad40..169306a4 100644 --- a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java index 7b83ca2b..2ee3aff8 100644 --- a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java index 5503d867..27b7aee8 100644 --- a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java index 3df8ca1c..4cba6a2a 100644 --- a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java index 4d4c50a5..03b4de3c 100644 --- a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java index 6f65a417..5cdfce4d 100644 --- a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java index f532137e..1cadd836 100644 --- a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index 6b865f80..1de345ff 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index 168bda67..290d90a7 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java index 6856fc06..e45c0e1a 100644 --- a/src/cuchaz/enigma/convert/ClassMatching.java +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/ClassNamer.java b/src/cuchaz/enigma/convert/ClassNamer.java index 3401a8bc..a01aec5c 100644 --- a/src/cuchaz/enigma/convert/ClassNamer.java +++ b/src/cuchaz/enigma/convert/ClassNamer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/AboutDialog.java b/src/cuchaz/enigma/gui/AboutDialog.java index c2716315..a245956e 100644 --- a/src/cuchaz/enigma/gui/AboutDialog.java +++ b/src/cuchaz/enigma/gui/AboutDialog.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/cuchaz/enigma/gui/BoxHighlightPainter.java index fee8776f..df63f5a8 100644 --- a/src/cuchaz/enigma/gui/BoxHighlightPainter.java +++ b/src/cuchaz/enigma/gui/BoxHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/BrowserCaret.java b/src/cuchaz/enigma/gui/BrowserCaret.java index 5f17ef0d..f7e608bb 100644 --- a/src/cuchaz/enigma/gui/BrowserCaret.java +++ b/src/cuchaz/enigma/gui/BrowserCaret.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ClassListCellRenderer.java b/src/cuchaz/enigma/gui/ClassListCellRenderer.java index 610aa15a..d9d65788 100644 --- a/src/cuchaz/enigma/gui/ClassListCellRenderer.java +++ b/src/cuchaz/enigma/gui/ClassListCellRenderer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java index 094b5380..8365def1 100644 --- a/src/cuchaz/enigma/gui/ClassSelector.java +++ b/src/cuchaz/enigma/gui/ClassSelector.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java index 791e3c4b..cffa7952 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java index 6215188c..ad88fb44 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/CrashDialog.java b/src/cuchaz/enigma/gui/CrashDialog.java index 178342cd..0eb9830c 100644 --- a/src/cuchaz/enigma/gui/CrashDialog.java +++ b/src/cuchaz/enigma/gui/CrashDialog.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java index c8bce3d0..6a428842 100644 --- a/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java +++ b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 87c14200..faa9b7b1 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 7da79c6c..2862ebed 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java index a276753b..9b889ef4 100644 --- a/src/cuchaz/enigma/gui/GuiTricks.java +++ b/src/cuchaz/enigma/gui/GuiTricks.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java index d586bab9..724be34e 100644 --- a/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java +++ b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/OtherHighlightPainter.java b/src/cuchaz/enigma/gui/OtherHighlightPainter.java index 782bb243..78de7325 100644 --- a/src/cuchaz/enigma/gui/OtherHighlightPainter.java +++ b/src/cuchaz/enigma/gui/OtherHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ProgressDialog.java b/src/cuchaz/enigma/gui/ProgressDialog.java index be2c1792..7f954314 100644 --- a/src/cuchaz/enigma/gui/ProgressDialog.java +++ b/src/cuchaz/enigma/gui/ProgressDialog.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ReadableToken.java b/src/cuchaz/enigma/gui/ReadableToken.java index f4156edf..3f430453 100644 --- a/src/cuchaz/enigma/gui/ReadableToken.java +++ b/src/cuchaz/enigma/gui/ReadableToken.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/RenameListener.java b/src/cuchaz/enigma/gui/RenameListener.java index 3d824b8d..7d45505b 100644 --- a/src/cuchaz/enigma/gui/RenameListener.java +++ b/src/cuchaz/enigma/gui/RenameListener.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/SelectionHighlightPainter.java b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java index 9d8d38a3..35f94518 100644 --- a/src/cuchaz/enigma/gui/SelectionHighlightPainter.java +++ b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/TokenListCellRenderer.java b/src/cuchaz/enigma/gui/TokenListCellRenderer.java index 4561dcb2..9247c066 100644 --- a/src/cuchaz/enigma/gui/TokenListCellRenderer.java +++ b/src/cuchaz/enigma/gui/TokenListCellRenderer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java index 96d7e71e..7ed3d328 100644 --- a/src/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ArgumentMapping.java b/src/cuchaz/enigma/mapping/ArgumentMapping.java index 12f28828..168306a2 100644 --- a/src/cuchaz/enigma/mapping/ArgumentMapping.java +++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/BehaviorEntry.java b/src/cuchaz/enigma/mapping/BehaviorEntry.java index 0eac62bf..8fc4eaf0 100644 --- a/src/cuchaz/enigma/mapping/BehaviorEntry.java +++ b/src/cuchaz/enigma/mapping/BehaviorEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java index 95352f04..d3cfb938 100644 --- a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java +++ b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index cad2c527..2c708f2a 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 14f46a4b..e084d4df 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ConstructorEntry.java b/src/cuchaz/enigma/mapping/ConstructorEntry.java index befb0f6e..d99d1c35 100644 --- a/src/cuchaz/enigma/mapping/ConstructorEntry.java +++ b/src/cuchaz/enigma/mapping/ConstructorEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/Entry.java b/src/cuchaz/enigma/mapping/Entry.java index a948e1dc..8524834c 100644 --- a/src/cuchaz/enigma/mapping/Entry.java +++ b/src/cuchaz/enigma/mapping/Entry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java index 3b2e9ed8..f94d77ea 100644 --- a/src/cuchaz/enigma/mapping/EntryPair.java +++ b/src/cuchaz/enigma/mapping/EntryPair.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java index 76a54329..626af576 100644 --- a/src/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java index 83c56efa..ae0855a8 100644 --- a/src/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java index 4cab5945..830f05c4 100644 --- a/src/cuchaz/enigma/mapping/IllegalNameException.java +++ b/src/cuchaz/enigma/mapping/IllegalNameException.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MappingParseException.java b/src/cuchaz/enigma/mapping/MappingParseException.java index c0d21d20..4fcc1f18 100644 --- a/src/cuchaz/enigma/mapping/MappingParseException.java +++ b/src/cuchaz/enigma/mapping/MappingParseException.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index dfe06ddf..3a39d100 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index 058a666d..4bd9f121 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index 98ed51df..3e5f1a4d 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java index 306d9a63..3c86dfc0 100644 --- a/src/cuchaz/enigma/mapping/MappingsWriter.java +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java index 4aafa0f8..dbc18855 100644 --- a/src/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index eb45331f..c51b0110 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java index d337d94d..c6ae5969 100644 --- a/src/cuchaz/enigma/mapping/NameValidator.java +++ b/src/cuchaz/enigma/mapping/NameValidator.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java index 1b56a09f..809473e5 100644 --- a/src/cuchaz/enigma/mapping/SignatureUpdater.java +++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/TranslationDirection.java b/src/cuchaz/enigma/mapping/TranslationDirection.java index 87b79dfe..79ae0d32 100644 --- a/src/cuchaz/enigma/mapping/TranslationDirection.java +++ b/src/cuchaz/enigma/mapping/TranslationDirection.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index d239b5e8..6cb52402 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/EntryFactory.java b/test/cuchaz/enigma/EntryFactory.java index f66149fc..5a8a4270 100644 --- a/test/cuchaz/enigma/EntryFactory.java +++ b/test/cuchaz/enigma/EntryFactory.java @@ -1,9 +1,10 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2014 Jeff Martin.\ + * * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java index 1f193292..71de24ae 100644 --- a/test/cuchaz/enigma/TestDeobfuscator.java +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -1,9 +1,10 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2014 Jeff Martin.\ + * * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java index 81626ff3..a7ee0b6d 100644 --- a/test/cuchaz/enigma/TestInnerClasses.java +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -1,9 +1,10 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. + * * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java index f2c6525f..02381718 100644 --- a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index dc7900c8..50c22825 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index defec460..e2a87d09 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -1,9 +1,10 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. + * * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java index 408dcde7..dc6ca7e2 100644 --- a/test/cuchaz/enigma/TestSourceIndex.java +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -1,9 +1,10 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. + * * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java index 449d70eb..24091532 100644 --- a/test/cuchaz/enigma/TestTokensConstructors.java +++ b/test/cuchaz/enigma/TestTokensConstructors.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TokenChecker.java b/test/cuchaz/enigma/TokenChecker.java index 6bcd2bb7..c0852f32 100644 --- a/test/cuchaz/enigma/TokenChecker.java +++ b/test/cuchaz/enigma/TokenChecker.java @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/inputs/Keep.java b/test/cuchaz/enigma/inputs/Keep.java index 3fb14573..3c12baea 100644 --- a/test/cuchaz/enigma/inputs/Keep.java +++ b/test/cuchaz/enigma/inputs/Keep.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs; public class Keep diff --git a/test/cuchaz/enigma/inputs/constructors/BaseClass.java b/test/cuchaz/enigma/inputs/constructors/BaseClass.java index e4e273d3..e6d87681 100644 --- a/test/cuchaz/enigma/inputs/constructors/BaseClass.java +++ b/test/cuchaz/enigma/inputs/constructors/BaseClass.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.constructors; // none/a diff --git a/test/cuchaz/enigma/inputs/constructors/Caller.java b/test/cuchaz/enigma/inputs/constructors/Caller.java index a7b6f1f7..b2186198 100644 --- a/test/cuchaz/enigma/inputs/constructors/Caller.java +++ b/test/cuchaz/enigma/inputs/constructors/Caller.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.constructors; // none/b diff --git a/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java index e89a32e2..6cfd35e6 100644 --- a/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java +++ b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.constructors; public class DefaultConstructable diff --git a/test/cuchaz/enigma/inputs/constructors/SubClass.java b/test/cuchaz/enigma/inputs/constructors/SubClass.java index 03589892..6ef77323 100644 --- a/test/cuchaz/enigma/inputs/constructors/SubClass.java +++ b/test/cuchaz/enigma/inputs/constructors/SubClass.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.constructors; // none/d extends none/a diff --git a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java index 76e69c1a..76a0f1f3 100644 --- a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java +++ b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.constructors; // none/e extends none/d diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java index 9d3807b1..8402dde3 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.inheritanceTree; // none/a diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java index 98a9570d..ed507093 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.inheritanceTree; // none/b extends none/a diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java index 29a29b84..fc4c8eed 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.inheritanceTree; // none/c extends none/a diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java index 4aa0af03..b3b83429 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.inheritanceTree; // none/d extends none/b diff --git a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java index 44b529ea..d36a514c 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java +++ b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; public class Anonymous diff --git a/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java b/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java index 4924d09f..e0a65e25 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java +++ b/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; public class AnonymousWithScopeArgs diff --git a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java index f7d06d44..e24395c7 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java +++ b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; @SuppressWarnings( "unused" ) diff --git a/test/cuchaz/enigma/inputs/innerClasses/Simple.java b/test/cuchaz/enigma/inputs/innerClasses/Simple.java index 9fc9b0c8..405c6399 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/Simple.java +++ b/test/cuchaz/enigma/inputs/innerClasses/Simple.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; public class Simple diff --git a/test/cuchaz/enigma/inputs/loneClass/LoneClass.java b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java index 1022aa2b..961b012e 100644 --- a/test/cuchaz/enigma/inputs/loneClass/LoneClass.java +++ b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java @@ -1,13 +1,3 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ package cuchaz.enigma.inputs.loneClass; public class LoneClass -- cgit v1.2.3 From e28d1efd91912a60bdbf30f01011b18018075629 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 6 Jan 2015 00:44:16 -0500 Subject: got rid of gradle in favor of ivy+ssjb --- .hgignore | 14 ++- build.gradle | 135 ------------------------- build.py | 57 +++++++++++ ivy.xml | 13 +++ libs/procyon-decompiler-0.5.26-enigma.jar | Bin 0 -> 1757481 bytes readme.gui.txt | 19 ++++ readme.translate.txt | 12 +++ readme.txt | 19 ---- ssjb.py | 163 ++++++++++++++++++++++++++++++ 9 files changed, 270 insertions(+), 162 deletions(-) delete mode 100644 build.gradle create mode 100644 build.py create mode 100644 ivy.xml create mode 100644 libs/procyon-decompiler-0.5.26-enigma.jar create mode 100644 readme.gui.txt create mode 100644 readme.translate.txt delete mode 100644 readme.txt create mode 100644 ssjb.py diff --git a/.hgignore b/.hgignore index d552dedf..9ff982e6 100644 --- a/.hgignore +++ b/.hgignore @@ -1,17 +1,15 @@ syntax: regexp -^\.classpath$ +^\. syntax: regexp -^\.project$ +^bin$ syntax: regexp -^\.gradle$ -syntax: regexp -^\.settings$ -syntax: regexp -^libs$ +^lib$ syntax: regexp ^build$ syntax: regexp ^data$ syntax: regexp -^input$ \ No newline at end of file +^input$ +syntax: regexp +^ivy$ \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100644 index ce4b65b8..00000000 --- a/build.gradle +++ /dev/null @@ -1,135 +0,0 @@ - -buildscript { - repositories { - mavenCentral() - } - - dependencies { - classpath "eu.appsatori:gradle-fatjar-plugin:0.2-rc1" - classpath "net.sf.proguard:proguard-gradle:5.0" - } -} - -apply plugin: "java" -apply plugin: "eclipse" -apply plugin: "maven" -apply plugin: "fatjar" - -sourceCompatibility = 1.7 -targetCompatibility = 1.7 - -group = "com.cuchazinteractive" -archivesBaseName = "enigma" -version = "0.6b" - -sourceSets { - main { - java { - srcDir "src" - } - resources { - srcDir "conf" - } - } - test { - java { - srcDir "test" - } - } -} - -repositories { - mavenCentral() -} - -dependencies { - compile fileTree( dir: "libs", include: "*.jar" ) - compile "de.sciss:jsyntaxpane:1.0.0" - compile "com.google.guava:guava:17.0" - compile "org.javassist:javassist:3.18.1-GA" - - testCompile "junit:junit:4.11" - testCompile "org.hamcrest:hamcrest-all:1.3" -} - -fatJar { - from( "." ) { - include( "*.txt" ) - } - manifest { - attributes( - "Title": archivesBaseName, - "Manifest-Version": "1.0", - "Version": version, - "Main-Class" : "cuchaz.enigma.Main" - ) - } -} - -task jarLoneClass( type: Jar ) { - from( sourceSets.test.output ) { - include( "cuchaz/enigma/inputs/Keep.class" ) - include( "cuchaz/enigma/inputs/loneClass/**" ) - } - archiveName( "testLoneClass.jar" ) -} - -task jarInheritanceTree( type: Jar ) { - from( sourceSets.test.output ) { - include( "cuchaz/enigma/inputs/Keep.class" ) - include( "cuchaz/enigma/inputs/inheritanceTree/**" ) - } - archiveName( "testInheritanceTree.jar" ) -} - -task jarConstructors( type: Jar ) { - from( sourceSets.test.output ) { - include( "cuchaz/enigma/inputs/Keep.class" ) - include( "cuchaz/enigma/inputs/constructors/**" ) - } - archiveName( "testConstructors.jar" ) -} - -task jarInnerClasses( type: Jar ) { - from( sourceSets.test.output ) { - include( "cuchaz/enigma/inputs/Keep.class" ) - include( "cuchaz/enigma/inputs/innerClasses/**" ) - } - archiveName( "testInnerClasses.jar" ) -} - -tasks.withType( proguard.gradle.ProGuardTask ) { - libraryjars( "${System.getProperty('java.home')}/lib/rt.jar" ) - overloadaggressively - repackageclasses - allowaccessmodification - dontoptimize - dontshrink - keep( "class cuchaz.enigma.inputs.Keep" ) -} - -task obfLoneClass( type: proguard.gradle.ProGuardTask, dependsOn: jarLoneClass ) { - def name = "LoneClass" - injars( "build/libs/test${name}.jar" ) - outjars( "build/libs/test${name}.obf.jar" ) -} - -task obfInheritanceTree( type: proguard.gradle.ProGuardTask, dependsOn: jarInheritanceTree ) { - def name = "InheritanceTree" - injars( "build/libs/test${name}.jar" ) - outjars( "build/libs/test${name}.obf.jar" ) -} - -task obfConstructors( type: proguard.gradle.ProGuardTask, dependsOn: jarConstructors ) { - def name = "Constructors" - injars( "build/libs/test${name}.jar" ) - outjars( "build/libs/test${name}.obf.jar" ) -} - -task obfInnerClasses( type: proguard.gradle.ProGuardTask, dependsOn: jarInnerClasses ) { - def name = "InnerClasses" - injars( "build/libs/test${name}.jar" ) - outjars( "build/libs/test${name}.obf.jar" ) -} - -task obfTestCases( dependsOn: tasks.withType( proguard.gradle.ProGuardTask ) ) diff --git a/build.py b/build.py new file mode 100644 index 00000000..69182514 --- /dev/null +++ b/build.py @@ -0,0 +1,57 @@ + +import os +import ssjb + +# settings +projectName = "enigma" +version = "0.6b" +author = "Cuchaz" + +dirBin = "bin" +dirBuild = "build" +dirTemp = os.path.join(dirBuild, "tmp") + + +def getJarFullName(name) : + return "%s-%s-%s.jar" % (projectName, name, version) + +def buildGuiJar(): + jarName = "gui" + os.makedirs(dirTemp) + ssjb.copyFiles(dirTemp, dirBin, ssjb.findFiles(dirBin)) + ssjb.unpackJars(dirTemp, "ivy/bundles", recursive=True) + ssjb.unpackJars(dirTemp, "ivy/jars", recursive=True) + ssjb.unpackJars(dirTemp, "libs", recursive=True) + ssjb.delete(os.path.join(dirTemp, "LICENSE.txt")) + ssjb.copyFile(dirTemp, "license.APL2.txt") + ssjb.copyFile(dirTemp, "license.GPL3.txt") + ssjb.copyFile(dirTemp, "readme.gui.txt", renameTo="readme.txt") + manifest = ssjb.buildManifest("%s-%s" % (projectName, jarName), version, author, "cuchaz.enigma.Main") + ssjb.jar(os.path.join(dirBuild, getJarFullName(jarName)), dirTemp, manifest=manifest) + ssjb.delete(dirTemp) + +def buildTranslateJar(): + jarName = "translate" + os.makedirs(dirTemp) + files = ssjb.findFiles(dirBin, "cuchaz/enigma/mapping/*") + files += ssjb.findFiles(dirBin, "cuchaz/enigma/bytecode/*") + ssjb.copyFiles(dirTemp, dirBin, files) + ssjb.copyFile(dirTemp, "license.GPL3.txt", renameTo="license.txt") + ssjb.copyFile(dirTemp, "readme.translate.txt", renameTo="readme.txt") + manifest = ssjb.buildManifest("%s-%s" % (projectName, jarName), version, author) + ssjb.jar(os.path.join(dirBuild, getJarFullName(jarName)), dirTemp, manifest=manifest) + ssjb.delete(dirTemp) + +def taskMain(): + ssjb.delete(dirBuild) + os.makedirs(dirBuild) + buildGuiJar() + buildTranslateJar() + + +ssjb.registerTask("main", taskMain) + + +if __name__ == "__main__": + ssjb.run() + diff --git a/ivy.xml b/ivy.xml new file mode 100644 index 00000000..aa9d1f84 --- /dev/null +++ b/ivy.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/libs/procyon-decompiler-0.5.26-enigma.jar b/libs/procyon-decompiler-0.5.26-enigma.jar new file mode 100644 index 00000000..4a3737ee Binary files /dev/null and b/libs/procyon-decompiler-0.5.26-enigma.jar differ diff --git a/readme.gui.txt b/readme.gui.txt new file mode 100644 index 00000000..81f985a0 --- /dev/null +++ b/readme.gui.txt @@ -0,0 +1,19 @@ + +Enigma v0.6 beta +A tool for deobfuscation of Java bytecode + +Copyright Jeff Martin, 2014 + + +LICENSE + +Enigma is distributed under the GNU General Public license version 3 + +Enigma includes a modified version of Procyon which is distributed under the Apache license version 2. Procyon is copyrighted by Mike Strobel, 2013 + +Enigma includes unmodified versions of the following libraries which are also released under the Apache license version 2. + Guava + Javassist + JSyntaxPane + +Copies of the GNU General Public license verion 3 and the Apache license v2 have been included in this distribution. diff --git a/readme.translate.txt b/readme.translate.txt new file mode 100644 index 00000000..455d16ed --- /dev/null +++ b/readme.translate.txt @@ -0,0 +1,12 @@ + +Enigma v0.6 beta +A tool for deobfuscation of Java bytecode + +Copyright Jeff Martin, 2014 + + +LICENSE + +Enigma is distributed under the GNU General Public license version 3 + +A copy of the GNU General Public license verion 3 has been included in this distribution. diff --git a/readme.txt b/readme.txt deleted file mode 100644 index 81f985a0..00000000 --- a/readme.txt +++ /dev/null @@ -1,19 +0,0 @@ - -Enigma v0.6 beta -A tool for deobfuscation of Java bytecode - -Copyright Jeff Martin, 2014 - - -LICENSE - -Enigma is distributed under the GNU General Public license version 3 - -Enigma includes a modified version of Procyon which is distributed under the Apache license version 2. Procyon is copyrighted by Mike Strobel, 2013 - -Enigma includes unmodified versions of the following libraries which are also released under the Apache license version 2. - Guava - Javassist - JSyntaxPane - -Copies of the GNU General Public license verion 3 and the Apache license v2 have been included in this distribution. diff --git a/ssjb.py b/ssjb.py new file mode 100644 index 00000000..d3e2bb52 --- /dev/null +++ b/ssjb.py @@ -0,0 +1,163 @@ + +# stupidly simple jar builder +# Jeff Martin +# 2015-01-05 + +import os +import sys +import shutil +import subprocess +import zipfile +import tempfile +import re +import fnmatch + + +# setup tasks +tasks = {} + +def registerTask(name, func): + tasks[name] = func + +def run(): + + # get the task name + taskName = "main" + if len(sys.argv) > 1: + taskName = sys.argv[1] + + # find that task + try: + task = tasks[taskName] + except: + print "Couldn't find task: %s" % taskName + return + + # run it! + task() + + +# set up the default main task +def mainTask(): + print "The main task doesn't do anything by default" + +registerTask("main", mainTask) + + +# library of useful functions + +def findFiles(dirSrc, pattern=None): + out = [] + for root, dirs, files in os.walk(dirSrc): + for file in files: + path = os.path.join(root, file)[len(dirSrc) + 1:] + if pattern is None or fnmatch.fnmatch(path, pattern): + out.append(path) + return out + +def copyFile(dirDest, pathSrc, renameTo=None): + (dirParent, filename) = os.path.split(pathSrc) + if renameTo is None: + renameTo = filename + pathDest = os.path.join(dirDest, renameTo) + shutil.copy2(pathSrc, pathDest) + +def copyFiles(dirDest, dirSrc, paths): + for path in paths: + pathSrc = os.path.join(dirSrc, path) + pathDest = os.path.join(dirDest, path) + dirParent = os.path.dirname(pathDest) + if not os.path.isdir(dirParent): + os.makedirs(dirParent) + shutil.copy2(pathSrc, pathDest) + +def patternStringToRegex(patternString): + + # escape special chars + patternString = re.escape(patternString) + + # process ** and * wildcards + replacements = { + re.escape("**"): ".*", + re.escape("*"): "[^" + os.sep + "]+" + } + def getReplacement(match): + print "matched", match + return "a" + patternString = re.compile("(\\\\\*)+").sub(lambda m: replacements[m.group(0)], patternString) + + return re.compile("^" + patternString + "$") + +def matchesAnyPath(path, patternStrings): + for patternString in patternStrings: + pattern = patternStringToRegex(patternString) + # TEMP + print path, pattern.match(path) is not None + if pattern.match(path) is not None: + return True + return False + +def delete(path): + try: + if os.path.isdir(path): + shutil.rmtree(path) + elif os.path.isfile(path): + os.remove(path) + except: + # don't care if it failed + pass + +def buildManifest(title, version, author, mainClass=None): + manifest = { + "Title": title, + "Version": version, + "Created-by": author + } + if mainClass is not None: + manifest["Main-Class"] = mainClass + return manifest + +def jar(pathOut, dirIn, dirRoot=None, manifest=None): + + # build the base args + if dirRoot is None: + dirRoot = dirIn + dirIn = "." + invokeArgs = ["jar"] + filesArgs = ["-C", dirRoot, dirIn] + + if manifest is not None: + # make a temp file for the manifest + tempFile, tempFilename = tempfile.mkstemp(text=True) + try: + # write the manifest + for (key, value) in manifest.iteritems(): + os.write(tempFile, "%s: %s\n" % (key, value)) + os.close(tempFile) + + # build the jar with a manifest + subprocess.call(invokeArgs + ["cmf", tempFilename, pathOut] + filesArgs) + + finally: + os.remove(tempFilename) + else: + # just build the jar without a manifest + subprocess.call(invokeArgs + ["cf", pathOut] + filesArgs) + + print "Wrote jar: %s" % pathOut + +def unpackJar(dirOut, pathJar): + with zipfile.ZipFile(pathJar) as zf: + for member in zf.infolist(): + zf.extract(member, dirOut) + print "Unpacked jar: %s" % pathJar + +def unpackJars(dirOut, dirJars, recursive=False): + for name in os.listdir(dirJars): + path = os.path.join(dirJars, name) + if os.path.isfile(path): + if name[-4:] == ".jar": + unpackJar(dirOut, path) + elif os.path.isdir(path) and recursive: + unpackJars(dirOut, path, recursive) + -- cgit v1.2.3 From 959cb5fd4f9586ec3bd265b452fe25fe1db82e3f Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 13 Jan 2015 23:25:04 -0500 Subject: source format change don't hate me too much if you were planning a big merge. =P --- src/cuchaz/enigma/CommandMain.java | 7 + src/cuchaz/enigma/Constants.java | 8 +- src/cuchaz/enigma/Deobfuscator.java | 595 ++++----- src/cuchaz/enigma/Main.java | 41 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 176 ++- src/cuchaz/enigma/Util.java | 107 +- src/cuchaz/enigma/analysis/Access.java | 30 +- .../enigma/analysis/BehaviorReferenceTreeNode.java | 56 +- src/cuchaz/enigma/analysis/BridgeFixer.java | 51 +- .../analysis/ClassImplementationsTreeNode.java | 48 +- .../enigma/analysis/ClassInheritanceTreeNode.java | 58 +- src/cuchaz/enigma/analysis/EntryReference.java | 94 +- src/cuchaz/enigma/analysis/EntryRenamer.java | 173 ++- .../enigma/analysis/FieldReferenceTreeNode.java | 65 +- src/cuchaz/enigma/analysis/JarClassIterator.java | 107 +- src/cuchaz/enigma/analysis/JarIndex.java | 820 ++++++------- .../analysis/MethodImplementationsTreeNode.java | 66 +- .../enigma/analysis/MethodInheritanceTreeNode.java | 80 +- src/cuchaz/enigma/analysis/ReferenceTreeNode.java | 3 +- src/cuchaz/enigma/analysis/SourceIndex.java | 144 +-- .../analysis/SourceIndexBehaviorVisitor.java | 159 +-- .../enigma/analysis/SourceIndexClassVisitor.java | 97 +- src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 370 +++--- src/cuchaz/enigma/analysis/Token.java | 41 +- src/cuchaz/enigma/analysis/TranslationIndex.java | 93 +- src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 424 +++---- .../enigma/bytecode/BytecodeIndexIterator.java | 121 +- src/cuchaz/enigma/bytecode/BytecodeTools.java | 269 ++--- src/cuchaz/enigma/bytecode/CheckCastIterator.java | 98 +- src/cuchaz/enigma/bytecode/ClassRenamer.java | 103 +- src/cuchaz/enigma/bytecode/ClassTranslator.java | 104 +- src/cuchaz/enigma/bytecode/ConstPoolEditor.java | 295 ++--- src/cuchaz/enigma/bytecode/InfoType.java | 347 +++--- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 103 +- .../enigma/bytecode/MethodParameterWriter.java | 47 +- .../enigma/bytecode/MethodParametersAttribute.java | 65 +- .../bytecode/accessors/ClassInfoAccessor.java | 58 +- .../bytecode/accessors/ConstInfoAccessor.java | 181 ++- .../accessors/InvokeDynamicInfoAccessor.java | 90 +- .../bytecode/accessors/MemberRefInfoAccessor.java | 90 +- .../accessors/MethodHandleInfoAccessor.java | 90 +- .../bytecode/accessors/MethodTypeInfoAccessor.java | 58 +- .../accessors/NameAndTypeInfoAccessor.java | 90 +- .../bytecode/accessors/StringInfoAccessor.java | 58 +- .../bytecode/accessors/Utf8InfoAccessor.java | 23 +- src/cuchaz/enigma/convert/ClassIdentity.java | 436 +++---- src/cuchaz/enigma/convert/ClassMatcher.java | 412 +++---- src/cuchaz/enigma/convert/ClassMatching.java | 152 +-- src/cuchaz/enigma/convert/ClassNamer.java | 53 +- src/cuchaz/enigma/gui/AboutDialog.java | 80 +- src/cuchaz/enigma/gui/BoxHighlightPainter.java | 37 +- src/cuchaz/enigma/gui/BrowserCaret.java | 21 +- src/cuchaz/enigma/gui/ClassListCellRenderer.java | 14 +- src/cuchaz/enigma/gui/ClassSelector.java | 131 +- src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 13 +- .../enigma/gui/ClassSelectorPackageNode.java | 13 +- src/cuchaz/enigma/gui/CrashDialog.java | 77 +- .../enigma/gui/DeobfuscatedHighlightPainter.java | 9 +- src/cuchaz/enigma/gui/Gui.java | 1265 ++++++++------------ src/cuchaz/enigma/gui/GuiController.java | 349 +++--- src/cuchaz/enigma/gui/GuiTricks.java | 23 +- .../enigma/gui/ObfuscatedHighlightPainter.java | 9 +- src/cuchaz/enigma/gui/OtherHighlightPainter.java | 9 +- src/cuchaz/enigma/gui/ProgressDialog.java | 90 +- src/cuchaz/enigma/gui/ReadableToken.java | 22 +- src/cuchaz/enigma/gui/RenameListener.java | 5 +- .../enigma/gui/SelectionHighlightPainter.java | 15 +- src/cuchaz/enigma/gui/TokenListCellRenderer.java | 14 +- src/cuchaz/enigma/mapping/ArgumentEntry.java | 85 +- src/cuchaz/enigma/mapping/ArgumentMapping.java | 26 +- src/cuchaz/enigma/mapping/BehaviorEntry.java | 3 +- .../enigma/mapping/BehaviorEntryFactory.java | 71 +- src/cuchaz/enigma/mapping/ClassEntry.java | 116 +- src/cuchaz/enigma/mapping/ClassMapping.java | 520 ++++---- src/cuchaz/enigma/mapping/ConstructorEntry.java | 99 +- src/cuchaz/enigma/mapping/Entry.java | 11 +- src/cuchaz/enigma/mapping/EntryPair.java | 9 +- src/cuchaz/enigma/mapping/FieldEntry.java | 65 +- src/cuchaz/enigma/mapping/FieldMapping.java | 30 +- .../enigma/mapping/IllegalNameException.java | 29 +- .../enigma/mapping/MappingParseException.java | 10 +- src/cuchaz/enigma/mapping/Mappings.java | 230 ++-- src/cuchaz/enigma/mapping/MappingsReader.java | 218 ++-- src/cuchaz/enigma/mapping/MappingsRenamer.java | 308 ++--- src/cuchaz/enigma/mapping/MappingsWriter.java | 110 +- src/cuchaz/enigma/mapping/MethodEntry.java | 79 +- src/cuchaz/enigma/mapping/MethodMapping.java | 168 ++- src/cuchaz/enigma/mapping/NameValidator.java | 79 +- src/cuchaz/enigma/mapping/SignatureUpdater.java | 88 +- .../enigma/mapping/TranslationDirection.java | 21 +- src/cuchaz/enigma/mapping/Translator.java | 280 ++--- test/cuchaz/enigma/EntryFactory.java | 44 +- test/cuchaz/enigma/TestDeobfuscator.java | 34 +- test/cuchaz/enigma/TestInnerClasses.java | 48 +- .../enigma/TestJarIndexConstructorReferences.java | 160 ++- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 254 ++-- test/cuchaz/enigma/TestJarIndexLoneClass.java | 168 ++- test/cuchaz/enigma/TestSourceIndex.java | 36 +- test/cuchaz/enigma/TestTokensConstructors.java | 137 +-- test/cuchaz/enigma/TokenChecker.java | 46 +- test/cuchaz/enigma/inputs/Keep.java | 8 +- .../enigma/inputs/constructors/BaseClass.java | 16 +- test/cuchaz/enigma/inputs/constructors/Caller.java | 49 +- .../inputs/constructors/DefaultConstructable.java | 3 +- .../enigma/inputs/constructors/SubClass.java | 20 +- .../enigma/inputs/constructors/SubSubClass.java | 9 +- .../enigma/inputs/inheritanceTree/BaseClass.java | 12 +- .../enigma/inputs/inheritanceTree/SubclassA.java | 9 +- .../enigma/inputs/inheritanceTree/SubclassB.java | 19 +- .../inputs/inheritanceTree/SubsubclassAA.java | 17 +- .../enigma/inputs/innerClasses/Anonymous.java | 13 +- .../innerClasses/AnonymousWithScopeArgs.java | 15 +- .../inputs/innerClasses/ConstructorArgs.java | 18 +- test/cuchaz/enigma/inputs/innerClasses/Simple.java | 7 +- test/cuchaz/enigma/inputs/loneClass/LoneClass.java | 10 +- 115 files changed, 5378 insertions(+), 7881 deletions(-) create mode 100644 src/cuchaz/enigma/CommandMain.java diff --git a/src/cuchaz/enigma/CommandMain.java b/src/cuchaz/enigma/CommandMain.java new file mode 100644 index 00000000..7f881749 --- /dev/null +++ b/src/cuchaz/enigma/CommandMain.java @@ -0,0 +1,7 @@ +package cuchaz.enigma; + +public class CommandMain { + + public static void main(String[] args) { + } +} diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 29a08b73..a1ba2e98 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -10,13 +10,11 @@ ******************************************************************************/ package cuchaz.enigma; - -public class Constants -{ +public class Constants { public static final String Name = "Enigma"; public static final String Version = "0.6 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; - public static final int MiB = 1024*1024; // 1 mebibyte - public static final int KiB = 1024; // 1 kebibyte + public static final int MiB = 1024 * 1024; // 1 mebibyte + public static final int KiB = 1024; // 1 kebibyte public static final String NonePackage = "none"; } diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 82c786cb..679518a4 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -61,12 +61,11 @@ import cuchaz.enigma.mapping.MethodMapping; import cuchaz.enigma.mapping.TranslationDirection; import cuchaz.enigma.mapping.Translator; -public class Deobfuscator -{ - public interface ProgressListener - { - void init( int totalWork, String title ); - void onProgress( int numDone, String message ); +public class Deobfuscator { + + public interface ProgressListener { + void init(int totalWork, String title); + void onProgress(int numDone, String message); } private File m_file; @@ -77,121 +76,104 @@ public class Deobfuscator private MappingsRenamer m_renamer; private Map m_translatorCache; - public Deobfuscator( File file ) - throws IOException - { + public Deobfuscator(File file) throws IOException { m_file = file; - m_jar = new JarFile( m_file ); + m_jar = new JarFile(m_file); // build the jar index m_jarIndex = new JarIndex(); - m_jarIndex.indexJar( m_jar, true ); + m_jarIndex.indexJar(m_jar, true); // config the decompiler m_settings = DecompilerSettings.javaDefaults(); - m_settings.setMergeVariables( true ); - m_settings.setForceExplicitImports( true ); - m_settings.setForceExplicitTypeArguments( true ); + m_settings.setMergeVariables(true); + m_settings.setForceExplicitImports(true); + m_settings.setForceExplicitTypeArguments(true); // DEBUG - //m_settings.setShowSyntheticMembers( true ); + // m_settings.setShowSyntheticMembers( true ); // init defaults m_translatorCache = Maps.newTreeMap(); // init mappings - setMappings( new Mappings() ); + setMappings(new Mappings()); } - public String getJarName( ) - { + public String getJarName() { return m_file.getName(); } - public JarIndex getJarIndex( ) - { + public JarIndex getJarIndex() { return m_jarIndex; } - public Mappings getMappings( ) - { + public Mappings getMappings() { return m_mappings; } - public void setMappings( Mappings val ) - { - if( val == null ) - { + + public void setMappings(Mappings val) { + if (val == null) { val = new Mappings(); } // pass 1: look for any classes that got moved to inner classes Map renames = Maps.newHashMap(); - for( ClassMapping classMapping : val.classes() ) - { + for (ClassMapping classMapping : val.classes()) { // make sure we strip the packages off of obfuscated inner classes - String innerClassName = new ClassEntry( classMapping.getObfName() ).getSimpleName(); - String outerClassName = m_jarIndex.getOuterClass( innerClassName ); - if( outerClassName != null ) - { + String innerClassName = new ClassEntry(classMapping.getObfName()).getSimpleName(); + String outerClassName = m_jarIndex.getOuterClass(innerClassName); + if (outerClassName != null) { // build the composite class name String newName = outerClassName + "$" + innerClassName; // add a rename - renames.put( classMapping.getObfName(), newName ); + renames.put(classMapping.getObfName(), newName); - System.out.println( String.format( "Converted class mapping %s to %s", classMapping.getObfName(), newName ) ); + System.out.println(String.format("Converted class mapping %s to %s", classMapping.getObfName(), newName)); } } - for( Map.Entry entry : renames.entrySet() ) - { - val.renameObfClass( entry.getKey(), entry.getValue() ); + for (Map.Entry entry : renames.entrySet()) { + val.renameObfClass(entry.getKey(), entry.getValue()); } // pass 2: look for fields/methods that are actually declared in superclasses - MappingsRenamer renamer = new MappingsRenamer( m_jarIndex, val ); - for( ClassMapping classMapping : val.classes() ) - { - ClassEntry obfClassEntry = new ClassEntry( classMapping.getObfName() ); + MappingsRenamer renamer = new MappingsRenamer(m_jarIndex, val); + for (ClassMapping classMapping : val.classes()) { + ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfName()); // fields - for( FieldMapping fieldMapping : Lists.newArrayList( classMapping.fields() ) ) - { - FieldEntry fieldEntry = new FieldEntry( obfClassEntry, fieldMapping.getObfName() ); - ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass( fieldEntry ); - if( resolvedObfClassEntry != null && !resolvedObfClassEntry.equals( fieldEntry.getClassEntry() ) ) - { - boolean wasMoved = renamer.moveFieldToObfClass( classMapping, fieldMapping, resolvedObfClassEntry ); - if( wasMoved ) - { - System.out.println( String.format( "Moved field %s to class %s", fieldEntry, resolvedObfClassEntry ) ); - } - else - { - System.err.println( String.format( "WARNING: Would move field %s to class %s but the field was already there. Dropping instead.", fieldEntry, resolvedObfClassEntry ) ); + for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { + FieldEntry fieldEntry = new FieldEntry(obfClassEntry, fieldMapping.getObfName()); + ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass(fieldEntry); + if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(fieldEntry.getClassEntry())) { + boolean wasMoved = renamer.moveFieldToObfClass(classMapping, fieldMapping, resolvedObfClassEntry); + if (wasMoved) { + System.out.println(String.format("Moved field %s to class %s", fieldEntry, resolvedObfClassEntry)); + } else { + System.err.println(String.format("WARNING: Would move field %s to class %s but the field was already there. Dropping instead.", fieldEntry, resolvedObfClassEntry)); } } } // methods - for( MethodMapping methodMapping : Lists.newArrayList( classMapping.methods() ) ) - { + for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { // skip constructors - if( methodMapping.isConstructor() ) - { + if (methodMapping.isConstructor()) { continue; } - MethodEntry methodEntry = new MethodEntry( obfClassEntry, methodMapping.getObfName(), methodMapping.getObfSignature() ); - ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass( methodEntry ); - if( resolvedObfClassEntry != null && !resolvedObfClassEntry.equals( methodEntry.getClassEntry() ) ) - { - boolean wasMoved = renamer.moveMethodToObfClass( classMapping, methodMapping, resolvedObfClassEntry ); - if( wasMoved ) - { - System.out.println( String.format( "Moved method %s to class %s", methodEntry, resolvedObfClassEntry ) ); - } - else - { - System.err.println( String.format( "WARNING: Would move method %s to class %s but the method was already there. Dropping instead.", methodEntry, resolvedObfClassEntry ) ); + MethodEntry methodEntry = new MethodEntry( + obfClassEntry, + methodMapping.getObfName(), + methodMapping.getObfSignature() + ); + ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass(methodEntry); + if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(methodEntry.getClassEntry())) { + boolean wasMoved = renamer.moveMethodToObfClass(classMapping, methodMapping, resolvedObfClassEntry); + if (wasMoved) { + System.out.println(String.format("Moved method %s to class %s", methodEntry, resolvedObfClassEntry)); + } else { + System.err.println(String.format("WARNING: Would move method %s to class %s but the method was already there. Dropping instead.", methodEntry, resolvedObfClassEntry)); } } } @@ -201,13 +183,11 @@ public class Deobfuscator // drop mappings that don't match the jar List unknownClasses = Lists.newArrayList(); - for( ClassMapping classMapping : val.classes() ) - { - checkClassMapping( unknownClasses, classMapping ); + for (ClassMapping classMapping : val.classes()) { + checkClassMapping(unknownClasses, classMapping); } - if( !unknownClasses.isEmpty() ) - { - throw new Error( "Unable to find classes in jar: " + unknownClasses ); + if (!unknownClasses.isEmpty()) { + throw new Error("Unable to find classes in jar: " + unknownClasses); } m_mappings = val; @@ -215,453 +195,346 @@ public class Deobfuscator m_translatorCache.clear(); } - private void checkClassMapping( List unknownClasses, ClassMapping classMapping ) - { + private void checkClassMapping(List unknownClasses, ClassMapping classMapping) { // check the class - ClassEntry classEntry = new ClassEntry( classMapping.getObfName() ); - String outerClassName = m_jarIndex.getOuterClass( classEntry.getSimpleName() ); - if( outerClassName != null ) - { - classEntry = new ClassEntry( outerClassName + "$" + classMapping.getObfName() ); + ClassEntry classEntry = new ClassEntry(classMapping.getObfName()); + String outerClassName = m_jarIndex.getOuterClass(classEntry.getSimpleName()); + if (outerClassName != null) { + classEntry = new ClassEntry(outerClassName + "$" + classMapping.getObfName()); } - if( !m_jarIndex.getObfClassEntries().contains( classEntry ) ) - { - unknownClasses.add( classEntry ); + if (!m_jarIndex.getObfClassEntries().contains(classEntry)) { + unknownClasses.add(classEntry); } // check the fields - for( FieldMapping fieldMapping : Lists.newArrayList( classMapping.fields() ) ) - { - FieldEntry fieldEntry = new FieldEntry( classEntry, fieldMapping.getObfName() ); - if( !m_jarIndex.containsObfField( fieldEntry ) ) - { - System.err.println( "WARNING: unable to find field " + fieldEntry + ". dropping mapping." ); - classMapping.removeFieldMapping( fieldMapping ); + for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { + FieldEntry fieldEntry = new FieldEntry(classEntry, fieldMapping.getObfName()); + if (!m_jarIndex.containsObfField(fieldEntry)) { + System.err.println("WARNING: unable to find field " + fieldEntry + ". dropping mapping."); + classMapping.removeFieldMapping(fieldMapping); } } // check methods - for( MethodMapping methodMapping : Lists.newArrayList( classMapping.methods() ) ) - { - BehaviorEntry obfBehaviorEntry = BehaviorEntryFactory.createObf( classEntry, methodMapping ); - if( !m_jarIndex.containsObfBehavior( obfBehaviorEntry ) ) - { - System.err.println( "WARNING: unable to find behavior " + obfBehaviorEntry + ". dropping mapping." ); - classMapping.removeMethodMapping( methodMapping ); - } + for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { + BehaviorEntry obfBehaviorEntry = BehaviorEntryFactory.createObf(classEntry, methodMapping); + if (!m_jarIndex.containsObfBehavior(obfBehaviorEntry)) { + System.err.println("WARNING: unable to find behavior " + obfBehaviorEntry + ". dropping mapping."); + classMapping.removeMethodMapping(methodMapping); + } } // check inner classes - for( ClassMapping innerClassMapping : classMapping.innerClasses() ) - { - checkClassMapping( unknownClasses, innerClassMapping ); + for (ClassMapping innerClassMapping : classMapping.innerClasses()) { + checkClassMapping(unknownClasses, innerClassMapping); } } - - public Translator getTranslator( TranslationDirection direction ) - { - Translator translator = m_translatorCache.get( direction ); - if( translator == null ) - { - translator = m_mappings.getTranslator( direction ); - m_translatorCache.put( direction, translator ); + + public Translator getTranslator(TranslationDirection direction) { + Translator translator = m_translatorCache.get(direction); + if (translator == null) { + translator = m_mappings.getTranslator(direction); + m_translatorCache.put(direction, translator); } return translator; } - public void getSeparatedClasses( List obfClasses, List deobfClasses ) - { - for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() ) - { + public void getSeparatedClasses(List obfClasses, List deobfClasses) { + for (ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries()) { // skip inner classes - if( obfClassEntry.isInnerClass() ) - { + if (obfClassEntry.isInnerClass()) { continue; } // separate the classes - ClassEntry deobfClassEntry = deobfuscateEntry( obfClassEntry ); - if( !deobfClassEntry.equals( obfClassEntry ) ) - { + ClassEntry deobfClassEntry = deobfuscateEntry(obfClassEntry); + if (!deobfClassEntry.equals(obfClassEntry)) { // if the class has a mapping, clearly it's deobfuscated - deobfClasses.add( deobfClassEntry ); - } - else if( !obfClassEntry.getPackageName().equals( Constants.NonePackage ) ) - { + deobfClasses.add(deobfClassEntry); + } else if (!obfClassEntry.getPackageName().equals(Constants.NonePackage)) { // also call it deobufscated if it's not in the none package - deobfClasses.add( obfClassEntry ); - } - else - { + deobfClasses.add(obfClassEntry); + } else { // otherwise, assume it's still obfuscated - obfClasses.add( obfClassEntry ); + obfClasses.add(obfClassEntry); } } } - public CompilationUnit getSourceTree( String obfClassName ) - { + public CompilationUnit getSourceTree(String obfClassName) { // is this class deobfuscated? // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out // the decompiler only sees the deobfuscated class, so we need to load it by the deobfuscated name String lookupClassName = obfClassName; - ClassMapping classMapping = m_mappings.getClassByObf( obfClassName ); - if( classMapping != null && classMapping.getDeobfName() != null ) - { + ClassMapping classMapping = m_mappings.getClassByObf(obfClassName); + if (classMapping != null && classMapping.getDeobfName() != null) { lookupClassName = classMapping.getDeobfName(); } // is this class even in the jar? - if( !m_jarIndex.containsObfClass( new ClassEntry( obfClassName ) ) ) - { + if (!m_jarIndex.containsObfClass(new ClassEntry(obfClassName))) { return null; } // set the type loader - m_settings.setTypeLoader( new TranslatingTypeLoader( + m_settings.setTypeLoader(new TranslatingTypeLoader( m_jar, m_jarIndex, - getTranslator( TranslationDirection.Obfuscating ), - getTranslator( TranslationDirection.Deobfuscating ) - ) ); + getTranslator(TranslationDirection.Obfuscating), + getTranslator(TranslationDirection.Deobfuscating) + )); // decompile it! - TypeDefinition resolvedType = new MetadataSystem( m_settings.getTypeLoader() ).lookupType( lookupClassName ).resolve(); + TypeDefinition resolvedType = new MetadataSystem(m_settings.getTypeLoader()).lookupType(lookupClassName).resolve(); DecompilerContext context = new DecompilerContext(); - context.setCurrentType( resolvedType ); - context.setSettings( m_settings ); - AstBuilder builder = new AstBuilder( context ); - builder.addType( resolvedType ); - builder.runTransformations( null ); + context.setCurrentType(resolvedType); + context.setSettings(m_settings); + AstBuilder builder = new AstBuilder(context); + builder.addType(resolvedType); + builder.runTransformations(null); return builder.getCompilationUnit(); } - public SourceIndex getSourceIndex( CompilationUnit sourceTree, String source ) - { + public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { // build the source index - SourceIndex index = new SourceIndex( source ); - sourceTree.acceptVisitor( new SourceIndexVisitor(), index ); + SourceIndex index = new SourceIndex(source); + sourceTree.acceptVisitor(new SourceIndexVisitor(), index); // DEBUG - //sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); + // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); // resolve all the classes in the source references - for( Token token : index.referenceTokens() ) - { - EntryReference deobfReference = index.getDeobfReference( token ); + for (Token token : index.referenceTokens()) { + EntryReference deobfReference = index.getDeobfReference(token); // get the obfuscated entry - Entry obfEntry = obfuscateEntry( deobfReference.entry ); + Entry obfEntry = obfuscateEntry(deobfReference.entry); // try to resolve the class - ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass( obfEntry ); - if( resolvedObfClassEntry != null && !resolvedObfClassEntry.equals( obfEntry.getClassEntry() ) ) - { + ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass(obfEntry); + if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { // change the class of the entry - obfEntry = obfEntry.cloneToNewClass( resolvedObfClassEntry ); + obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); // save the new deobfuscated reference - deobfReference.entry = deobfuscateEntry( obfEntry ); - index.replaceDeobfReference( token, deobfReference ); + deobfReference.entry = deobfuscateEntry(obfEntry); + index.replaceDeobfReference(token, deobfReference); } // DEBUG - //System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); + // System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); } return index; } - public String getSource( CompilationUnit sourceTree ) - { + public String getSource(CompilationUnit sourceTree) { // render the AST into source StringWriter buf = new StringWriter(); - sourceTree.acceptVisitor( new InsertParenthesesVisitor(), null ); - sourceTree.acceptVisitor( new JavaOutputVisitor( new PlainTextOutput( buf ), m_settings ), null ); + sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); + sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), m_settings), null); return buf.toString(); } - public void writeSources( File dirOut, ProgressListener progress ) - throws IOException - { + public void writeSources(File dirOut, ProgressListener progress) throws IOException { // get the classes to decompile Set classEntries = Sets.newHashSet(); - for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() ) - { + for (ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries()) { // skip inner classes - if( obfClassEntry.isInnerClass() ) - { + if (obfClassEntry.isInnerClass()) { continue; } - classEntries.add( obfClassEntry ); + classEntries.add(obfClassEntry); } - if( progress != null ) - { - progress.init( classEntries.size(), "Decompiling classes..." ); + if (progress != null) { + progress.init(classEntries.size(), "Decompiling classes..."); } // DEOBFUSCATE ALL THE THINGS!! @_@ int i = 0; - for( ClassEntry obfClassEntry : classEntries ) - { - ClassEntry deobfClassEntry = deobfuscateEntry( new ClassEntry( obfClassEntry ) ); - if( progress != null ) - { - progress.onProgress( i++, deobfClassEntry.toString() ); + for (ClassEntry obfClassEntry : classEntries) { + ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry)); + if (progress != null) { + progress.onProgress(i++, deobfClassEntry.toString()); } - try - { + try { // get the source - String source = getSource( getSourceTree( obfClassEntry.getName() ) ); + String source = getSource(getSourceTree(obfClassEntry.getName())); // write the file - File file = new File( dirOut, deobfClassEntry.getName().replace( '.', '/' ) + ".java" ); + File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); file.getParentFile().mkdirs(); - try( FileWriter out = new FileWriter( file ) ) - { - out.write( source ); + try (FileWriter out = new FileWriter(file)) { + out.write(source); } - } - catch( Throwable t ) - { - throw new Error( "Unable to deobfuscate class " + deobfClassEntry.toString() + " (" + obfClassEntry.toString() + ")", t ); + } catch (Throwable t) { + throw new Error("Unable to deobfuscate class " + deobfClassEntry.toString() + " (" + obfClassEntry.toString() + ")", t); } } - if( progress != null ) - { - progress.onProgress( i, "Done!" ); + if (progress != null) { + progress.onProgress(i, "Done!"); } } - public void writeJar( File out, ProgressListener progress ) - { - try( JarOutputStream outJar = new JarOutputStream( new FileOutputStream( out ) ) ) - { - if( progress != null ) - { - progress.init( JarClassIterator.getClassEntries( m_jar ).size(), "Translating classes..." ); + public void writeJar(File out, ProgressListener progress) { + try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { + if (progress != null) { + progress.init(JarClassIterator.getClassEntries(m_jar).size(), "Translating classes..."); } // prep the loader TranslatingTypeLoader loader = new TranslatingTypeLoader( m_jar, m_jarIndex, - getTranslator( TranslationDirection.Obfuscating ), - getTranslator( TranslationDirection.Deobfuscating ) + getTranslator(TranslationDirection.Obfuscating), + getTranslator(TranslationDirection.Deobfuscating) ); int i = 0; - for( CtClass c : JarClassIterator.classes( m_jar ) ) - { - if( progress != null ) - { - progress.onProgress( i++, c.getName() ); + for (CtClass c : JarClassIterator.classes(m_jar)) { + if (progress != null) { + progress.onProgress(i++, c.getName()); } - try - { - c = loader.transformClass( c ); - outJar.putNextEntry( new JarEntry( c.getName().replace( '.', '/' ) + ".class" ) ); - outJar.write( c.toBytecode() ); + try { + c = loader.transformClass(c); + outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); + outJar.write(c.toBytecode()); outJar.closeEntry(); - } - catch( Throwable t ) - { - throw new Error( "Unable to deobfuscate class " + c.getName(), t ); + } catch (Throwable t) { + throw new Error("Unable to deobfuscate class " + c.getName(), t); } } - if( progress != null ) - { - progress.onProgress( i, "Done!" ); + if (progress != null) { + progress.onProgress(i, "Done!"); } outJar.close(); - } - catch( IOException ex ) - { - throw new Error( "Unable to write to Jar file!" ); + } catch (IOException ex) { + throw new Error("Unable to write to Jar file!"); } } - public T obfuscateEntry( T deobfEntry ) - { - if( deobfEntry == null ) - { + public T obfuscateEntry(T deobfEntry) { + if (deobfEntry == null) { return null; } - return getTranslator( TranslationDirection.Obfuscating ).translateEntry( deobfEntry ); + return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry); } - public T deobfuscateEntry( T obfEntry ) - { - if( obfEntry == null ) - { + public T deobfuscateEntry(T obfEntry) { + if (obfEntry == null) { return null; } - return getTranslator( TranslationDirection.Deobfuscating ).translateEntry( obfEntry ); + return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry); } - public EntryReference obfuscateReference( EntryReference deobfReference ) - { - if( deobfReference == null ) - { + public EntryReference obfuscateReference(EntryReference deobfReference) { + if (deobfReference == null) { return null; } return new EntryReference( - obfuscateEntry( deobfReference.entry ), - obfuscateEntry( deobfReference.context ), + obfuscateEntry(deobfReference.entry), + obfuscateEntry(deobfReference.context), deobfReference ); } - public EntryReference deobfuscateReference( EntryReference obfReference ) - { - if( obfReference == null ) - { + public EntryReference deobfuscateReference(EntryReference obfReference) { + if (obfReference == null) { return null; } return new EntryReference( - deobfuscateEntry( obfReference.entry ), - deobfuscateEntry( obfReference.context ), + deobfuscateEntry(obfReference.entry), + deobfuscateEntry(obfReference.context), obfReference ); } - public boolean isObfuscatedIdentifier( Entry obfEntry ) - { - return m_jarIndex.containsObfEntry( obfEntry ); + public boolean isObfuscatedIdentifier(Entry obfEntry) { + return m_jarIndex.containsObfEntry(obfEntry); } - public boolean isRenameable( EntryReference obfReference ) - { - return obfReference.isNamed() && isObfuscatedIdentifier( obfReference.getNameableEntry() ); + public boolean isRenameable(EntryReference obfReference) { + return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry()); } - // NOTE: these methods are a bit messy... oh well - - public boolean hasDeobfuscatedName( Entry obfEntry ) - { - Translator translator = getTranslator( TranslationDirection.Deobfuscating ); - if( obfEntry instanceof ClassEntry ) - { - return translator.translate( (ClassEntry)obfEntry ) != null; - } - else if( obfEntry instanceof FieldEntry ) - { - return translator.translate( (FieldEntry)obfEntry ) != null; - } - else if( obfEntry instanceof MethodEntry ) - { - return translator.translate( (MethodEntry)obfEntry ) != null; - } - else if( obfEntry instanceof ConstructorEntry ) - { + + public boolean hasDeobfuscatedName(Entry obfEntry) { + Translator translator = getTranslator(TranslationDirection.Deobfuscating); + if (obfEntry instanceof ClassEntry) { + return translator.translate((ClassEntry)obfEntry) != null; + } else if (obfEntry instanceof FieldEntry) { + return translator.translate((FieldEntry)obfEntry) != null; + } else if (obfEntry instanceof MethodEntry) { + return translator.translate((MethodEntry)obfEntry) != null; + } else if (obfEntry instanceof ConstructorEntry) { // constructors have no names return false; - } - else if( obfEntry instanceof ArgumentEntry ) - { - return translator.translate( (ArgumentEntry)obfEntry ) != null; - } - else - { - throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); + } else if (obfEntry instanceof ArgumentEntry) { + return translator.translate((ArgumentEntry)obfEntry) != null; + } else { + throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); } } - - public void rename( Entry obfEntry, String newName ) - { - if( obfEntry instanceof ClassEntry ) - { - m_renamer.setClassName( (ClassEntry)obfEntry, Descriptor.toJvmName( newName ) ); - } - else if( obfEntry instanceof FieldEntry ) - { - m_renamer.setFieldName( (FieldEntry)obfEntry, newName ); - } - else if( obfEntry instanceof MethodEntry ) - { - m_renamer.setMethodTreeName( (MethodEntry)obfEntry, newName ); - } - else if( obfEntry instanceof ConstructorEntry ) - { - throw new IllegalArgumentException( "Cannot rename constructors" ); - } - else if( obfEntry instanceof ArgumentEntry ) - { - m_renamer.setArgumentName( (ArgumentEntry)obfEntry, newName ); - } - else - { - throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); + + public void rename(Entry obfEntry, String newName) { + if (obfEntry instanceof ClassEntry) { + m_renamer.setClassName((ClassEntry)obfEntry, Descriptor.toJvmName(newName)); + } else if (obfEntry instanceof FieldEntry) { + m_renamer.setFieldName((FieldEntry)obfEntry, newName); + } else if (obfEntry instanceof MethodEntry) { + m_renamer.setMethodTreeName((MethodEntry)obfEntry, newName); + } else if (obfEntry instanceof ConstructorEntry) { + throw new IllegalArgumentException("Cannot rename constructors"); + } else if (obfEntry instanceof ArgumentEntry) { + m_renamer.setArgumentName((ArgumentEntry)obfEntry, newName); + } else { + throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); } // clear caches m_translatorCache.clear(); } - public void removeMapping( Entry obfEntry ) - { - if( obfEntry instanceof ClassEntry ) - { - m_renamer.removeClassMapping( (ClassEntry)obfEntry ); - } - else if( obfEntry instanceof FieldEntry ) - { - m_renamer.removeFieldMapping( (FieldEntry)obfEntry ); - } - else if( obfEntry instanceof MethodEntry ) - { - m_renamer.removeMethodTreeMapping( (MethodEntry)obfEntry ); - } - else if( obfEntry instanceof ConstructorEntry ) - { - throw new IllegalArgumentException( "Cannot rename constructors" ); - } - else if( obfEntry instanceof ArgumentEntry ) - { - m_renamer.removeArgumentMapping( (ArgumentEntry)obfEntry ); - } - else - { - throw new Error( "Unknown entry type: " + obfEntry ); + public void removeMapping(Entry obfEntry) { + if (obfEntry instanceof ClassEntry) { + m_renamer.removeClassMapping((ClassEntry)obfEntry); + } else if (obfEntry instanceof FieldEntry) { + m_renamer.removeFieldMapping((FieldEntry)obfEntry); + } else if (obfEntry instanceof MethodEntry) { + m_renamer.removeMethodTreeMapping((MethodEntry)obfEntry); + } else if (obfEntry instanceof ConstructorEntry) { + throw new IllegalArgumentException("Cannot rename constructors"); + } else if (obfEntry instanceof ArgumentEntry) { + m_renamer.removeArgumentMapping((ArgumentEntry)obfEntry); + } else { + throw new Error("Unknown entry type: " + obfEntry); } // clear caches m_translatorCache.clear(); } - public void markAsDeobfuscated( Entry obfEntry ) - { - if( obfEntry instanceof ClassEntry ) - { - m_renamer.markClassAsDeobfuscated( (ClassEntry)obfEntry ); - } - else if( obfEntry instanceof FieldEntry ) - { - m_renamer.markFieldAsDeobfuscated( (FieldEntry)obfEntry ); - } - else if( obfEntry instanceof MethodEntry ) - { - m_renamer.markMethodTreeAsDeobfuscated( (MethodEntry)obfEntry ); - } - else if( obfEntry instanceof ConstructorEntry ) - { - throw new IllegalArgumentException( "Cannot rename constructors" ); + public void markAsDeobfuscated(Entry obfEntry) { + if (obfEntry instanceof ClassEntry) { + m_renamer.markClassAsDeobfuscated((ClassEntry)obfEntry); + } else if (obfEntry instanceof FieldEntry) { + m_renamer.markFieldAsDeobfuscated((FieldEntry)obfEntry); + } else if (obfEntry instanceof MethodEntry) { + m_renamer.markMethodTreeAsDeobfuscated((MethodEntry)obfEntry); + } else if (obfEntry instanceof ConstructorEntry) { + throw new IllegalArgumentException("Cannot rename constructors"); + } else if (obfEntry instanceof ArgumentEntry) { + m_renamer.markArgumentAsDeobfuscated((ArgumentEntry)obfEntry); + } else { + throw new Error("Unknown entry type: " + obfEntry); } - else if( obfEntry instanceof ArgumentEntry ) - { - m_renamer.markArgumentAsDeobfuscated( (ArgumentEntry)obfEntry ); - } - else - { - throw new Error( "Unknown entry type: " + obfEntry ); - } - + // clear caches m_translatorCache.clear(); } diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index 73a12db5..f8d3afe2 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -14,46 +14,37 @@ import java.io.File; import cuchaz.enigma.gui.Gui; -public class Main -{ - public static void main( String[] args ) - throws Exception - { +public class Main { + + public static void main(String[] args) throws Exception { Gui gui = new Gui(); // parse command-line args - if( args.length >= 1 ) - { - gui.getController().openJar( getFile( args[0] ) ); + if (args.length >= 1) { + gui.getController().openJar(getFile(args[0])); } - if( args.length >= 2 ) - { - gui.getController().openMappings( getFile( args[1] ) ); + if (args.length >= 2) { + gui.getController().openMappings(getFile(args[1])); } // DEBUG - //gui.getController().openDeclaration( new ClassEntry( "none/ces" ) ); + // gui.getController().openDeclaration( new ClassEntry( "none/ces" ) ); } - private static File getFile( String path ) - { + private static File getFile(String path) { // expand ~ to the home dir - if( path.startsWith( "~" ) ) - { + if (path.startsWith("~")) { // get the home dir - File dirHome = new File( System.getProperty( "user.home" ) ); + File dirHome = new File(System.getProperty("user.home")); // is the path just ~/ or is it ~user/ ? - if( path.startsWith( "~/" ) ) - { - return new File( dirHome, path.substring( 2 ) ); - } - else - { - return new File( dirHome.getParentFile(), path.substring( 1 ) ); + if (path.startsWith("~/")) { + return new File(dirHome, path.substring(2)); + } else { + return new File(dirHome.getParentFile(), path.substring(1)); } } - return new File( path ); + return new File(path); } } diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index e69e5cfe..091f916d 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -38,8 +38,8 @@ import cuchaz.enigma.bytecode.MethodParameterWriter; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Translator; -public class TranslatingTypeLoader implements ITypeLoader -{ +public class TranslatingTypeLoader implements ITypeLoader { + private JarFile m_jar; private JarIndex m_jarIndex; private Translator m_obfuscatingTranslator; @@ -47,13 +47,11 @@ public class TranslatingTypeLoader implements ITypeLoader private Map m_cache; private ClasspathTypeLoader m_defaultTypeLoader; - public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex ) - { - this( jar, jarIndex, new Translator(), new Translator() ); + public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex) { + this(jar, jarIndex, new Translator(), new Translator()); } - public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator ) - { + public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) { m_jar = jar; m_jarIndex = jarIndex; m_obfuscatingTranslator = obfuscatingTranslator; @@ -62,184 +60,154 @@ public class TranslatingTypeLoader implements ITypeLoader m_defaultTypeLoader = new ClasspathTypeLoader(); } - public void clearCache( ) - { + public void clearCache() { m_cache.clear(); } @Override - public boolean tryLoadType( String deobfClassName, Buffer out ) - { + public boolean tryLoadType(String deobfClassName, Buffer out) { // check the cache byte[] data; - if( m_cache.containsKey( deobfClassName ) ) - { - data = m_cache.get( deobfClassName ); - } - else - { - data = loadType( deobfClassName ); - m_cache.put( deobfClassName, data ); + if (m_cache.containsKey(deobfClassName)) { + data = m_cache.get(deobfClassName); + } else { + data = loadType(deobfClassName); + m_cache.put(deobfClassName, data); } - if( data == null ) - { + if (data == null) { // chain to default type loader - return m_defaultTypeLoader.tryLoadType( deobfClassName, out ); + return m_defaultTypeLoader.tryLoadType(deobfClassName, out); } // send the class to the decompiler - out.reset( data.length ); - System.arraycopy( data, 0, out.array(), out.position(), data.length ); - out.position( 0 ); + out.reset(data.length); + System.arraycopy(data, 0, out.array(), out.position(), data.length); + out.position(0); return true; } - public CtClass loadClass( String deobfClassName ) - { - byte[] data = loadType( deobfClassName ); - if( data == null ) - { + public CtClass loadClass(String deobfClassName) { + byte[] data = loadType(deobfClassName); + if (data == null) { return null; } // return a javassist handle for the class - String javaClassFileName = Descriptor.toJavaName( deobfClassName ); + String javaClassFileName = Descriptor.toJavaName(deobfClassName); ClassPool classPool = new ClassPool(); - classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, data ) ); - try - { - return classPool.get( javaClassFileName ); - } - catch( NotFoundException ex ) - { - throw new Error( ex ); + classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, data)); + try { + return classPool.get(javaClassFileName); + } catch (NotFoundException ex) { + throw new Error(ex); } } - private byte[] loadType( String deobfClassName ) - { - ClassEntry deobfClassEntry = new ClassEntry( deobfClassName ); - ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry( deobfClassEntry ); + private byte[] loadType(String deobfClassName) { + ClassEntry deobfClassEntry = new ClassEntry(deobfClassName); + ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(deobfClassEntry); // is this an inner class referenced directly? - String obfOuterClassName = m_jarIndex.getOuterClass( obfClassEntry.getSimpleName() ); - if( obfOuterClassName != null ) - { + String obfOuterClassName = m_jarIndex.getOuterClass(obfClassEntry.getSimpleName()); + if (obfOuterClassName != null) { // this class doesn't really exist. Reference it by outer$inner instead - System.err.println( String.format( "WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassName ) ); + System.err.println(String.format("WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassName)); return null; } /* DEBUG - if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) - { + if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) { System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); } */ // get the jar entry String classFileName; - if( obfClassEntry.isInnerClass() ) - { + if (obfClassEntry.isInnerClass()) { // use just the inner class name for inner classes classFileName = obfClassEntry.getInnerClassName(); - } - else if( obfClassEntry.getPackageName().equals( Constants.NonePackage ) ) - { + } else if (obfClassEntry.getPackageName().equals(Constants.NonePackage)) { // use the outer class simple name for classes in the none package classFileName = obfClassEntry.getSimpleName(); - } - else - { + } else { // otherwise, just use the class name (ie for classes in packages) classFileName = obfClassEntry.getName(); } - JarEntry entry = m_jar.getJarEntry( classFileName + ".class" ); - if( entry == null ) - { + JarEntry entry = m_jar.getJarEntry(classFileName + ".class"); + if (entry == null) { return null; } - - try - { + + try { // read the class file into a buffer ByteArrayOutputStream data = new ByteArrayOutputStream(); - byte[] buf = new byte[1024*1024]; // 1 KiB - InputStream in = m_jar.getInputStream( entry ); - while( true ) - { - int bytesRead = in.read( buf ); - if( bytesRead <= 0 ) - { + byte[] buf = new byte[1024 * 1024]; // 1 KiB + InputStream in = m_jar.getInputStream(entry); + while (true) { + int bytesRead = in.read(buf); + if (bytesRead <= 0) { break; } - data.write( buf, 0, bytesRead ); + data.write(buf, 0, bytesRead); } data.close(); in.close(); buf = data.toByteArray(); // load the javassist handle to the raw class - String javaClassFileName = Descriptor.toJavaName( classFileName ); + String javaClassFileName = Descriptor.toJavaName(classFileName); ClassPool classPool = new ClassPool(); - classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); - CtClass c = classPool.get( javaClassFileName ); + classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, buf)); + CtClass c = classPool.get(javaClassFileName); - c = transformClass( c ); + c = transformClass(c); // sanity checking - assertClassName( c, deobfClassEntry ); + assertClassName(c, deobfClassEntry); // DEBUG - //Util.writeClass( c ); - + // Util.writeClass( c ); + // we have a transformed class! return c.toBytecode(); - } - catch( IOException | NotFoundException | CannotCompileException ex ) - { - throw new Error( ex ); + } catch (IOException | NotFoundException | CannotCompileException ex) { + throw new Error(ex); } } - public CtClass transformClass( CtClass c ) - throws IOException, NotFoundException, CannotCompileException - { + public CtClass transformClass(CtClass c) throws IOException, NotFoundException, CannotCompileException { // we moved a lot of classes out of the default package into the none package // make sure all the class references are consistent - ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); // reconstruct inner classes - new InnerClassWriter( m_jarIndex ).write( c ); + new InnerClassWriter(m_jarIndex).write(c); // re-get the javassist handle since we changed class names - ClassEntry obfClassEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); - String javaClassReconstructedName = Descriptor.toJavaName( obfClassEntry.getName() ); + ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + String javaClassReconstructedName = Descriptor.toJavaName(obfClassEntry.getName()); ClassPool classPool = new ClassPool(); - classPool.insertClassPath( new ByteArrayClassPath( javaClassReconstructedName, c.toBytecode() ) ); - c = classPool.get( javaClassReconstructedName ); + classPool.insertClassPath(new ByteArrayClassPath(javaClassReconstructedName, c.toBytecode())); + c = classPool.get(javaClassReconstructedName); // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong) - assertClassName( c, obfClassEntry ); + assertClassName(c, obfClassEntry); // do all kinds of deobfuscating transformations on the class - new BridgeFixer( m_jarIndex ).fixBridges( c ); - new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); - new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); + new BridgeFixer(m_jarIndex).fixBridges(c); + new MethodParameterWriter(m_deobfuscatingTranslator).writeMethodArguments(c); + new ClassTranslator(m_deobfuscatingTranslator).translate(c); return c; } - - private void assertClassName( CtClass c, ClassEntry obfClassEntry ) - { - String name1 = Descriptor.toJvmName( c.getName() ); - assert( name1.equals( obfClassEntry.getName() ) ) - : String.format( "Looking for %s, instead found %s", obfClassEntry.getName(), name1 ); + + private void assertClassName(CtClass c, ClassEntry obfClassEntry) { + String name1 = Descriptor.toJvmName(c.getName()); + assert (name1.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name1); - String name2 = Descriptor.toJvmName( c.getClassFile().getName() ); - assert( name2.equals( obfClassEntry.getName() ) ) - : String.format( "Looking for %s, instead found %s", obfClassEntry.getName(), name2 ); + String name2 = Descriptor.toJvmName(c.getClassFile().getName()); + assert (name2.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name2); } } diff --git a/src/cuchaz/enigma/Util.java b/src/cuchaz/enigma/Util.java index 678de546..7f04bda0 100644 --- a/src/cuchaz/enigma/Util.java +++ b/src/cuchaz/enigma/Util.java @@ -28,108 +28,77 @@ import javassist.bytecode.Descriptor; import com.google.common.io.CharStreams; - -public class Util -{ - public static int combineHashesOrdered( Object ... objs ) - { - return combineHashesOrdered( Arrays.asList( objs ) ); +public class Util { + + public static int combineHashesOrdered(Object... objs) { + return combineHashesOrdered(Arrays.asList(objs)); } - public static int combineHashesOrdered( Iterable objs ) - { + public static int combineHashesOrdered(Iterable objs) { final int prime = 67; int result = 1; - for( Object obj : objs ) - { + for (Object obj : objs) { result *= prime; - if( obj != null ) - { + if (obj != null) { result += obj.hashCode(); } } return result; } - public static void closeQuietly( Closeable closeable ) - { - if( closeable != null ) - { - try - { + public static void closeQuietly(Closeable closeable) { + if (closeable != null) { + try { closeable.close(); - } - catch( IOException ex ) - { + } catch (IOException ex) { // just ignore any further exceptions } } } - public static void closeQuietly( JarFile jarFile ) - { + public static void closeQuietly(JarFile jarFile) { // silly library should implement Closeable... - if( jarFile != null ) - { - try - { + if (jarFile != null) { + try { jarFile.close(); - } - catch( IOException ex ) - { + } catch (IOException ex) { // just ignore any further exceptions } } } - - public static String readStreamToString( InputStream in ) - throws IOException - { - return CharStreams.toString( new InputStreamReader( in, "UTF-8" ) ); + + public static String readStreamToString(InputStream in) throws IOException { + return CharStreams.toString(new InputStreamReader(in, "UTF-8")); } - public static String readResourceToString( String path ) - throws IOException - { - InputStream in = Util.class.getResourceAsStream( path ); - if( in == null ) - { - throw new IllegalArgumentException( "Resource not found! " + path ); + public static String readResourceToString(String path) throws IOException { + InputStream in = Util.class.getResourceAsStream(path); + if (in == null) { + throw new IllegalArgumentException("Resource not found! " + path); } - return readStreamToString( in ); + return readStreamToString(in); } - public static void openUrl( String url ) - { - if( Desktop.isDesktopSupported() ) - { + public static void openUrl(String url) { + if (Desktop.isDesktopSupported()) { Desktop desktop = Desktop.getDesktop(); - try - { - desktop.browse( new URI( url ) ); - } - catch( IOException ex ) - { - throw new Error( ex ); - } - catch( URISyntaxException ex ) - { - throw new IllegalArgumentException( ex ); + try { + desktop.browse(new URI(url)); + } catch (IOException ex) { + throw new Error(ex); + } catch (URISyntaxException ex) { + throw new IllegalArgumentException(ex); } } } - public static void writeClass( CtClass c ) - { - String name = Descriptor.toJavaName( c.getName() ); - File file = new File( name + ".class" ); - try( FileOutputStream out = new FileOutputStream( file ) ) - { - out.write( c.toBytecode() ); - } - catch( IOException | CannotCompileException ex ) - { - throw new Error( ex ); + public static void writeClass(CtClass c) { + String name = Descriptor.toJavaName(c.getName()); + File file = new File(name + ".class"); + try (FileOutputStream out = new FileOutputStream(file)) { + out.write(c.toBytecode()); + } catch (IOException | CannotCompileException ex) { + throw new Error(ex); } } } diff --git a/src/cuchaz/enigma/analysis/Access.java b/src/cuchaz/enigma/analysis/Access.java index e35bb21b..8d3409ac 100644 --- a/src/cuchaz/enigma/analysis/Access.java +++ b/src/cuchaz/enigma/analysis/Access.java @@ -15,37 +15,29 @@ import java.lang.reflect.Modifier; import javassist.CtBehavior; import javassist.CtField; -public enum Access -{ +public enum Access { + Public, Protected, Private; - public static Access get( CtBehavior behavior ) - { - return get( behavior.getModifiers() ); + public static Access get(CtBehavior behavior) { + return get(behavior.getModifiers()); } - public static Access get( CtField field ) - { - return get( field.getModifiers() ); + public static Access get(CtField field) { + return get(field.getModifiers()); } - public static Access get( int modifiers ) - { - if( Modifier.isPublic( modifiers ) ) - { + public static Access get(int modifiers) { + if (Modifier.isPublic(modifiers)) { return Public; - } - else if( Modifier.isProtected( modifiers ) ) - { + } else if (Modifier.isProtected(modifiers)) { return Protected; - } - else if( Modifier.isPrivate( modifiers ) ) - { + } else if (Modifier.isPrivate(modifiers)) { return Private; } // assume public by default return Public; } -} \ No newline at end of file +} diff --git a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java index 20f1d472..9adac5e9 100644 --- a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java @@ -21,8 +21,8 @@ import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.Translator; -public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode -{ +public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { + private static final long serialVersionUID = -3658163700783307520L; private Translator m_deobfuscatingTranslator; @@ -30,15 +30,13 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements private EntryReference m_reference; private Access m_access; - public BehaviorReferenceTreeNode( Translator deobfuscatingTranslator, BehaviorEntry entry ) - { + public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) { m_deobfuscatingTranslator = deobfuscatingTranslator; m_entry = entry; m_reference = null; } - public BehaviorReferenceTreeNode( Translator deobfuscatingTranslator, EntryReference reference, Access access ) - { + public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference reference, Access access) { m_deobfuscatingTranslator = deobfuscatingTranslator; m_entry = reference.entry; m_reference = reference; @@ -46,60 +44,48 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements } @Override - public BehaviorEntry getEntry( ) - { + public BehaviorEntry getEntry() { return m_entry; } @Override - public EntryReference getReference( ) - { + public EntryReference getReference() { return m_reference; } @Override - public String toString( ) - { - if( m_reference != null ) - { - return String.format( "%s (%s)", m_deobfuscatingTranslator.translateEntry( m_reference.context ), m_access ); + public String toString() { + if (m_reference != null) { + return String.format("%s (%s)", m_deobfuscatingTranslator.translateEntry(m_reference.context), m_access); } - return m_deobfuscatingTranslator.translateEntry( m_entry ).toString(); + return m_deobfuscatingTranslator.translateEntry(m_entry).toString(); } - public void load( JarIndex index, boolean recurse ) - { + public void load(JarIndex index, boolean recurse) { // get all the child nodes - for( EntryReference reference : index.getBehaviorReferences( m_entry ) ) - { - add( new BehaviorReferenceTreeNode( m_deobfuscatingTranslator, reference, index.getAccess( m_entry ) ) ); + for (EntryReference reference : index.getBehaviorReferences(m_entry)) { + add(new BehaviorReferenceTreeNode(m_deobfuscatingTranslator, reference, index.getAccess(m_entry))); } - if( recurse && children != null ) - { - for( Object child : children ) - { - if( child instanceof BehaviorReferenceTreeNode ) - { + if (recurse && children != null) { + for (Object child : children) { + if (child instanceof BehaviorReferenceTreeNode) { BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode)child; // don't recurse into ancestor Set ancestors = Sets.newHashSet(); TreeNode n = (TreeNode)node; - while( n.getParent() != null ) - { + while (n.getParent() != null) { n = n.getParent(); - if( n instanceof BehaviorReferenceTreeNode ) - { - ancestors.add( ((BehaviorReferenceTreeNode)n).getEntry() ); + if (n instanceof BehaviorReferenceTreeNode) { + ancestors.add( ((BehaviorReferenceTreeNode)n).getEntry()); } } - if( ancestors.contains( node.getEntry() ) ) - { + if (ancestors.contains(node.getEntry())) { continue; } - node.load( index, true ); + node.load(index, true); } } } diff --git a/src/cuchaz/enigma/analysis/BridgeFixer.java b/src/cuchaz/enigma/analysis/BridgeFixer.java index 112b864a..ad23b000 100644 --- a/src/cuchaz/enigma/analysis/BridgeFixer.java +++ b/src/cuchaz/enigma/analysis/BridgeFixer.java @@ -20,61 +20,48 @@ import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.MethodEntry; -public class BridgeFixer -{ +public class BridgeFixer { + private JarIndex m_index; - public BridgeFixer( JarIndex index ) - { + public BridgeFixer(JarIndex index) { m_index = index; } - - public void fixBridges( CtClass c ) - { + + public void fixBridges(CtClass c) { // rename declared methods - for( CtMethod method : c.getDeclaredMethods() ) - { + for (CtMethod method : c.getDeclaredMethods()) { // get the method entry MethodEntry methodEntry = new MethodEntry( - new ClassEntry( Descriptor.toJvmName( c.getName() ) ), + new ClassEntry(Descriptor.toJvmName(c.getName())), method.getName(), method.getSignature() ); - MethodEntry bridgeMethodEntry = m_index.getBridgeMethod( methodEntry ); - if( bridgeMethodEntry != null ) - { + MethodEntry bridgeMethodEntry = m_index.getBridgeMethod(methodEntry); + if (bridgeMethodEntry != null) { // fix this bridged method - method.setName( bridgeMethodEntry.getName() ); + method.setName(bridgeMethodEntry.getName()); } } // rename method references // translate all the field and method references in the code by editing the constant pool ConstPool constants = c.getClassFile().getConstPool(); - ConstPoolEditor editor = new ConstPoolEditor( constants ); - for( int i=1; i nodes = Lists.newArrayList(); - for( String implementingClassName : index.getImplementingClasses( m_entry.getClassName() ) ) - { - nodes.add( new ClassImplementationsTreeNode( m_deobfuscatingTranslator, new ClassEntry( implementingClassName ) ) ); + for (String implementingClassName : index.getImplementingClasses(m_entry.getClassName())) { + nodes.add(new ClassImplementationsTreeNode(m_deobfuscatingTranslator, new ClassEntry(implementingClassName))); } // add them to this node - for( ClassImplementationsTreeNode node : nodes ) - { - this.add( node ); + for (ClassImplementationsTreeNode node : nodes) { + this.add(node); } } - public static ClassImplementationsTreeNode findNode( ClassImplementationsTreeNode node, MethodEntry entry ) - { + public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { // is this the node? - if( node.m_entry.equals( entry ) ) - { + if (node.m_entry.equals(entry)) { return node; } // recurse - for( int i=0; i nodes = Lists.newArrayList(); - for( String subclassName : ancestries.getSubclassNames( m_obfClassName ) ) - { - nodes.add( new ClassInheritanceTreeNode( m_deobfuscatingTranslator, subclassName ) ); + for (String subclassName : ancestries.getSubclassNames(m_obfClassName)) { + nodes.add(new ClassInheritanceTreeNode(m_deobfuscatingTranslator, subclassName)); } // add them to this node - for( ClassInheritanceTreeNode node : nodes ) - { - this.add( node ); + for (ClassInheritanceTreeNode node : nodes) { + this.add(node); } - if( recurse ) - { - for( ClassInheritanceTreeNode node : nodes ) - { - node.load( ancestries, true ); + if (recurse) { + for (ClassInheritanceTreeNode node : nodes) { + node.load(ancestries, true); } } } - - public static ClassInheritanceTreeNode findNode( ClassInheritanceTreeNode node, ClassEntry entry ) - { + + public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { // is this the node? - if( node.getObfClassName().equals( entry.getName() ) ) - { + if (node.getObfClassName().equals(entry.getName())) { return node; } // recurse - for( int i=0; i -{ - private static final List ConstructorNonNames = Arrays.asList( "this", "super", "static" ); +public class EntryReference { + + private static final List ConstructorNonNames = Arrays.asList("this", "super", "static"); public E entry; public C context; private boolean m_isNamed; - public EntryReference( E entry, String sourceName ) - { - this( entry, sourceName, null ); + public EntryReference(E entry, String sourceName) { + this(entry, sourceName, null); } - public EntryReference( E entry, String sourceName, C context ) - { - if( entry == null ) - { - throw new IllegalArgumentException( "Entry cannot be null!" ); + public EntryReference(E entry, String sourceName, C context) { + if (entry == null) { + throw new IllegalArgumentException("Entry cannot be null!"); } this.entry = entry; this.context = context; m_isNamed = sourceName != null && sourceName.length() > 0; - if( entry instanceof ConstructorEntry && ConstructorNonNames.contains( sourceName ) ) - { + if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) { m_isNamed = false; } } - public EntryReference( E entry, C context, EntryReference other ) - { + public EntryReference(E entry, C context, EntryReference other) { this.entry = entry; this.context = context; m_isNamed = other.m_isNamed; } - public ClassEntry getLocationClassEntry( ) - { - if( context != null ) - { + public ClassEntry getLocationClassEntry() { + if (context != null) { return context.getClassEntry(); } return entry.getClassEntry(); } - public boolean isNamed( ) - { + public boolean isNamed() { return m_isNamed; } - public Entry getNameableEntry( ) - { - if( entry instanceof ConstructorEntry ) - { + public Entry getNameableEntry() { + if (entry instanceof ConstructorEntry) { // renaming a constructor really means renaming the class return entry.getClassEntry(); } return entry; } - public String getNamableName( ) - { - if( getNameableEntry() instanceof ClassEntry ) - { + public String getNamableName() { + if (getNameableEntry() instanceof ClassEntry) { ClassEntry classEntry = (ClassEntry)getNameableEntry(); - if( classEntry.isInnerClass() ) - { + if (classEntry.isInnerClass()) { // make sure we only rename the inner class name return classEntry.getInnerClassName(); } @@ -95,55 +82,44 @@ public class EntryReference } @Override - public int hashCode( ) - { - if( context != null ) - { - return Util.combineHashesOrdered( entry.hashCode(), context.hashCode() ); + public int hashCode() { + if (context != null) { + return Util.combineHashesOrdered(entry.hashCode(), context.hashCode()); } return entry.hashCode(); } @Override - public boolean equals( Object other ) - { - if( other instanceof EntryReference ) - { - return equals( (EntryReference)other ); + public boolean equals(Object other) { + if (other instanceof EntryReference) { + return equals((EntryReference)other); } return false; } - public boolean equals( EntryReference other ) - { + public boolean equals(EntryReference other) { // check entry first - boolean isEntrySame = entry.equals( other.entry ); - if( !isEntrySame ) - { + boolean isEntrySame = entry.equals(other.entry); + if (!isEntrySame) { return false; } // check caller - if( context == null && other.context == null ) - { + if (context == null && other.context == null) { return true; - } - else if( context != null && other.context != null ) - { - return context.equals( other.context ); + } else if (context != null && other.context != null) { + return context.equals(other.context); } return false; } @Override - public String toString( ) - { + public String toString() { StringBuilder buf = new StringBuilder(); - buf.append( entry ); - if( context != null ) - { - buf.append( " called from " ); - buf.append( context ); + buf.append(entry); + if (context != null) { + buf.append(" called from "); + buf.append(context); } return buf.toString(); } diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java index 2d59fe9d..b54489cd 100644 --- a/src/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -26,100 +26,83 @@ import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; -public class EntryRenamer -{ - public static void renameClassesInSet( Map renames, Set set ) - { +public class EntryRenamer { + + public static void renameClassesInSet(Map renames, Set set) { List entries = Lists.newArrayList(); - for( T val : set ) - { - entries.add( renameClassesInThing( renames, val ) ); + for (T val : set) { + entries.add(renameClassesInThing(renames, val)); } set.clear(); - set.addAll( entries ); + set.addAll(entries); } - public static void renameClassesInMap( Map renames, Map map ) - { + public static void renameClassesInMap(Map renames, Map map) { // for each key/value pair... Set> entriesToAdd = Sets.newHashSet(); - for( Map.Entry entry : map.entrySet() ) - { - entriesToAdd.add( new AbstractMap.SimpleEntry( - renameClassesInThing( renames, entry.getKey() ), - renameClassesInThing( renames, entry.getValue() ) - ) ); + for (Map.Entry entry : map.entrySet()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameClassesInThing(renames, entry.getKey()), + renameClassesInThing(renames, entry.getValue()) + )); } map.clear(); - for( Map.Entry entry : entriesToAdd ) - { - map.put( entry.getKey(), entry.getValue() ); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); } } - public static void renameClassesInMultimap( Map renames, Multimap map ) - { + public static void renameClassesInMultimap(Map renames, Multimap map) { // for each key/value pair... Set> entriesToAdd = Sets.newHashSet(); - for( Map.Entry entry : map.entries() ) - { - entriesToAdd.add( new AbstractMap.SimpleEntry( - renameClassesInThing( renames, entry.getKey() ), - renameClassesInThing( renames, entry.getValue() ) - ) ); + for (Map.Entry entry : map.entries()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameClassesInThing(renames, entry.getKey()), + renameClassesInThing(renames, entry.getValue()) + )); } map.clear(); - for( Map.Entry entry : entriesToAdd ) - { - map.put( entry.getKey(), entry.getValue() ); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); } } - public static void renameMethodsInMultimap( Map renames, Multimap map ) - { + public static void renameMethodsInMultimap(Map renames, Multimap map) { // for each key/value pair... Set> entriesToAdd = Sets.newHashSet(); - for( Map.Entry entry : map.entries() ) - { - entriesToAdd.add( new AbstractMap.SimpleEntry( - renameMethodsInThing( renames, entry.getKey() ), - renameMethodsInThing( renames, entry.getValue() ) - ) ); + for (Map.Entry entry : map.entries()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameMethodsInThing(renames, entry.getKey()), + renameMethodsInThing(renames, entry.getValue()) + )); } map.clear(); - for( Map.Entry entry : entriesToAdd ) - { - map.put( entry.getKey(), entry.getValue() ); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); } } - public static void renameMethodsInMap( Map renames, Map map ) - { + public static void renameMethodsInMap(Map renames, Map map) { // for each key/value pair... Set> entriesToAdd = Sets.newHashSet(); - for( Map.Entry entry : map.entrySet() ) - { - entriesToAdd.add( new AbstractMap.SimpleEntry( - renameMethodsInThing( renames, entry.getKey() ), - renameMethodsInThing( renames, entry.getValue() ) - ) ); + for (Map.Entry entry : map.entrySet()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameMethodsInThing(renames, entry.getKey()), + renameMethodsInThing(renames, entry.getValue()) + )); } map.clear(); - for( Map.Entry entry : entriesToAdd ) - { - map.put( entry.getKey(), entry.getValue() ); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); } } - @SuppressWarnings( "unchecked" ) - public static T renameMethodsInThing( Map renames, T thing ) - { - if( thing instanceof MethodEntry ) - { + @SuppressWarnings("unchecked") + public static T renameMethodsInThing(Map renames, T thing) { + if (thing instanceof MethodEntry) { MethodEntry methodEntry = (MethodEntry)thing; - MethodEntry newMethodEntry = renames.get( methodEntry ); - if( newMethodEntry != null ) - { + MethodEntry newMethodEntry = renames.get(methodEntry); + if (newMethodEntry != null) { return (T)new MethodEntry( methodEntry.getClassEntry(), newMethodEntry.getName(), @@ -127,81 +110,59 @@ public class EntryRenamer ); } return thing; - } - else if( thing instanceof ArgumentEntry ) - { + } else if (thing instanceof ArgumentEntry) { ArgumentEntry argumentEntry = (ArgumentEntry)thing; return (T)new ArgumentEntry( - renameMethodsInThing( renames, argumentEntry.getBehaviorEntry() ), + renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName() ); - } - else if( thing instanceof EntryReference ) - { + } else if (thing instanceof EntryReference) { EntryReference reference = (EntryReference)thing; - reference.entry = renameMethodsInThing( renames, reference.entry ); - reference.context = renameMethodsInThing( renames, reference.context ); + reference.entry = renameMethodsInThing(renames, reference.entry); + reference.context = renameMethodsInThing(renames, reference.context); return thing; } return thing; } - - @SuppressWarnings( "unchecked" ) - public static T renameClassesInThing( Map renames, T thing ) - { - if( thing instanceof String ) - { + + @SuppressWarnings("unchecked") + public static T renameClassesInThing(Map renames, T thing) { + if (thing instanceof String) { String stringEntry = (String)thing; - if( renames.containsKey( stringEntry ) ) - { - return (T)renames.get( stringEntry ); + if (renames.containsKey(stringEntry)) { + return (T)renames.get(stringEntry); } - } - else if( thing instanceof ClassEntry ) - { + } else if (thing instanceof ClassEntry) { ClassEntry classEntry = (ClassEntry)thing; - return (T)new ClassEntry( renameClassesInThing( renames, classEntry.getClassName() ) ); - } - else if( thing instanceof FieldEntry ) - { + return (T)new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); + } else if (thing instanceof FieldEntry) { FieldEntry fieldEntry = (FieldEntry)thing; - return (T)new FieldEntry( - renameClassesInThing( renames, fieldEntry.getClassEntry() ), - fieldEntry.getName() - ); - } - else if( thing instanceof ConstructorEntry ) - { + return (T)new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName()); + } else if (thing instanceof ConstructorEntry) { ConstructorEntry constructorEntry = (ConstructorEntry)thing; return (T)new ConstructorEntry( - renameClassesInThing( renames, constructorEntry.getClassEntry() ), + renameClassesInThing(renames, constructorEntry.getClassEntry()), constructorEntry.getSignature() ); - } - else if( thing instanceof MethodEntry ) - { + } else if (thing instanceof MethodEntry) { MethodEntry methodEntry = (MethodEntry)thing; return (T)new MethodEntry( - renameClassesInThing( renames, methodEntry.getClassEntry() ), + renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), methodEntry.getSignature() ); - } - else if( thing instanceof ArgumentEntry ) - { + } else if (thing instanceof ArgumentEntry) { ArgumentEntry argumentEntry = (ArgumentEntry)thing; return (T)new ArgumentEntry( - renameClassesInThing( renames, argumentEntry.getBehaviorEntry() ), + renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName() ); - } - else if( thing instanceof EntryReference ) - { + } else if (thing instanceof EntryReference) { EntryReference reference = (EntryReference)thing; - reference.entry = renameClassesInThing( renames, reference.entry ); - reference.context = renameClassesInThing( renames, reference.context ); + reference.entry = renameClassesInThing(renames, reference.entry); + reference.context = renameClassesInThing(renames, reference.context); return thing; } diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java index 2652f64a..2173eea6 100644 --- a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -16,24 +16,22 @@ import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Translator; -public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode -{ +public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { + private static final long serialVersionUID = -7934108091928699835L; private Translator m_deobfuscatingTranslator; private FieldEntry m_entry; private EntryReference m_reference; private Access m_access; - - public FieldReferenceTreeNode( Translator deobfuscatingTranslator, FieldEntry entry ) - { + + public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { m_deobfuscatingTranslator = deobfuscatingTranslator; m_entry = entry; m_reference = null; } - private FieldReferenceTreeNode( Translator deobfuscatingTranslator, EntryReference reference, Access access ) - { + private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference reference, Access access) { m_deobfuscatingTranslator = deobfuscatingTranslator; m_entry = reference.entry; m_reference = reference; @@ -41,56 +39,41 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re } @Override - public FieldEntry getEntry( ) - { + public FieldEntry getEntry() { return m_entry; } @Override - public EntryReference getReference( ) - { + public EntryReference getReference() { return m_reference; } @Override - public String toString( ) - { - if( m_reference != null ) - { - return String.format( "%s (%s)", m_deobfuscatingTranslator.translateEntry( m_reference.context ), m_access ); + public String toString() { + if (m_reference != null) { + return String.format("%s (%s)", m_deobfuscatingTranslator.translateEntry(m_reference.context), m_access); } - return m_deobfuscatingTranslator.translateEntry( m_entry ).toString(); + return m_deobfuscatingTranslator.translateEntry(m_entry).toString(); } - public void load( JarIndex index, boolean recurse ) - { + public void load(JarIndex index, boolean recurse) { // get all the child nodes - if( m_reference == null ) - { - for( EntryReference reference : index.getFieldReferences( m_entry ) ) - { - add( new FieldReferenceTreeNode( m_deobfuscatingTranslator, reference, index.getAccess( m_entry ) ) ); + if (m_reference == null) { + for (EntryReference reference : index.getFieldReferences(m_entry)) { + add(new FieldReferenceTreeNode(m_deobfuscatingTranslator, reference, index.getAccess(m_entry))); } - } - else - { - for( EntryReference reference : index.getBehaviorReferences( m_reference.context ) ) - { - add( new BehaviorReferenceTreeNode( m_deobfuscatingTranslator, reference, index.getAccess( m_reference.context ) ) ); + } else { + for (EntryReference reference : index.getBehaviorReferences(m_reference.context)) { + add(new BehaviorReferenceTreeNode(m_deobfuscatingTranslator, reference, index.getAccess(m_reference.context))); } } - if( recurse && children != null ) - { - for( Object node : children ) - { - if( node instanceof BehaviorReferenceTreeNode ) - { - ((BehaviorReferenceTreeNode)node).load( index, true ); - } - else if( node instanceof FieldReferenceTreeNode ) - { - ((FieldReferenceTreeNode)node).load( index, true ); + if (recurse && children != null) { + for (Object node : children) { + if (node instanceof BehaviorReferenceTreeNode) { + ((BehaviorReferenceTreeNode)node).load(index, true); + } else if (node instanceof FieldReferenceTreeNode) { + ((FieldReferenceTreeNode)node).load(index, true); } } } diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java index f65b8e79..8d9947c1 100644 --- a/src/cuchaz/enigma/analysis/JarClassIterator.java +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -30,132 +30,107 @@ import com.google.common.collect.Lists; import cuchaz.enigma.Constants; import cuchaz.enigma.mapping.ClassEntry; -public class JarClassIterator implements Iterator -{ +public class JarClassIterator implements Iterator { + private JarFile m_jar; private Iterator m_iter; - public JarClassIterator( JarFile jar ) - { + public JarClassIterator(JarFile jar) { m_jar = jar; // get the jar entries that correspond to classes List classEntries = Lists.newArrayList(); Enumeration entries = m_jar.entries(); - while( entries.hasMoreElements() ) - { + while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); // is this a class file? - if( entry.getName().endsWith( ".class" ) ) - { - classEntries.add( entry ); + if (entry.getName().endsWith(".class")) { + classEntries.add(entry); } } m_iter = classEntries.iterator(); } @Override - public boolean hasNext( ) - { + public boolean hasNext() { return m_iter.hasNext(); } - + @Override - public CtClass next( ) - { + public CtClass next() { JarEntry entry = m_iter.next(); - try - { - return getClass( m_jar, entry ); - } - catch( IOException | NotFoundException ex ) - { - throw new Error( "Unable to load class: " + entry.getName() ); + try { + return getClass(m_jar, entry); + } catch (IOException | NotFoundException ex) { + throw new Error("Unable to load class: " + entry.getName()); } } - + @Override - public void remove( ) - { + public void remove() { throw new UnsupportedOperationException(); } - public static List getClassEntries( JarFile jar ) - { + public static List getClassEntries(JarFile jar) { List classEntries = Lists.newArrayList(); Enumeration entries = jar.entries(); - while( entries.hasMoreElements() ) - { + while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); // is this a class file? - if( !entry.isDirectory() && entry.getName().endsWith( ".class" ) ) - { - classEntries.add( getClassEntry( entry ) ); + if (!entry.isDirectory() && entry.getName().endsWith(".class")) { + classEntries.add(getClassEntry(entry)); } } return classEntries; } - public static Iterable classes( final JarFile jar ) - { - return new Iterable( ) - { + public static Iterable classes(final JarFile jar) { + return new Iterable() { @Override - public Iterator iterator( ) - { - return new JarClassIterator( jar ); + public Iterator iterator() { + return new JarClassIterator(jar); } }; } - public static CtClass getClass( JarFile jar, ClassEntry classEntry ) - { - try - { - return getClass( jar, new JarEntry( classEntry.getName() + ".class" ) ); - } - catch( IOException | NotFoundException ex ) - { - throw new Error( "Unable to load class: " + classEntry.getName() ); + public static CtClass getClass(JarFile jar, ClassEntry classEntry) { + try { + return getClass(jar, new JarEntry(classEntry.getName() + ".class")); + } catch (IOException | NotFoundException ex) { + throw new Error("Unable to load class: " + classEntry.getName()); } } - private static CtClass getClass( JarFile jar, JarEntry entry ) - throws IOException, NotFoundException - { + private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException { // read the class into a buffer ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buf = new byte[Constants.KiB]; int totalNumBytesRead = 0; - InputStream in = jar.getInputStream( entry ); - while( in.available() > 0 ) - { - int numBytesRead = in.read( buf ); - if( numBytesRead < 0 ) - { + InputStream in = jar.getInputStream(entry); + while (in.available() > 0) { + int numBytesRead = in.read(buf); + if (numBytesRead < 0) { break; } - bos.write( buf, 0, numBytesRead ); + bos.write(buf, 0, numBytesRead); // sanity checking totalNumBytesRead += numBytesRead; - if( totalNumBytesRead > Constants.MiB ) - { - throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" ); + if (totalNumBytesRead > Constants.MiB) { + throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!"); } } // get a javassist handle for the class - String className = Descriptor.toJavaName( getClassEntry( entry ).getName() ); + String className = Descriptor.toJavaName(getClassEntry(entry).getName()); ClassPool classPool = new ClassPool(); - classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); - return classPool.get( className ); + classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray())); + return classPool.get(className); } - private static ClassEntry getClassEntry( JarEntry entry ) - { - return new ClassEntry( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); + private static ClassEntry getClassEntry(JarEntry entry) { + return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length())); } } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 0954564e..4b03a332 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -53,8 +53,8 @@ import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.SignatureUpdater; import cuchaz.enigma.mapping.Translator; -public class JarIndex -{ +public class JarIndex { + private Set m_obfClassEntries; private TranslationIndex m_translationIndex; private Multimap m_interfaces; @@ -68,8 +68,7 @@ public class JarIndex private Map m_anonymousClasses; private Map m_bridgeMethods; - public JarIndex( ) - { + public JarIndex() { m_obfClassEntries = Sets.newHashSet(); m_translationIndex = new TranslationIndex(); m_interfaces = HashMultimap.create(); @@ -84,192 +83,161 @@ public class JarIndex m_bridgeMethods = Maps.newHashMap(); } - public void indexJar( JarFile jar, boolean buildInnerClasses ) - { + public void indexJar(JarFile jar, boolean buildInnerClasses) { // step 1: read the class names - for( ClassEntry classEntry : JarClassIterator.getClassEntries( jar ) ) - { - if( classEntry.isInDefaultPackage() ) - { + for (ClassEntry classEntry : JarClassIterator.getClassEntries(jar)) { + if (classEntry.isInDefaultPackage()) { // move out of default package - classEntry = new ClassEntry( Constants.NonePackage + "/" + classEntry.getName() ); + classEntry = new ClassEntry(Constants.NonePackage + "/" + classEntry.getName()); } - m_obfClassEntries.add( classEntry ); + m_obfClassEntries.add(classEntry); } // step 2: index field/method/constructor access - for( CtClass c : JarClassIterator.classes( jar ) ) - { - ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); - ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); - for( CtField field : c.getDeclaredFields() ) - { - FieldEntry fieldEntry = new FieldEntry( classEntry, field.getName() ); - m_access.put( fieldEntry, Access.get( field ) ); + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = new FieldEntry(classEntry, field.getName()); + m_access.put(fieldEntry, Access.get(field)); } - for( CtMethod method : c.getDeclaredMethods() ) - { - MethodEntry methodEntry = new MethodEntry( classEntry, method.getName(), method.getSignature() ); - m_access.put( methodEntry, Access.get( method ) ); + for (CtMethod method : c.getDeclaredMethods()) { + MethodEntry methodEntry = new MethodEntry(classEntry, method.getName(), method.getSignature()); + m_access.put(methodEntry, Access.get(method)); } - for( CtConstructor constructor : c.getDeclaredConstructors() ) - { - ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getSignature() ); - m_access.put( constructorEntry, Access.get( constructor ) ); + for (CtConstructor constructor : c.getDeclaredConstructors()) { + ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, constructor.getSignature()); + m_access.put(constructorEntry, Access.get(constructor)); } } // step 3: index extends, implements, fields, and methods - for( CtClass c : JarClassIterator.classes( jar ) ) - { - ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); - String className = Descriptor.toJvmName( c.getName() ); - m_translationIndex.addSuperclass( className, Descriptor.toJvmName( c.getClassFile().getSuperclass() ) ); - for( String interfaceName : c.getClassFile().getInterfaces() ) - { - className = Descriptor.toJvmName( className ); - interfaceName = Descriptor.toJvmName( interfaceName ); - if( className.equals( interfaceName ) ) - { - throw new IllegalArgumentException( "Class cannot be its own interface! " + className ); + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + String className = Descriptor.toJvmName(c.getName()); + m_translationIndex.addSuperclass(className, Descriptor.toJvmName(c.getClassFile().getSuperclass())); + for (String interfaceName : c.getClassFile().getInterfaces()) { + className = Descriptor.toJvmName(className); + interfaceName = Descriptor.toJvmName(interfaceName); + if (className.equals(interfaceName)) { + throw new IllegalArgumentException("Class cannot be its own interface! " + className); } - m_interfaces.put( className, interfaceName ); + m_interfaces.put(className, interfaceName); } - for( CtField field : c.getDeclaredFields() ) - { - indexField( field ); + for (CtField field : c.getDeclaredFields()) { + indexField(field); } - for( CtBehavior behavior : c.getDeclaredBehaviors() ) - { - indexBehavior( behavior ); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + indexBehavior(behavior); } } // step 4: index field, method, constructor references - for( CtClass c : JarClassIterator.classes( jar ) ) - { - ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); - for( CtBehavior behavior : c.getDeclaredBehaviors() ) - { - indexBehaviorReferences( behavior ); + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + indexBehaviorReferences(behavior); } } - if( buildInnerClasses ) - { + if (buildInnerClasses) { // step 5: index inner classes and anonymous classes - for( CtClass c : JarClassIterator.classes( jar ) ) - { - ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); - String outerClassName = findOuterClass( c ); - if( outerClassName != null ) - { + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + String outerClassName = findOuterClass(c); + if (outerClassName != null) { String innerClassName = c.getSimpleName(); - m_innerClasses.put( outerClassName, innerClassName ); - boolean innerWasAdded = m_outerClasses.put( innerClassName, outerClassName ) == null; - assert( innerWasAdded ); + m_innerClasses.put(outerClassName, innerClassName); + boolean innerWasAdded = m_outerClasses.put(innerClassName, outerClassName) == null; + assert (innerWasAdded); - BehaviorEntry enclosingBehavior = isAnonymousClass( c, outerClassName ); - if( enclosingBehavior != null ) - { - m_anonymousClasses.put( innerClassName, enclosingBehavior ); + BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassName); + if (enclosingBehavior != null) { + m_anonymousClasses.put(innerClassName, enclosingBehavior); // DEBUG - //System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); - } - else - { + // System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); + } else { // DEBUG - //System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); + // System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); } } } // step 6: update other indices with inner class info Map renames = Maps.newHashMap(); - for( Map.Entry entry : m_outerClasses.entrySet() ) - { - renames.put( Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey() ); + for (Map.Entry entry : m_outerClasses.entrySet()) { + renames.put(Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey()); } - EntryRenamer.renameClassesInSet( renames, m_obfClassEntries ); - m_translationIndex.renameClasses( renames ); - EntryRenamer.renameClassesInMultimap( renames, m_interfaces ); - EntryRenamer.renameClassesInMultimap( renames, m_methodImplementations ); - EntryRenamer.renameClassesInMultimap( renames, m_behaviorReferences ); - EntryRenamer.renameClassesInMultimap( renames, m_fieldReferences ); - EntryRenamer.renameClassesInMap( renames, m_bridgeMethods ); - EntryRenamer.renameClassesInMap( renames, m_access ); + EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); + m_translationIndex.renameClasses(renames); + EntryRenamer.renameClassesInMultimap(renames, m_interfaces); + EntryRenamer.renameClassesInMultimap(renames, m_methodImplementations); + EntryRenamer.renameClassesInMultimap(renames, m_behaviorReferences); + EntryRenamer.renameClassesInMultimap(renames, m_fieldReferences); + EntryRenamer.renameClassesInMap(renames, m_bridgeMethods); + EntryRenamer.renameClassesInMap(renames, m_access); } // step 6: update other indices with bridge method info - EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_methodImplementations ); - EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_behaviorReferences ); - EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_fieldReferences ); - EntryRenamer.renameMethodsInMap( m_bridgeMethods, m_access ); + EntryRenamer.renameMethodsInMultimap(m_bridgeMethods, m_methodImplementations); + EntryRenamer.renameMethodsInMultimap(m_bridgeMethods, m_behaviorReferences); + EntryRenamer.renameMethodsInMultimap(m_bridgeMethods, m_fieldReferences); + EntryRenamer.renameMethodsInMap(m_bridgeMethods, m_access); } - private void indexField( CtField field ) - { + private void indexField(CtField field) { // get the field entry - String className = Descriptor.toJvmName( field.getDeclaringClass().getName() ); - FieldEntry fieldEntry = new FieldEntry( new ClassEntry( className ), field.getName() ); + String className = Descriptor.toJvmName(field.getDeclaringClass().getName()); + FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName()); - m_translationIndex.addField( className, field.getName() ); + m_translationIndex.addField(className, field.getName()); // is the field a class type? - if( field.getSignature().startsWith( "L" ) ) - { - ClassEntry fieldTypeEntry = new ClassEntry( field.getSignature().substring( 1, field.getSignature().length() - 1 ) ); - m_fieldClasses.put( fieldEntry, fieldTypeEntry ); + if (field.getSignature().startsWith("L")) { + ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1)); + m_fieldClasses.put(fieldEntry, fieldTypeEntry); } } - - private void indexBehavior( CtBehavior behavior ) - { + + private void indexBehavior(CtBehavior behavior) { // get the behavior entry - final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create( behavior ); - if( behaviorEntry instanceof MethodEntry ) - { + final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + if (behaviorEntry instanceof MethodEntry) { MethodEntry methodEntry = (MethodEntry)behaviorEntry; // index implementation - m_methodImplementations.put( behaviorEntry.getClassName(), methodEntry ); + m_methodImplementations.put(behaviorEntry.getClassName(), methodEntry); // look for bridge methods - CtMethod bridgedMethod = getBridgedMethod( (CtMethod)behavior ); - if( bridgedMethod != null ) - { + CtMethod bridgedMethod = getBridgedMethod((CtMethod)behavior); + if (bridgedMethod != null) { MethodEntry bridgedMethodEntry = new MethodEntry( behaviorEntry.getClassEntry(), bridgedMethod.getName(), bridgedMethod.getSignature() ); - m_bridgeMethods.put( bridgedMethodEntry, methodEntry ); + m_bridgeMethods.put(bridgedMethodEntry, methodEntry); } } // looks like we don't care about constructors here } - private void indexBehaviorReferences( CtBehavior behavior ) - { + private void indexBehaviorReferences(CtBehavior behavior) { // index method calls - final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create( behavior ); - try - { - behavior.instrument( new ExprEditor( ) - { + final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + try { + behavior.instrument(new ExprEditor() { @Override - public void edit( MethodCall call ) - { - String className = Descriptor.toJvmName( call.getClassName() ); + public void edit(MethodCall call) { + String className = Descriptor.toJvmName(call.getClassName()); MethodEntry calledMethodEntry = new MethodEntry( - new ClassEntry( className ), + new ClassEntry(className), call.getMethodName(), call.getSignature() ); - ClassEntry resolvedClassEntry = resolveEntryClass( calledMethodEntry ); - if( resolvedClassEntry != null && !resolvedClassEntry.equals( calledMethodEntry.getClassEntry() ) ) - { + ClassEntry resolvedClassEntry = resolveEntryClass(calledMethodEntry); + if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { calledMethodEntry = new MethodEntry( resolvedClassEntry, call.getMethodName(), @@ -281,39 +249,33 @@ public class JarIndex call.getMethodName(), behaviorEntry ); - m_behaviorReferences.put( calledMethodEntry, reference ); + m_behaviorReferences.put(calledMethodEntry, reference); } @Override - public void edit( FieldAccess call ) - { - String className = Descriptor.toJvmName( call.getClassName() ); + public void edit(FieldAccess call) { + String className = Descriptor.toJvmName(call.getClassName()); FieldEntry calledFieldEntry = new FieldEntry( - new ClassEntry( className ), + new ClassEntry(className), call.getFieldName() ); - ClassEntry resolvedClassEntry = resolveEntryClass( calledFieldEntry ); - if( resolvedClassEntry != null && !resolvedClassEntry.equals( calledFieldEntry.getClassEntry() ) ) - { - calledFieldEntry = new FieldEntry( - resolvedClassEntry, - call.getFieldName() - ); + ClassEntry resolvedClassEntry = resolveEntryClass(calledFieldEntry); + if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { + calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName()); } EntryReference reference = new EntryReference( calledFieldEntry, call.getFieldName(), behaviorEntry ); - m_fieldReferences.put( calledFieldEntry, reference ); + m_fieldReferences.put(calledFieldEntry, reference); } @Override - public void edit( ConstructorCall call ) - { - String className = Descriptor.toJvmName( call.getClassName() ); + public void edit(ConstructorCall call) { + String className = Descriptor.toJvmName(call.getClassName()); ConstructorEntry calledConstructorEntry = new ConstructorEntry( - new ClassEntry( className ), + new ClassEntry(className), call.getSignature() ); EntryReference reference = new EntryReference( @@ -321,15 +283,14 @@ public class JarIndex call.getMethodName(), behaviorEntry ); - m_behaviorReferences.put( calledConstructorEntry, reference ); + m_behaviorReferences.put(calledConstructorEntry, reference); } @Override - public void edit( NewExpr call ) - { - String className = Descriptor.toJvmName( call.getClassName() ); + public void edit(NewExpr call) { + String className = Descriptor.toJvmName(call.getClassName()); ConstructorEntry calledConstructorEntry = new ConstructorEntry( - new ClassEntry( className ), + new ClassEntry(className), call.getSignature() ); EntryReference reference = new EntryReference( @@ -337,173 +298,141 @@ public class JarIndex call.getClassName(), behaviorEntry ); - m_behaviorReferences.put( calledConstructorEntry, reference ); + m_behaviorReferences.put(calledConstructorEntry, reference); } - } ); - } - catch( CannotCompileException ex ) - { - throw new Error( ex ); + }); + } catch (CannotCompileException ex) { + throw new Error(ex); } } - public ClassEntry resolveEntryClass( Entry obfEntry ) - { + public ClassEntry resolveEntryClass(Entry obfEntry) { + // this entry could refer to a method on a class where the method is not actually implemented // travel up the inheritance tree to find the closest implementation - while( !containsObfEntry( obfEntry ) ) - { + while (!containsObfEntry(obfEntry)) { // is there a parent class? - String superclassName = m_translationIndex.getSuperclassName( obfEntry.getClassName() ); - if( superclassName == null ) - { + String superclassName = m_translationIndex.getSuperclassName(obfEntry.getClassName()); + if (superclassName == null) { // this is probably a method from a class in a library // we can't trace the implementation up any higher unless we index the library return null; } // move up to the parent class - obfEntry = obfEntry.cloneToNewClass( new ClassEntry( superclassName ) ); + obfEntry = obfEntry.cloneToNewClass(new ClassEntry(superclassName)); } return obfEntry.getClassEntry(); } - - private CtMethod getBridgedMethod( CtMethod method ) - { + + private CtMethod getBridgedMethod(CtMethod method) { + // bridge methods just call another method, cast it to the return type, and return the result // let's see if we can detect this scenario // skip non-synthetic methods - if( ( method.getModifiers() & AccessFlag.SYNTHETIC ) == 0 ) - { + if ( (method.getModifiers() & AccessFlag.SYNTHETIC) == 0) { return null; } - + // get all the called methods final List methodCalls = Lists.newArrayList(); - try - { - method.instrument( new ExprEditor( ) - { + try { + method.instrument(new ExprEditor() { @Override - public void edit( MethodCall call ) - { - methodCalls.add( call ); + public void edit(MethodCall call) { + methodCalls.add(call); } - } ); - } - catch( CannotCompileException ex ) - { + }); + } catch (CannotCompileException ex) { // this is stupid... we're not even compiling anything - throw new Error( ex ); + throw new Error(ex); } // is there just one? - if( methodCalls.size() != 1 ) - { + if (methodCalls.size() != 1) { return null; } - MethodCall call = methodCalls.get( 0 ); + MethodCall call = methodCalls.get(0); - try - { + try { // we have a bridge method! return call.getMethod(); - } - catch( NotFoundException ex ) - { + } catch (NotFoundException ex) { // can't find the type? not a bridge method return null; } } - private String findOuterClass( CtClass c ) - { + private String findOuterClass(CtClass c) { + // inner classes: - // have constructors that can (illegally) set synthetic fields - // the outer class is the only class that calls constructors + // have constructors that can (illegally) set synthetic fields + // the outer class is the only class that calls constructors // use the synthetic fields to find the synthetic constructors - for( CtConstructor constructor : c.getDeclaredConstructors() ) - { + for (CtConstructor constructor : c.getDeclaredConstructors()) { Set syntheticFieldTypes = Sets.newHashSet(); - if( !isIllegalConstructor( syntheticFieldTypes, constructor ) ) - { + if (!isIllegalConstructor(syntheticFieldTypes, constructor)) { continue; } - ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); - ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getMethodInfo().getDescriptor() ); + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + ConstructorEntry constructorEntry = new ConstructorEntry( + classEntry, + constructor.getMethodInfo().getDescriptor() + ); // gather the classes from the illegally-set synthetic fields Set illegallySetClasses = Sets.newHashSet(); - for( String type : syntheticFieldTypes ) - { - if( type.startsWith( "L" ) ) - { - ClassEntry outerClassEntry = new ClassEntry( type.substring( 1, type.length() - 1 ) ); - if( isSaneOuterClass( outerClassEntry, classEntry ) ) - { - illegallySetClasses.add( outerClassEntry ); + for (String type : syntheticFieldTypes) { + if (type.startsWith("L")) { + ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1)); + if (isSaneOuterClass(outerClassEntry, classEntry)) { + illegallySetClasses.add(outerClassEntry); } } } // who calls this constructor? Set callerClasses = Sets.newHashSet(); - for( EntryReference reference : getBehaviorReferences( constructorEntry ) ) - { + for (EntryReference reference : getBehaviorReferences(constructorEntry)) { + // make sure it's not a call to super - if( reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry ) - { + if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { + // is the entry a superclass of the context? String calledClassName = reference.entry.getClassName(); - String callerSuperclassName = m_translationIndex.getSuperclassName( reference.context.getClassName() ); - if( callerSuperclassName != null && callerSuperclassName.equals( calledClassName ) ) - { + String callerSuperclassName = m_translationIndex.getSuperclassName(reference.context.getClassName()); + if (callerSuperclassName != null && callerSuperclassName.equals(calledClassName)) { // it's a super call, skip continue; } } - if( isSaneOuterClass( reference.context.getClassEntry(), classEntry ) ) - { - callerClasses.add( reference.context.getClassEntry() ); + if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) { + callerClasses.add(reference.context.getClassEntry()); } } // do we have an answer yet? - if( callerClasses.isEmpty() ) - { - if( illegallySetClasses.size() == 1 ) - { + if (callerClasses.isEmpty()) { + if (illegallySetClasses.size() == 1) { return illegallySetClasses.iterator().next().getName(); + } else { + System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); } - else - { - System.out.println( String.format( "WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry ) ); - } - } - else - { - if( callerClasses.size() == 1 ) - { + } else { + if (callerClasses.size() == 1) { return callerClasses.iterator().next().getName(); - } - else - { + } else { // multiple callers, do the illegally set classes narrow it down? - Set intersection = Sets.newHashSet( callerClasses ); - intersection.retainAll( illegallySetClasses ); - if( intersection.size() == 1 ) - { + Set intersection = Sets.newHashSet(callerClasses); + intersection.retainAll(illegallySetClasses); + if (intersection.size() == 1) { return intersection.iterator().next().getName(); - } - else - { - System.out.println( String.format( "WARNING: Unable to choose outer class for %s among options: %s", - classEntry, callerClasses - ) ); + } else { + System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); } } } @@ -512,99 +441,82 @@ public class JarIndex return null; } - private boolean isSaneOuterClass( ClassEntry outerClassEntry, ClassEntry innerClassEntry ) - { + private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { + // clearly this would be silly - if( outerClassEntry.equals( innerClassEntry ) ) - { + if (outerClassEntry.equals(innerClassEntry)) { return false; } // is the outer class in the jar? - if( !m_obfClassEntries.contains( outerClassEntry ) ) - { + if (!m_obfClassEntries.contains(outerClassEntry)) { return false; } return true; } - - @SuppressWarnings( "unchecked" ) - private boolean isIllegalConstructor( Set syntheticFieldTypes, CtConstructor constructor ) - { + + @SuppressWarnings("unchecked") + private boolean isIllegalConstructor(Set syntheticFieldTypes, CtConstructor constructor) { + // illegal constructors only set synthetic member fields, then call super() String className = constructor.getDeclaringClass().getName(); // collect all the field accesses, constructor calls, and method calls final List illegalFieldWrites = Lists.newArrayList(); final List constructorCalls = Lists.newArrayList(); - try - { - constructor.instrument( new ExprEditor( ) - { + try { + constructor.instrument(new ExprEditor() { @Override - public void edit( FieldAccess fieldAccess ) - { - if( fieldAccess.isWriter() && constructorCalls.isEmpty() ) - { - illegalFieldWrites.add( fieldAccess ); + public void edit(FieldAccess fieldAccess) { + if (fieldAccess.isWriter() && constructorCalls.isEmpty()) { + illegalFieldWrites.add(fieldAccess); } } @Override - public void edit( ConstructorCall constructorCall ) - { - constructorCalls.add( constructorCall ); + public void edit(ConstructorCall constructorCall) { + constructorCalls.add(constructorCall); } - } ); - } - catch( CannotCompileException ex ) - { + }); + } catch (CannotCompileException ex) { // we're not compiling anything... this is stupid - throw new Error( ex ); + throw new Error(ex); } // are there any illegal field writes? - if( illegalFieldWrites.isEmpty() ) - { + if (illegalFieldWrites.isEmpty()) { return false; } // are all the writes to synthetic fields? - for( FieldAccess fieldWrite : illegalFieldWrites ) - { + for (FieldAccess fieldWrite : illegalFieldWrites) { + // all illegal writes have to be to the local class - if( !fieldWrite.getClassName().equals( className ) ) - { - System.err.println( String.format( "WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName() ) ); + if (!fieldWrite.getClassName().equals(className)) { + System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName())); return false; } // find the field FieldInfo fieldInfo = null; - for( FieldInfo info : (List)constructor.getDeclaringClass().getClassFile().getFields() ) - { - if( info.getName().equals( fieldWrite.getFieldName() ) && info.getDescriptor().equals( fieldWrite.getSignature() ) ) - { + for (FieldInfo info : (List)constructor.getDeclaringClass().getClassFile().getFields()) { + if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) { fieldInfo = info; break; } } - if( fieldInfo == null ) - { + if (fieldInfo == null) { // field is in a superclass or something, can't be a local synthetic member return false; } // is this field synthetic? boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; - if( isSynthetic ) - { - syntheticFieldTypes.add( fieldInfo.getDescriptor() ); - } - else - { - System.err.println( String.format( "WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName() ) ); + if (isSynthetic) { + syntheticFieldTypes.add(fieldInfo.getDescriptor()); + } else { + System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName())); return false; } } @@ -612,56 +524,51 @@ public class JarIndex // we passed all the tests! return true; } - - private BehaviorEntry isAnonymousClass( CtClass c, String outerClassName ) - { - ClassEntry innerClassEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); + + private BehaviorEntry isAnonymousClass(CtClass c, String outerClassName) { + + ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); // anonymous classes: - // can't be abstract - // have only one constructor - // it's called exactly once by the outer class - // the type the instance is assigned to can't be this type + // can't be abstract + // have only one constructor + // it's called exactly once by the outer class + // the type the instance is assigned to can't be this type // is abstract? - if( Modifier.isAbstract( c.getModifiers() ) ) - { + if (Modifier.isAbstract(c.getModifiers())) { return null; } // is there exactly one constructor? - if( c.getDeclaredConstructors().length != 1 ) - { + if (c.getDeclaredConstructors().length != 1) { return null; } CtConstructor constructor = c.getDeclaredConstructors()[0]; // is this constructor called exactly once? - ConstructorEntry constructorEntry = new ConstructorEntry( innerClassEntry, constructor.getMethodInfo().getDescriptor() ); - Collection> references = getBehaviorReferences( constructorEntry ); - if( references.size() != 1 ) - { + ConstructorEntry constructorEntry = new ConstructorEntry( + innerClassEntry, + constructor.getMethodInfo().getDescriptor() + ); + Collection> references = getBehaviorReferences(constructorEntry); + if (references.size() != 1) { return null; } // does the caller use this type? BehaviorEntry caller = references.iterator().next().context; - for( FieldEntry fieldEntry : getReferencedFields( caller ) ) - { - ClassEntry fieldClass = getFieldClass( fieldEntry ); - if( fieldClass != null && fieldClass.equals( innerClassEntry ) ) - { + for (FieldEntry fieldEntry : getReferencedFields(caller)) { + ClassEntry fieldClass = getFieldClass(fieldEntry); + if (fieldClass != null && fieldClass.equals(innerClassEntry)) { // caller references this type, so it can't be anonymous return null; } } - for( BehaviorEntry behaviorEntry : getReferencedBehaviors( caller ) ) - { + for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) { // get the class types from the signature - for( String className : SignatureUpdater.getClasses( behaviorEntry.getSignature() ) ) - { - if( className.equals( innerClassEntry.getName() ) ) - { + for (String className : SignatureUpdater.getClasses(behaviorEntry.getSignature())) { + if (className.equals(innerClassEntry.getName())) { // caller references this type, so it can't be anonymous return null; } @@ -670,330 +577,275 @@ public class JarIndex return caller; } - - public Set getObfClassEntries( ) - { + + public Set getObfClassEntries() { return m_obfClassEntries; } - public TranslationIndex getTranslationIndex( ) - { + public TranslationIndex getTranslationIndex() { return m_translationIndex; } - public Access getAccess( Entry entry ) - { - return m_access.get( entry ); + public Access getAccess(Entry entry) { + return m_access.get(entry); } - public ClassEntry getFieldClass( FieldEntry fieldEntry ) - { - return m_fieldClasses.get( fieldEntry ); + public ClassEntry getFieldClass(FieldEntry fieldEntry) { + return m_fieldClasses.get(fieldEntry); } - public ClassInheritanceTreeNode getClassInheritance( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) - { + public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { + // get the root node List ancestry = Lists.newArrayList(); - ancestry.add( obfClassEntry.getName() ); - ancestry.addAll( m_translationIndex.getAncestry( obfClassEntry.getName() ) ); - ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, ancestry.get( ancestry.size() - 1 ) ); + ancestry.add(obfClassEntry.getName()); + ancestry.addAll(m_translationIndex.getAncestry(obfClassEntry.getName())); + ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( + deobfuscatingTranslator, + ancestry.get(ancestry.size() - 1) + ); // expand all children recursively - rootNode.load( m_translationIndex, true ); + rootNode.load(m_translationIndex, true); return rootNode; } - public ClassImplementationsTreeNode getClassImplementations( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) - { + public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { + // is this even an interface? - if( isInterface( obfClassEntry.getClassName() ) ) - { - ClassImplementationsTreeNode node = new ClassImplementationsTreeNode( deobfuscatingTranslator, obfClassEntry ); - node.load( this ); + if (isInterface(obfClassEntry.getClassName())) { + ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry); + node.load(this); return node; } return null; } - public MethodInheritanceTreeNode getMethodInheritance( Translator deobfuscatingTranslator, MethodEntry obfMethodEntry ) - { + public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { + // travel to the ancestor implementation String baseImplementationClassName = obfMethodEntry.getClassName(); - for( String ancestorClassName : m_translationIndex.getAncestry( obfMethodEntry.getClassName() ) ) - { + for (String ancestorClassName : m_translationIndex.getAncestry(obfMethodEntry.getClassName())) { MethodEntry ancestorMethodEntry = new MethodEntry( - new ClassEntry( ancestorClassName ), + new ClassEntry(ancestorClassName), obfMethodEntry.getName(), obfMethodEntry.getSignature() ); - if( containsObfBehavior( ancestorMethodEntry ) ) - { + if (containsObfBehavior(ancestorMethodEntry)) { baseImplementationClassName = ancestorClassName; } } // make a root node at the base MethodEntry methodEntry = new MethodEntry( - new ClassEntry( baseImplementationClassName ), + new ClassEntry(baseImplementationClassName), obfMethodEntry.getName(), obfMethodEntry.getSignature() ); MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( deobfuscatingTranslator, methodEntry, - containsObfBehavior( methodEntry ) + containsObfBehavior(methodEntry) ); // expand the full tree - rootNode.load( this, true ); + rootNode.load(this, true); return rootNode; } - public MethodImplementationsTreeNode getMethodImplementations( Translator deobfuscatingTranslator, MethodEntry obfMethodEntry ) - { + public MethodImplementationsTreeNode getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { + MethodEntry interfaceMethodEntry; // is this method on an interface? - if( isInterface( obfMethodEntry.getClassName() ) ) - { + if (isInterface(obfMethodEntry.getClassName())) { interfaceMethodEntry = obfMethodEntry; - } - else - { + } else { // get the interface class List methodInterfaces = Lists.newArrayList(); - for( String interfaceName : getInterfaces( obfMethodEntry.getClassName() ) ) - { + for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) { // is this method defined in this interface? MethodEntry methodInterface = new MethodEntry( - new ClassEntry( interfaceName ), + new ClassEntry(interfaceName), obfMethodEntry.getName(), obfMethodEntry.getSignature() ); - if( containsObfBehavior( methodInterface ) ) - { - methodInterfaces.add( methodInterface ); + if (containsObfBehavior(methodInterface)) { + methodInterfaces.add(methodInterface); } } - if( methodInterfaces.isEmpty() ) - { + if (methodInterfaces.isEmpty()) { return null; } - if( methodInterfaces.size() > 1 ) - { - throw new Error( "Too many interfaces define this method! This is not yet supported by Enigma!" ); + if (methodInterfaces.size() > 1) { + throw new Error("Too many interfaces define this method! This is not yet supported by Enigma!"); } - interfaceMethodEntry = methodInterfaces.get( 0 ); + interfaceMethodEntry = methodInterfaces.get(0); } - MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode( deobfuscatingTranslator, interfaceMethodEntry ); - rootNode.load( this ); + MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); + rootNode.load(this); return rootNode; } - public Set getRelatedMethodImplementations( MethodEntry obfMethodEntry ) - { + public Set getRelatedMethodImplementations(MethodEntry obfMethodEntry) { Set methodEntries = Sets.newHashSet(); - getRelatedMethodImplementations( methodEntries, getMethodInheritance( null, obfMethodEntry ) ); + getRelatedMethodImplementations(methodEntries, getMethodInheritance(null, obfMethodEntry)); return methodEntries; } - private void getRelatedMethodImplementations( Set methodEntries, MethodInheritanceTreeNode node ) - { + private void getRelatedMethodImplementations(Set methodEntries, MethodInheritanceTreeNode node) { MethodEntry methodEntry = node.getMethodEntry(); - if( containsObfBehavior( methodEntry ) ) - { + if (containsObfBehavior(methodEntry)) { // collect the entry - methodEntries.add( methodEntry ); + methodEntries.add(methodEntry); } // look at interface methods too - MethodImplementationsTreeNode implementations = getMethodImplementations( null, methodEntry ); - if( implementations != null ) - { - getRelatedMethodImplementations( methodEntries, implementations ); + MethodImplementationsTreeNode implementations = getMethodImplementations(null, methodEntry); + if (implementations != null) { + getRelatedMethodImplementations(methodEntries, implementations); } // recurse - for( int i=0; i methodEntries, MethodImplementationsTreeNode node ) - { + private void getRelatedMethodImplementations(Set methodEntries, MethodImplementationsTreeNode node) { MethodEntry methodEntry = node.getMethodEntry(); - if( containsObfBehavior( methodEntry ) ) - { + if (containsObfBehavior(methodEntry)) { // collect the entry - methodEntries.add( methodEntry ); + methodEntries.add(methodEntry); } // recurse - for( int i=0; i> getFieldReferences( FieldEntry fieldEntry ) - { - return m_fieldReferences.get( fieldEntry ); + + public Collection> getFieldReferences(FieldEntry fieldEntry) { + return m_fieldReferences.get(fieldEntry); } - public Collection getReferencedFields( BehaviorEntry behaviorEntry ) - { + public Collection getReferencedFields(BehaviorEntry behaviorEntry) { // linear search is fast enough for now Set fieldEntries = Sets.newHashSet(); - for( EntryReference reference : m_fieldReferences.values() ) - { - if( reference.context == behaviorEntry ) - { - fieldEntries.add( reference.entry ); + for (EntryReference reference : m_fieldReferences.values()) { + if (reference.context == behaviorEntry) { + fieldEntries.add(reference.entry); } } return fieldEntries; } - public Collection> getBehaviorReferences( BehaviorEntry behaviorEntry ) - { - return m_behaviorReferences.get( behaviorEntry ); + public Collection> getBehaviorReferences(BehaviorEntry behaviorEntry) { + return m_behaviorReferences.get(behaviorEntry); } - - public Collection getReferencedBehaviors( BehaviorEntry behaviorEntry ) - { + + public Collection getReferencedBehaviors(BehaviorEntry behaviorEntry) { // linear search is fast enough for now Set behaviorEntries = Sets.newHashSet(); - for( EntryReference reference : m_behaviorReferences.values() ) - { - if( reference.context == behaviorEntry ) - { - behaviorEntries.add( reference.entry ); + for (EntryReference reference : m_behaviorReferences.values()) { + if (reference.context == behaviorEntry) { + behaviorEntries.add(reference.entry); } } return behaviorEntries; } - public Collection getInnerClasses( String obfOuterClassName ) - { - return m_innerClasses.get( obfOuterClassName ); + public Collection getInnerClasses(String obfOuterClassName) { + return m_innerClasses.get(obfOuterClassName); } - public String getOuterClass( String obfInnerClassName ) - { + public String getOuterClass(String obfInnerClassName) { // make sure we use the right name - if( new ClassEntry( obfInnerClassName ).getPackageName() != null ) - { - throw new IllegalArgumentException( "Don't reference obfuscated inner classes using packages: " + obfInnerClassName ); + if (new ClassEntry(obfInnerClassName).getPackageName() != null) { + throw new IllegalArgumentException("Don't reference obfuscated inner classes using packages: " + obfInnerClassName); } - return m_outerClasses.get( obfInnerClassName ); + return m_outerClasses.get(obfInnerClassName); } - public boolean isAnonymousClass( String obfInnerClassName ) - { - return m_anonymousClasses.containsKey( obfInnerClassName ); + public boolean isAnonymousClass(String obfInnerClassName) { + return m_anonymousClasses.containsKey(obfInnerClassName); } - public BehaviorEntry getAnonymousClassCaller( String obfInnerClassName ) - { - return m_anonymousClasses.get( obfInnerClassName ); + public BehaviorEntry getAnonymousClassCaller(String obfInnerClassName) { + return m_anonymousClasses.get(obfInnerClassName); } - public Set getInterfaces( String className ) - { + public Set getInterfaces(String className) { Set interfaceNames = new HashSet(); - interfaceNames.addAll( m_interfaces.get( className ) ); - for( String ancestor : m_translationIndex.getAncestry( className ) ) - { - interfaceNames.addAll( m_interfaces.get( ancestor ) ); + interfaceNames.addAll(m_interfaces.get(className)); + for (String ancestor : m_translationIndex.getAncestry(className)) { + interfaceNames.addAll(m_interfaces.get(ancestor)); } return interfaceNames; } - public Set getImplementingClasses( String targetInterfaceName ) - { + public Set getImplementingClasses(String targetInterfaceName) { // linear search is fast enough for now Set classNames = Sets.newHashSet(); - for( Map.Entry entry : m_interfaces.entries() ) - { + for (Map.Entry entry : m_interfaces.entries()) { String className = entry.getKey(); String interfaceName = entry.getValue(); - if( interfaceName.equals( targetInterfaceName ) ) - { - classNames.add( className ); - m_translationIndex.getSubclassNamesRecursively( classNames, className ); + if (interfaceName.equals(targetInterfaceName)) { + classNames.add(className); + m_translationIndex.getSubclassNamesRecursively(classNames, className); } } return classNames; } - public boolean isInterface( String className ) - { - return m_interfaces.containsValue( className ); + public boolean isInterface(String className) { + return m_interfaces.containsValue(className); } - public MethodEntry getBridgeMethod( MethodEntry methodEntry ) - { - return m_bridgeMethods.get( methodEntry ); + public MethodEntry getBridgeMethod(MethodEntry methodEntry) { + return m_bridgeMethods.get(methodEntry); } - public boolean containsObfClass( ClassEntry obfClassEntry ) - { - return m_obfClassEntries.contains( obfClassEntry ); + public boolean containsObfClass(ClassEntry obfClassEntry) { + return m_obfClassEntries.contains(obfClassEntry); } - - public boolean containsObfField( FieldEntry obfFieldEntry ) - { - return m_access.containsKey( obfFieldEntry ); + + public boolean containsObfField(FieldEntry obfFieldEntry) { + return m_access.containsKey(obfFieldEntry); } - - public boolean containsObfBehavior( BehaviorEntry obfBehaviorEntry ) - { - return m_access.containsKey( obfBehaviorEntry ); + + public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { + return m_access.containsKey(obfBehaviorEntry); } - public boolean containsObfArgument( ArgumentEntry obfArgumentEntry ) - { + public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { // check the behavior - if( !containsObfBehavior( obfArgumentEntry.getBehaviorEntry() ) ) - { + if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { return false; } // check the argument - if( obfArgumentEntry.getIndex() >= Descriptor.numOfParameters( obfArgumentEntry.getBehaviorEntry().getSignature() ) ) - { + if (obfArgumentEntry.getIndex() >= Descriptor.numOfParameters(obfArgumentEntry.getBehaviorEntry().getSignature())) { return false; } return true; } - public boolean containsObfEntry( Entry obfEntry ) - { - if( obfEntry instanceof ClassEntry ) - { - return containsObfClass( (ClassEntry)obfEntry ); - } - else if( obfEntry instanceof FieldEntry ) - { - return containsObfField( (FieldEntry)obfEntry ); - } - else if( obfEntry instanceof BehaviorEntry ) - { - return containsObfBehavior( (BehaviorEntry)obfEntry ); - } - else if( obfEntry instanceof ArgumentEntry ) - { - return containsObfArgument( (ArgumentEntry)obfEntry ); - } - else - { - throw new Error( "Entry type not supported: " + obfEntry.getClass().getName() ); + public boolean containsObfEntry(Entry obfEntry) { + if (obfEntry instanceof ClassEntry) { + return containsObfClass((ClassEntry)obfEntry); + } else if (obfEntry instanceof FieldEntry) { + return containsObfField((FieldEntry)obfEntry); + } else if (obfEntry instanceof BehaviorEntry) { + return containsObfBehavior((BehaviorEntry)obfEntry); + } else if (obfEntry instanceof ArgumentEntry) { + return containsObfArgument((ArgumentEntry)obfEntry); + } else { + throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); } } } diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index a050282b..10092268 100644 --- a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -20,94 +20,78 @@ import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; -public class MethodImplementationsTreeNode extends DefaultMutableTreeNode -{ +public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { + private static final long serialVersionUID = 3781080657461899915L; private Translator m_deobfuscatingTranslator; private MethodEntry m_entry; - public MethodImplementationsTreeNode( Translator deobfuscatingTranslator, MethodEntry entry ) - { - if( entry == null ) - { - throw new IllegalArgumentException( "entry cannot be null!" ); + public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { + if (entry == null) { + throw new IllegalArgumentException("entry cannot be null!"); } m_deobfuscatingTranslator = deobfuscatingTranslator; m_entry = entry; } - public MethodEntry getMethodEntry( ) - { + public MethodEntry getMethodEntry() { return m_entry; } - public String getDeobfClassName( ) - { - return m_deobfuscatingTranslator.translateClass( m_entry.getClassName() ); + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_entry.getClassName()); } - public String getDeobfMethodName( ) - { - return m_deobfuscatingTranslator.translate( m_entry ); + public String getDeobfMethodName() { + return m_deobfuscatingTranslator.translate(m_entry); } @Override - public String toString( ) - { + public String toString() { String className = getDeobfClassName(); - if( className == null ) - { + if (className == null) { className = m_entry.getClassName(); } String methodName = getDeobfMethodName(); - if( methodName == null ) - { + if (methodName == null) { methodName = m_entry.getName(); } return className + "." + methodName + "()"; } - public void load( JarIndex index ) - { + public void load(JarIndex index) { // get all method implementations List nodes = Lists.newArrayList(); - for( String implementingClassName : index.getImplementingClasses( m_entry.getClassName() ) ) - { + for (String implementingClassName : index.getImplementingClasses(m_entry.getClassName())) { MethodEntry methodEntry = new MethodEntry( - new ClassEntry( implementingClassName ), + new ClassEntry(implementingClassName), m_entry.getName(), m_entry.getSignature() ); - if( index.containsObfBehavior( methodEntry ) ) - { - nodes.add( new MethodImplementationsTreeNode( m_deobfuscatingTranslator, methodEntry ) ); + if (index.containsObfBehavior(methodEntry)) { + nodes.add(new MethodImplementationsTreeNode(m_deobfuscatingTranslator, methodEntry)); } } // add them to this node - for( MethodImplementationsTreeNode node : nodes ) - { - this.add( node ); + for (MethodImplementationsTreeNode node : nodes) { + this.add(node); } } - public static MethodImplementationsTreeNode findNode( MethodImplementationsTreeNode node, MethodEntry entry ) - { + public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) { // is this the node? - if( node.getMethodEntry().equals( entry ) ) - { + if (node.getMethodEntry().equals(entry)) { return node; } // recurse - for( int i=0; i nodes = Lists.newArrayList(); - for( String subclassName : index.getTranslationIndex().getSubclassNames( m_entry.getClassName() ) ) - { + for (String subclassName : index.getTranslationIndex().getSubclassNames(m_entry.getClassName())) { MethodEntry methodEntry = new MethodEntry( - new ClassEntry( subclassName ), + new ClassEntry(subclassName), m_entry.getName(), m_entry.getSignature() ); - nodes.add( new MethodInheritanceTreeNode( + nodes.add(new MethodInheritanceTreeNode( m_deobfuscatingTranslator, methodEntry, - index.containsObfBehavior( methodEntry ) - ) ); + index.containsObfBehavior(methodEntry) + )); } // add them to this node - for( MethodInheritanceTreeNode node : nodes ) - { - this.add( node ); + for (MethodInheritanceTreeNode node : nodes) { + this.add(node); } - if( recurse ) - { - for( MethodInheritanceTreeNode node : nodes ) - { - node.load( index, true ); + if (recurse) { + for (MethodInheritanceTreeNode node : nodes) { + node.load(index, true); } } } - public static MethodInheritanceTreeNode findNode( MethodInheritanceTreeNode node, MethodEntry entry ) - { + public static MethodInheritanceTreeNode findNode(MethodInheritanceTreeNode node, MethodEntry entry) { // is this the node? - if( node.getMethodEntry().equals( entry ) ) - { + if (node.getMethodEntry().equals(entry)) { return node; } // recurse - for( int i=0; i -{ +public interface ReferenceTreeNode { E getEntry(); EntryReference getReference(); } diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 0e33de00..b43ab614 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -25,16 +25,15 @@ import com.strobel.decompiler.languages.java.ast.Identifier; import cuchaz.enigma.mapping.Entry; -public class SourceIndex -{ +public class SourceIndex { + private String m_source; private TreeMap> m_tokenToReference; private Multimap,Token> m_referenceToTokens; private Map m_declarationToToken; private List m_lineOffsets; - public SourceIndex( String source ) - { + public SourceIndex(String source) { m_source = source; m_tokenToReference = Maps.newTreeMap(); m_referenceToTokens = HashMultimap.create(); @@ -42,142 +41,119 @@ public class SourceIndex m_lineOffsets = Lists.newArrayList(); // count the lines - m_lineOffsets.add( 0 ); - for( int i=0; i= 0 ) - { + int pos = name.lastIndexOf('$'); + if (pos >= 0) { token.end -= pos + 1; } return token; } - public void addReference( AstNode node, Entry deobfEntry, Entry deobfContext ) - { - Token token = getToken( node ); - if( token != null ) - { - EntryReference deobfReference = new EntryReference( deobfEntry, token.text, deobfContext ); - m_tokenToReference.put( token, deobfReference ); - m_referenceToTokens.put( deobfReference, token ); + public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { + Token token = getToken(node); + if (token != null) { + EntryReference deobfReference = new EntryReference(deobfEntry, token.text, deobfContext); + m_tokenToReference.put(token, deobfReference); + m_referenceToTokens.put(deobfReference, token); } } - public void addDeclaration( AstNode node, Entry deobfEntry ) - { - Token token = getToken( node ); - if( token != null ) - { - EntryReference reference = new EntryReference( deobfEntry, token.text ); - m_tokenToReference.put( token, reference ); - m_referenceToTokens.put( reference, token ); - m_declarationToToken.put( deobfEntry, token ); + public void addDeclaration(AstNode node, Entry deobfEntry) { + Token token = getToken(node); + if (token != null) { + EntryReference reference = new EntryReference(deobfEntry, token.text); + m_tokenToReference.put(token, reference); + m_referenceToTokens.put(reference, token); + m_declarationToToken.put(deobfEntry, token); } } - public Token getReferenceToken( int pos ) - { - Token token = m_tokenToReference.floorKey( new Token( pos, pos, null ) ); - if( token != null && token.contains( pos ) ) - { + public Token getReferenceToken(int pos) { + Token token = m_tokenToReference.floorKey(new Token(pos, pos, null)); + if (token != null && token.contains(pos)) { return token; } return null; } - public Collection getReferenceTokens( EntryReference deobfReference ) - { - return m_referenceToTokens.get( deobfReference ); + public Collection getReferenceTokens(EntryReference deobfReference) { + return m_referenceToTokens.get(deobfReference); } - public EntryReference getDeobfReference( Token token ) - { - if( token == null ) - { + public EntryReference getDeobfReference(Token token) { + if (token == null) { return null; } - return m_tokenToReference.get( token ); + return m_tokenToReference.get(token); } - public void replaceDeobfReference( Token token, EntryReference newDeobfReference ) - { - EntryReference oldDeobfReference = m_tokenToReference.get( token ); - m_tokenToReference.put( token, newDeobfReference ); - Collection tokens = m_referenceToTokens.get( oldDeobfReference ); - m_referenceToTokens.removeAll( oldDeobfReference ); - m_referenceToTokens.putAll( newDeobfReference, tokens ); + public void replaceDeobfReference(Token token, EntryReference newDeobfReference) { + EntryReference oldDeobfReference = m_tokenToReference.get(token); + m_tokenToReference.put(token, newDeobfReference); + Collection tokens = m_referenceToTokens.get(oldDeobfReference); + m_referenceToTokens.removeAll(oldDeobfReference); + m_referenceToTokens.putAll(newDeobfReference, tokens); } - public Iterable referenceTokens( ) - { + public Iterable referenceTokens() { return m_tokenToReference.keySet(); } - public Iterable declarationTokens( ) - { + public Iterable declarationTokens() { return m_declarationToToken.values(); } - public Token getDeclarationToken( Entry deobfEntry ) - { - return m_declarationToToken.get( deobfEntry ); + public Token getDeclarationToken(Entry deobfEntry) { + return m_declarationToToken.get(deobfEntry); } - public int getLineNumber( int pos ) - { + public int getLineNumber(int pos) { // line number is 1-based int line = 0; - for( Integer offset : m_lineOffsets ) - { - if( offset > pos ) - { + for (Integer offset : m_lineOffsets) { + if (offset > pos) { break; } line++; @@ -185,15 +161,13 @@ public class SourceIndex return line; } - public int getColumnNumber( int pos ) - { + public int getColumnNumber(int pos) { // column number is 1-based - return pos - m_lineOffsets.get( getLineNumber( pos ) - 1 ) + 1; + return pos - m_lineOffsets.get(getLineNumber(pos) - 1) + 1; } - - private int toPos( int line, int col ) - { + + private int toPos(int line, int col) { // line and col are 1-based - return m_lineOffsets.get( line - 1 ) + col - 1; + return m_lineOffsets.get(line - 1) + col - 1; } } diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index 7ffd1700..43c17499 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -36,159 +36,128 @@ import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; -public class SourceIndexBehaviorVisitor extends SourceIndexVisitor -{ +public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { + private BehaviorEntry m_behaviorEntry; - public SourceIndexBehaviorVisitor( BehaviorEntry behaviorEntry ) - { + public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) { m_behaviorEntry = behaviorEntry; } @Override - public Void visitMethodDeclaration( MethodDeclaration node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitConstructorDeclaration( ConstructorDeclaration node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitInvocationExpression( InvocationExpression node, SourceIndex index ) - { - MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); + public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); // get the behavior entry - ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); BehaviorEntry behaviorEntry = null; - if( ref instanceof MethodReference ) - { + if (ref instanceof MethodReference) { MethodReference methodRef = (MethodReference)ref; - if( methodRef.isConstructor() ) - { - behaviorEntry = new ConstructorEntry( classEntry, ref.getSignature() ); - } - else if( methodRef.isTypeInitializer() ) - { - behaviorEntry = new ConstructorEntry( classEntry ); - } - else - { - behaviorEntry = new MethodEntry( classEntry, ref.getName(), ref.getSignature() ); + if (methodRef.isConstructor()) { + behaviorEntry = new ConstructorEntry(classEntry, ref.getSignature()); + } else if (methodRef.isTypeInitializer()) { + behaviorEntry = new ConstructorEntry(classEntry); + } else { + behaviorEntry = new MethodEntry(classEntry, ref.getName(), ref.getSignature()); } } - if( behaviorEntry != null ) - { + if (behaviorEntry != null) { // get the node for the token AstNode tokenNode = null; - if( node.getTarget() instanceof MemberReferenceExpression ) - { + if (node.getTarget() instanceof MemberReferenceExpression) { tokenNode = ((MemberReferenceExpression)node.getTarget()).getMemberNameToken(); - } - else if( node.getTarget() instanceof SuperReferenceExpression ) - { + } else if (node.getTarget() instanceof SuperReferenceExpression) { tokenNode = node.getTarget(); - } - else if( node.getTarget() instanceof ThisReferenceExpression ) - { + } else if (node.getTarget() instanceof ThisReferenceExpression) { tokenNode = node.getTarget(); } - if( tokenNode != null ) - { - index.addReference( tokenNode, behaviorEntry, m_behaviorEntry ); + if (tokenNode != null) { + index.addReference(tokenNode, behaviorEntry, m_behaviorEntry); } } - return recurse( node, index ); + return recurse(node, index); } @Override - public Void visitMemberReferenceExpression( MemberReferenceExpression node, SourceIndex index ) - { - MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); - if( ref != null ) - { + public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { // make sure this is actually a field - if( ref.getSignature().indexOf( '(' ) >= 0 ) - { - throw new Error( "Expected a field here! got " + ref ); + if (ref.getSignature().indexOf('(') >= 0) { + throw new Error("Expected a field here! got " + ref); } - ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); - FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); - index.addReference( node.getMemberNameToken(), fieldEntry, m_behaviorEntry ); + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName()); + index.addReference(node.getMemberNameToken(), fieldEntry, m_behaviorEntry); } - return recurse( node, index ); + return recurse(node, index); } @Override - public Void visitSimpleType( SimpleType node, SourceIndex index ) - { - TypeReference ref = node.getUserData( Keys.TYPE_REFERENCE ); - if( node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY ) - { - ClassEntry classEntry = new ClassEntry( ref.getInternalName() ); - index.addReference( node.getIdentifierToken(), classEntry, m_behaviorEntry ); + public Void visitSimpleType(SimpleType node, SourceIndex index) { + TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); + if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { + ClassEntry classEntry = new ClassEntry(ref.getInternalName()); + index.addReference(node.getIdentifierToken(), classEntry, m_behaviorEntry); } - return recurse( node, index ); + return recurse(node, index); } @Override - public Void visitParameterDeclaration( ParameterDeclaration node, SourceIndex index ) - { - ParameterDefinition def = node.getUserData( Keys.PARAMETER_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); + public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { + ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); MethodDefinition methodDef = (MethodDefinition)def.getMethod(); BehaviorEntry behaviorEntry; - if( methodDef.isConstructor() ) - { - behaviorEntry = new ConstructorEntry( classEntry, methodDef.getSignature() ); - } - else - { - behaviorEntry = new MethodEntry( classEntry, methodDef.getName(), methodDef.getSignature() ); + if (methodDef.isConstructor()) { + behaviorEntry = new ConstructorEntry(classEntry, methodDef.getSignature()); + } else { + behaviorEntry = new MethodEntry(classEntry, methodDef.getName(), methodDef.getSignature()); } - ArgumentEntry argumentEntry = new ArgumentEntry( behaviorEntry, def.getPosition(), node.getName() ); - index.addDeclaration( node.getNameToken(), argumentEntry ); + ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); + index.addDeclaration(node.getNameToken(), argumentEntry); - return recurse( node, index ); + return recurse(node, index); } @Override - public Void visitIdentifierExpression( IdentifierExpression node, SourceIndex index ) - { - MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); - if( ref != null ) - { - ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); - FieldEntry fieldEntry = new FieldEntry( classEntry, ref.getName() ); - index.addReference( node.getIdentifierToken(), fieldEntry, m_behaviorEntry ); + public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName()); + index.addReference(node.getIdentifierToken(), fieldEntry, m_behaviorEntry); } - return recurse( node, index ); + return recurse(node, index); } @Override - public Void visitObjectCreationExpression( ObjectCreationExpression node, SourceIndex index ) - { - MemberReference ref = node.getUserData( Keys.MEMBER_REFERENCE ); - if( ref != null ) - { - ClassEntry classEntry = new ClassEntry( ref.getDeclaringType().getInternalName() ); - ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, ref.getSignature() ); - if( node.getType() instanceof SimpleType ) - { + public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, ref.getSignature()); + if (node.getType() instanceof SimpleType) { SimpleType simpleTypeNode = (SimpleType)node.getType(); - index.addReference( simpleTypeNode.getIdentifierToken(), constructorEntry, m_behaviorEntry ); + index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, m_behaviorEntry); } } - return recurse( node, index ); + return recurse(node, index); } } diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 24c48227..7b902a9d 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -31,95 +31,84 @@ import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.FieldEntry; -public class SourceIndexClassVisitor extends SourceIndexVisitor -{ +public class SourceIndexClassVisitor extends SourceIndexVisitor { + private ClassEntry m_classEntry; - public SourceIndexClassVisitor( ClassEntry classEntry ) - { + public SourceIndexClassVisitor(ClassEntry classEntry) { m_classEntry = classEntry; } @Override - public Void visitTypeDeclaration( TypeDeclaration node, SourceIndex index ) - { + public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { // is this this class, or a subtype? - TypeDefinition def = node.getUserData( Keys.TYPE_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getInternalName() ); - if( !classEntry.equals( m_classEntry ) ) - { + TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getInternalName()); + if (!classEntry.equals(m_classEntry)) { // it's a sub-type, recurse - index.addDeclaration( node.getNameToken(), classEntry ); - return node.acceptVisitor( new SourceIndexClassVisitor( classEntry ), index ); + index.addDeclaration(node.getNameToken(), classEntry); + return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); } - return recurse( node, index ); + return recurse(node, index); } @Override - public Void visitSimpleType( SimpleType node, SourceIndex index ) - { - TypeReference ref = node.getUserData( Keys.TYPE_REFERENCE ); - if( node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY ) - { - ClassEntry classEntry = new ClassEntry( ref.getInternalName() ); - index.addReference( node.getIdentifierToken(), classEntry, m_classEntry ); + public Void visitSimpleType(SimpleType node, SourceIndex index) { + TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); + if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { + ClassEntry classEntry = new ClassEntry(ref.getInternalName()); + index.addReference(node.getIdentifierToken(), classEntry, m_classEntry); } - return recurse( node, index ); + return recurse(node, index); } @Override - public Void visitMethodDeclaration( MethodDeclaration node, SourceIndex index ) - { - MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - BehaviorEntry behaviorEntry = BehaviorEntryFactory.create( classEntry, def.getName(), def.getSignature() ); + public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { + MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(classEntry, def.getName(), def.getSignature()); AstNode tokenNode = node.getNameToken(); - if( behaviorEntry instanceof ConstructorEntry ) - { + if (behaviorEntry instanceof ConstructorEntry) { ConstructorEntry constructorEntry = (ConstructorEntry)behaviorEntry; - if( constructorEntry.isStatic() ) - { + if (constructorEntry.isStatic()) { tokenNode = node.getModifiers().firstOrNullObject(); } } - index.addDeclaration( tokenNode, behaviorEntry ); - return node.acceptVisitor( new SourceIndexBehaviorVisitor( behaviorEntry ), index ); + index.addDeclaration(tokenNode, behaviorEntry); + return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry), index); } @Override - public Void visitConstructorDeclaration( ConstructorDeclaration node, SourceIndex index ) - { - MethodDefinition def = node.getUserData( Keys.METHOD_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, def.getSignature() ); - index.addDeclaration( node.getNameToken(), constructorEntry ); - return node.acceptVisitor( new SourceIndexBehaviorVisitor( constructorEntry ), index ); + public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { + MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, def.getSignature()); + index.addDeclaration(node.getNameToken(), constructorEntry); + return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); } @Override - public Void visitFieldDeclaration( FieldDeclaration node, SourceIndex index ) - { - FieldDefinition def = node.getUserData( Keys.FIELD_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - FieldEntry fieldEntry = new FieldEntry( classEntry, def.getName() ); - assert( node.getVariables().size() == 1 ); + public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { + FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName()); + assert (node.getVariables().size() == 1); VariableInitializer variable = node.getVariables().firstOrNullObject(); - index.addDeclaration( variable.getNameToken(), fieldEntry ); + index.addDeclaration(variable.getNameToken(), fieldEntry); - return recurse( node, index ); + return recurse(node, index); } @Override - public Void visitEnumValueDeclaration( EnumValueDeclaration node, SourceIndex index ) - { + public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { // treat enum declarations as field declarations - FieldDefinition def = node.getUserData( Keys.FIELD_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getDeclaringType().getInternalName() ); - FieldEntry fieldEntry = new FieldEntry( classEntry, def.getName() ); - index.addDeclaration( node.getNameToken(), fieldEntry ); + FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName()); + index.addDeclaration(node.getNameToken(), fieldEntry); - return recurse( node, index ); + return recurse(node, index); } } diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java index 4e98989e..0d5bdc02 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -87,438 +87,366 @@ import com.strobel.decompiler.patterns.Pattern; import cuchaz.enigma.mapping.ClassEntry; -public class SourceIndexVisitor implements IAstVisitor -{ - @Override - public Void visitTypeDeclaration( TypeDeclaration node, SourceIndex index ) - { - TypeDefinition def = node.getUserData( Keys.TYPE_DEFINITION ); - ClassEntry classEntry = new ClassEntry( def.getInternalName() ); - index.addDeclaration( node.getNameToken(), classEntry ); +public class SourceIndexVisitor implements IAstVisitor { + + @Override + public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { + TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getInternalName()); + index.addDeclaration(node.getNameToken(), classEntry); - return node.acceptVisitor( new SourceIndexClassVisitor( classEntry ), index ); + return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); } - protected Void recurse( AstNode node, SourceIndex index ) - { - for( final AstNode child : node.getChildren() ) - { - child.acceptVisitor( this, index ); + protected Void recurse(AstNode node, SourceIndex index) { + for (final AstNode child : node.getChildren()) { + child.acceptVisitor(this, index); } return null; } @Override - public Void visitMethodDeclaration( MethodDeclaration node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitConstructorDeclaration( ConstructorDeclaration node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitFieldDeclaration( FieldDeclaration node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitEnumValueDeclaration( EnumValueDeclaration node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitParameterDeclaration( ParameterDeclaration node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitInvocationExpression( InvocationExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitMemberReferenceExpression( MemberReferenceExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitSimpleType( SimpleType node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitSimpleType(SimpleType node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitIdentifierExpression( IdentifierExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitComment( Comment node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitComment(Comment node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitPatternPlaceholder( AstNode node, Pattern pattern, SourceIndex index ) - { - return recurse( node, index ); + public Void visitPatternPlaceholder(AstNode node, Pattern pattern, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitTypeReference( TypeReferenceExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitTypeReference(TypeReferenceExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitJavaTokenNode( JavaTokenNode node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitJavaTokenNode(JavaTokenNode node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitIdentifier( Identifier node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitIdentifier(Identifier node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitNullReferenceExpression( NullReferenceExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitNullReferenceExpression(NullReferenceExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitThisReferenceExpression( ThisReferenceExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitThisReferenceExpression(ThisReferenceExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitSuperReferenceExpression( SuperReferenceExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitSuperReferenceExpression(SuperReferenceExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitClassOfExpression( ClassOfExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitClassOfExpression(ClassOfExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitBlockStatement( BlockStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitBlockStatement(BlockStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitExpressionStatement( ExpressionStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitExpressionStatement(ExpressionStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitBreakStatement( BreakStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitBreakStatement(BreakStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitContinueStatement( ContinueStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitContinueStatement(ContinueStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitDoWhileStatement( DoWhileStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitDoWhileStatement(DoWhileStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitEmptyStatement( EmptyStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitEmptyStatement(EmptyStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitIfElseStatement( IfElseStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitIfElseStatement(IfElseStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitLabelStatement( LabelStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitLabelStatement(LabelStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitLabeledStatement( LabeledStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitLabeledStatement(LabeledStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitReturnStatement( ReturnStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitReturnStatement(ReturnStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitSwitchStatement( SwitchStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitSwitchStatement(SwitchStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitSwitchSection( SwitchSection node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitSwitchSection(SwitchSection node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitCaseLabel( CaseLabel node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitCaseLabel(CaseLabel node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitThrowStatement( ThrowStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitThrowStatement(ThrowStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitCatchClause( CatchClause node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitCatchClause(CatchClause node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitAnnotation( Annotation node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitAnnotation(Annotation node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitNewLine( NewLineNode node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitNewLine(NewLineNode node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitVariableDeclaration( VariableDeclarationStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitVariableInitializer( VariableInitializer node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitVariableInitializer(VariableInitializer node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitText( TextNode node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitText(TextNode node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitImportDeclaration( ImportDeclaration node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitImportDeclaration(ImportDeclaration node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitInitializerBlock( InstanceInitializer node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitInitializerBlock(InstanceInitializer node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitTypeParameterDeclaration( TypeParameterDeclaration node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitCompilationUnit( CompilationUnit node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitCompilationUnit(CompilationUnit node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitPackageDeclaration( PackageDeclaration node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitPackageDeclaration(PackageDeclaration node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitArraySpecifier( ArraySpecifier node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitArraySpecifier(ArraySpecifier node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitComposedType( ComposedType node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitComposedType(ComposedType node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitWhileStatement( WhileStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitWhileStatement(WhileStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitPrimitiveExpression( PrimitiveExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitPrimitiveExpression(PrimitiveExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitCastExpression( CastExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitCastExpression(CastExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitBinaryOperatorExpression( BinaryOperatorExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitInstanceOfExpression( InstanceOfExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitInstanceOfExpression(InstanceOfExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitIndexerExpression( IndexerExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitIndexerExpression(IndexerExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitUnaryOperatorExpression( UnaryOperatorExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitConditionalExpression( ConditionalExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitConditionalExpression(ConditionalExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitArrayInitializerExpression( ArrayInitializerExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitArrayInitializerExpression(ArrayInitializerExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitObjectCreationExpression( ObjectCreationExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitArrayCreationExpression( ArrayCreationExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitArrayCreationExpression(ArrayCreationExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitAssignmentExpression( AssignmentExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitAssignmentExpression(AssignmentExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitForStatement( ForStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitForStatement(ForStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitForEachStatement( ForEachStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitTryCatchStatement( TryCatchStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitTryCatchStatement(TryCatchStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitGotoStatement( GotoStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitGotoStatement(GotoStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitParenthesizedExpression( ParenthesizedExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitParenthesizedExpression(ParenthesizedExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitSynchronizedStatement( SynchronizedStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitSynchronizedStatement(SynchronizedStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitAnonymousObjectCreationExpression( AnonymousObjectCreationExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitWildcardType( WildcardType node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitWildcardType(WildcardType node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitMethodGroupExpression( MethodGroupExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitAssertStatement( AssertStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitAssertStatement(AssertStatement node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitLambdaExpression( LambdaExpression node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitLambdaExpression(LambdaExpression node, SourceIndex index) { + return recurse(node, index); } @Override - public Void visitLocalTypeDeclarationStatement( LocalTypeDeclarationStatement node, SourceIndex index ) - { - return recurse( node, index ); + public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, SourceIndex index) { + return recurse(node, index); } } diff --git a/src/cuchaz/enigma/analysis/Token.java b/src/cuchaz/enigma/analysis/Token.java index 5e70db71..481d2f47 100644 --- a/src/cuchaz/enigma/analysis/Token.java +++ b/src/cuchaz/enigma/analysis/Token.java @@ -10,56 +10,47 @@ ******************************************************************************/ package cuchaz.enigma.analysis; -public class Token implements Comparable -{ +public class Token implements Comparable { + public int start; public int end; public String text; - public Token( int start, int end ) - { - this( start, end, null ); + public Token(int start, int end) { + this(start, end, null); } - public Token( int start, int end, String source ) - { + public Token(int start, int end, String source) { this.start = start; this.end = end; - if( source != null ) - { - this.text = source.substring( start, end ); + if (source != null) { + this.text = source.substring(start, end); } } - public boolean contains( int pos ) - { + public boolean contains(int pos) { return pos >= start && pos <= end; } - + @Override - public int compareTo( Token other ) - { + public int compareTo(Token other) { return start - other.start; } @Override - public boolean equals( Object other ) - { - if( other instanceof Token ) - { - return equals( (Token)other ); + public boolean equals(Object other) { + if (other instanceof Token) { + return equals((Token)other); } return false; } - public boolean equals( Token other ) - { + public boolean equals(Token other) { return start == other.start && end == other.end; } @Override - public String toString( ) - { - return String.format( "[%d,%d]", start, end ); + public String toString() { + return String.format("[%d,%d]", start, end); } } diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index 5311ec70..c14fd593 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -23,104 +23,85 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; -public class TranslationIndex implements Serializable -{ +public class TranslationIndex implements Serializable { + private static final long serialVersionUID = 738687982126844179L; private Map m_superclasses; private Multimap m_fields; - public TranslationIndex( ) - { + public TranslationIndex() { m_superclasses = Maps.newHashMap(); m_fields = HashMultimap.create(); } - public TranslationIndex( TranslationIndex other ) - { - m_superclasses = Maps.newHashMap( other.m_superclasses ); - m_fields = HashMultimap.create( other.m_fields ); + public TranslationIndex(TranslationIndex other) { + m_superclasses = Maps.newHashMap(other.m_superclasses); + m_fields = HashMultimap.create(other.m_fields); } - public void addSuperclass( String className, String superclassName ) - { - className = Descriptor.toJvmName( className ); - superclassName = Descriptor.toJvmName( superclassName ); + public void addSuperclass(String className, String superclassName) { + className = Descriptor.toJvmName(className); + superclassName = Descriptor.toJvmName(superclassName); - if( className.equals( superclassName ) ) - { - throw new IllegalArgumentException( "Class cannot be its own superclass! " + className ); + if (className.equals(superclassName)) { + throw new IllegalArgumentException("Class cannot be its own superclass! " + className); } - if( !isJre( className ) && !isJre( superclassName ) ) - { - m_superclasses.put( className, superclassName ); + if (!isJre(className) && !isJre(superclassName)) { + m_superclasses.put(className, superclassName); } } - public void addField( String className, String fieldName ) - { - m_fields.put( className, fieldName ); + public void addField(String className, String fieldName) { + m_fields.put(className, fieldName); } - public void renameClasses( Map renames ) - { - EntryRenamer.renameClassesInMap( renames, m_superclasses ); - EntryRenamer.renameClassesInMultimap( renames, m_fields ); + public void renameClasses(Map renames) { + EntryRenamer.renameClassesInMap(renames, m_superclasses); + EntryRenamer.renameClassesInMultimap(renames, m_fields); } - public String getSuperclassName( String className ) - { - return m_superclasses.get( className ); + public String getSuperclassName(String className) { + return m_superclasses.get(className); } - public List getAncestry( String className ) - { + public List getAncestry(String className) { List ancestors = new ArrayList(); - while( className != null ) - { - className = getSuperclassName( className ); - if( className != null ) - { - ancestors.add( className ); + while (className != null) { + className = getSuperclassName(className); + if (className != null) { + ancestors.add(className); } } return ancestors; } - public List getSubclassNames( String className ) - { + public List getSubclassNames(String className) { // linear search is fast enough for now List subclasses = Lists.newArrayList(); - for( Map.Entry entry : m_superclasses.entrySet() ) - { + for (Map.Entry entry : m_superclasses.entrySet()) { String subclass = entry.getKey(); String superclass = entry.getValue(); - if( className.equals( superclass ) ) - { - subclasses.add( subclass ); + if (className.equals(superclass)) { + subclasses.add(subclass); } } return subclasses; } - public void getSubclassNamesRecursively( Set out, String className ) - { - for( String subclassName : getSubclassNames( className ) ) - { - out.add( subclassName ); - getSubclassNamesRecursively( out, subclassName ); + public void getSubclassNamesRecursively(Set out, String className) { + for (String subclassName : getSubclassNames(className)) { + out.add(subclassName); + getSubclassNamesRecursively(out, subclassName); } } - public boolean containsField( String className, String fieldName ) - { - return m_fields.containsEntry( className, fieldName ); + public boolean containsField(String className, String fieldName) { + return m_fields.containsEntry(className, fieldName); } - private boolean isJre( String className ) - { - return className.startsWith( "java/" ) - || className.startsWith( "javax/" ); + private boolean isJre(String className) { + return className.startsWith("java/") || className.startsWith("javax/"); } } diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java index e6ecb10e..23f80899 100644 --- a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java +++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -90,92 +90,73 @@ import com.strobel.decompiler.languages.java.ast.WhileStatement; import com.strobel.decompiler.languages.java.ast.WildcardType; import com.strobel.decompiler.patterns.Pattern; -public class TreeDumpVisitor implements IAstVisitor -{ +public class TreeDumpVisitor implements IAstVisitor { + private File m_file; private Writer m_out; - public TreeDumpVisitor( File file ) - { + public TreeDumpVisitor(File file) { m_file = file; m_out = null; } @Override - public Void visitCompilationUnit( CompilationUnit node, Void ignored ) - { - try - { - m_out = new FileWriter( m_file ); - recurse( node, ignored ); + public Void visitCompilationUnit(CompilationUnit node, Void ignored) { + try { + m_out = new FileWriter(m_file); + recurse(node, ignored); m_out.close(); return null; - } - catch( IOException ex ) - { - throw new Error( ex ); + } catch (IOException ex) { + throw new Error(ex); } } - - private Void recurse( AstNode node, Void ignored ) - { + + private Void recurse(AstNode node, Void ignored) { // show the tree - try - { - m_out.write( getIndent( node ) + node.getClass().getSimpleName() + " " + getText( node ) + " " + dumpUserData( node ) + " " + node.getRegion() + "\n" ); - } - catch( IOException ex ) - { - throw new Error( ex ); + try { + m_out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n"); + } catch (IOException ex) { + throw new Error(ex); } // recurse - for( final AstNode child : node.getChildren() ) - { - child.acceptVisitor( this, ignored ); + for (final AstNode child : node.getChildren()) { + child.acceptVisitor(this, ignored); } return null; } - private String getText( AstNode node ) - { - if( node instanceof Identifier ) - { + private String getText(AstNode node) { + if (node instanceof Identifier) { return "\"" + ((Identifier)node).getName() + "\""; } return ""; } - private String dumpUserData( AstNode node ) - { + private String dumpUserData(AstNode node) { StringBuilder buf = new StringBuilder(); - for( Key key : Keys.ALL_KEYS ) - { - Object val = node.getUserData( key ); - if( val != null ) - { - buf.append( String.format( " [%s=%s]", key, val ) ); + for (Key key : Keys.ALL_KEYS) { + Object val = node.getUserData(key); + if (val != null) { + buf.append(String.format(" [%s=%s]", key, val)); } } return buf.toString(); } - private String getIndent( AstNode node ) - { + private String getIndent(AstNode node) { StringBuilder buf = new StringBuilder(); - int depth = getDepth( node ); - for( int i = 0; i < depth; i++ ) - { - buf.append( "\t" ); + int depth = getDepth(node); + for (int i = 0; i < depth; i++) { + buf.append("\t"); } return buf.toString(); } - private int getDepth( AstNode node ) - { + private int getDepth(AstNode node) { int depth = -1; - while( node != null ) - { + while (node != null) { depth++; node = node.getParent(); } @@ -185,416 +166,347 @@ public class TreeDumpVisitor implements IAstVisitor // OVERRIDES WE DON'T CARE ABOUT @Override - public Void visitInvocationExpression( InvocationExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitInvocationExpression(InvocationExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitMemberReferenceExpression( MemberReferenceExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitMemberReferenceExpression(MemberReferenceExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitSimpleType( SimpleType node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitSimpleType(SimpleType node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitMethodDeclaration( MethodDeclaration node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitMethodDeclaration(MethodDeclaration node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitConstructorDeclaration( ConstructorDeclaration node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitConstructorDeclaration(ConstructorDeclaration node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitParameterDeclaration( ParameterDeclaration node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitParameterDeclaration(ParameterDeclaration node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitFieldDeclaration( FieldDeclaration node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitFieldDeclaration(FieldDeclaration node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitTypeDeclaration( TypeDeclaration node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitTypeDeclaration(TypeDeclaration node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitComment( Comment node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitComment(Comment node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitPatternPlaceholder( AstNode node, Pattern pattern, Void ignored ) - { - return recurse( node, ignored ); + public Void visitPatternPlaceholder(AstNode node, Pattern pattern, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitTypeReference( TypeReferenceExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitTypeReference(TypeReferenceExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitJavaTokenNode( JavaTokenNode node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitJavaTokenNode(JavaTokenNode node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitIdentifier( Identifier node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitIdentifier(Identifier node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitNullReferenceExpression( NullReferenceExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitNullReferenceExpression(NullReferenceExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitThisReferenceExpression( ThisReferenceExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitThisReferenceExpression(ThisReferenceExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitSuperReferenceExpression( SuperReferenceExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitSuperReferenceExpression(SuperReferenceExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitClassOfExpression( ClassOfExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitClassOfExpression(ClassOfExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitBlockStatement( BlockStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitBlockStatement(BlockStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitExpressionStatement( ExpressionStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitExpressionStatement(ExpressionStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitBreakStatement( BreakStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitBreakStatement(BreakStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitContinueStatement( ContinueStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitContinueStatement(ContinueStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitDoWhileStatement( DoWhileStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitDoWhileStatement(DoWhileStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitEmptyStatement( EmptyStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitEmptyStatement(EmptyStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitIfElseStatement( IfElseStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitIfElseStatement(IfElseStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitLabelStatement( LabelStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitLabelStatement(LabelStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitLabeledStatement( LabeledStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitLabeledStatement(LabeledStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitReturnStatement( ReturnStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitReturnStatement(ReturnStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitSwitchStatement( SwitchStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitSwitchStatement(SwitchStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitSwitchSection( SwitchSection node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitSwitchSection(SwitchSection node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitCaseLabel( CaseLabel node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitCaseLabel(CaseLabel node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitThrowStatement( ThrowStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitThrowStatement(ThrowStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitCatchClause( CatchClause node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitCatchClause(CatchClause node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitAnnotation( Annotation node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitAnnotation(Annotation node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitNewLine( NewLineNode node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitNewLine(NewLineNode node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitVariableDeclaration( VariableDeclarationStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitVariableDeclaration(VariableDeclarationStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitVariableInitializer( VariableInitializer node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitVariableInitializer(VariableInitializer node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitText( TextNode node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitText(TextNode node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitImportDeclaration( ImportDeclaration node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitImportDeclaration(ImportDeclaration node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitInitializerBlock( InstanceInitializer node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitInitializerBlock(InstanceInitializer node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitTypeParameterDeclaration( TypeParameterDeclaration node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitPackageDeclaration( PackageDeclaration node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitPackageDeclaration(PackageDeclaration node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitArraySpecifier( ArraySpecifier node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitArraySpecifier(ArraySpecifier node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitComposedType( ComposedType node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitComposedType(ComposedType node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitWhileStatement( WhileStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitWhileStatement(WhileStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitPrimitiveExpression( PrimitiveExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitPrimitiveExpression(PrimitiveExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitCastExpression( CastExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitCastExpression(CastExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitBinaryOperatorExpression( BinaryOperatorExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitInstanceOfExpression( InstanceOfExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitInstanceOfExpression(InstanceOfExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitIndexerExpression( IndexerExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitIndexerExpression(IndexerExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitIdentifierExpression( IdentifierExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitIdentifierExpression(IdentifierExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitUnaryOperatorExpression( UnaryOperatorExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitConditionalExpression( ConditionalExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitConditionalExpression(ConditionalExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitArrayInitializerExpression( ArrayInitializerExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitArrayInitializerExpression(ArrayInitializerExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitObjectCreationExpression( ObjectCreationExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitObjectCreationExpression(ObjectCreationExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitArrayCreationExpression( ArrayCreationExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitArrayCreationExpression(ArrayCreationExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitAssignmentExpression( AssignmentExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitAssignmentExpression(AssignmentExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitForStatement( ForStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitForStatement(ForStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitForEachStatement( ForEachStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitForEachStatement(ForEachStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitTryCatchStatement( TryCatchStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitTryCatchStatement(TryCatchStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitGotoStatement( GotoStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitGotoStatement(GotoStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitParenthesizedExpression( ParenthesizedExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitParenthesizedExpression(ParenthesizedExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitSynchronizedStatement( SynchronizedStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitSynchronizedStatement(SynchronizedStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitAnonymousObjectCreationExpression( AnonymousObjectCreationExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitWildcardType( WildcardType node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitWildcardType(WildcardType node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitMethodGroupExpression( MethodGroupExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitMethodGroupExpression(MethodGroupExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitEnumValueDeclaration( EnumValueDeclaration node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitAssertStatement( AssertStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitAssertStatement(AssertStatement node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitLambdaExpression( LambdaExpression node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitLambdaExpression(LambdaExpression node, Void ignored) { + return recurse(node, ignored); } @Override - public Void visitLocalTypeDeclarationStatement( LocalTypeDeclarationStatement node, Void ignored ) - { - return recurse( node, ignored ); + public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, Void ignored) { + return recurse(node, ignored); } } diff --git a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java index aadbeb25..fc2bac3d 100644 --- a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java +++ b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java @@ -18,67 +18,53 @@ import javassist.bytecode.CodeAttribute; import javassist.bytecode.CodeIterator; import javassist.bytecode.Opcode; -public class BytecodeIndexIterator implements Iterator -{ - public static class Index - { +public class BytecodeIndexIterator implements Iterator { + + public static class Index { + private CodeIterator m_iter; private int m_pos; private boolean m_isWide; - protected Index( CodeIterator iter, int pos, boolean isWide ) - { + protected Index(CodeIterator iter, int pos, boolean isWide) { m_iter = iter; m_pos = pos; m_isWide = isWide; } - public int getIndex( ) - { - if( m_isWide ) - { - return m_iter.s16bitAt( m_pos ); - } - else - { - return m_iter.byteAt( m_pos ); + public int getIndex() { + if (m_isWide) { + return m_iter.s16bitAt(m_pos); + } else { + return m_iter.byteAt(m_pos); } } - public void setIndex( int val ) - throws BadBytecode - { - if( m_isWide ) - { - m_iter.write16bit( val, m_pos ); - } - else - { - if( val < 256 ) - { + public void setIndex(int val) throws BadBytecode { + if (m_isWide) { + m_iter.write16bit(val, m_pos); + } else { + if (val < 256) { // we can write the byte - m_iter.writeByte( val, m_pos ); - } - else - { + m_iter.writeByte(val, m_pos); + } else { // we need to upgrade this instruction to LDC_W - assert( m_iter.byteAt( m_pos - 1 ) == Opcode.LDC ); - m_iter.insertGap( m_pos - 1, 1 ); - m_iter.writeByte( Opcode.LDC_W, m_pos - 1 ); - m_iter.write16bit( val, m_pos ); + assert (m_iter.byteAt(m_pos - 1) == Opcode.LDC); + m_iter.insertGap(m_pos - 1, 1); + m_iter.writeByte(Opcode.LDC_W, m_pos - 1); + m_iter.write16bit(val, m_pos); m_isWide = true; // move the iterator to the next opcode - m_iter.move( m_pos + 2 ); + m_iter.move(m_pos + 2); } } // sanity check - assert( val == getIndex() ); + assert (val == getIndex()); } - public boolean isValid( Bytecode bytecode ) - { + public boolean isValid(Bytecode bytecode) { return getIndex() >= 0 && getIndex() < bytecode.getConstPool().getSize(); } } @@ -88,9 +74,7 @@ public class BytecodeIndexIterator implements Iterator indices( ) - { - return new Iterable( ) - { + public Iterable indices() { + return new Iterable() { @Override - public Iterator iterator( ) - { + public Iterator iterator() { return BytecodeIndexIterator.this; } }; } - public void saveChangesToBytecode( ) - { - BytecodeTools.setBytecode( m_bytecode, m_attribute.getCode() ); + public void saveChangesToBytecode() { + BytecodeTools.setBytecode(m_bytecode, m_attribute.getCode()); } } diff --git a/src/cuchaz/enigma/bytecode/BytecodeTools.java b/src/cuchaz/enigma/bytecode/BytecodeTools.java index 4407a904..2e456f45 100644 --- a/src/cuchaz/enigma/bytecode/BytecodeTools.java +++ b/src/cuchaz/enigma/bytecode/BytecodeTools.java @@ -34,256 +34,222 @@ import cuchaz.enigma.Util; import cuchaz.enigma.bytecode.BytecodeIndexIterator.Index; import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; -public class BytecodeTools -{ - public static byte[] writeBytecode( Bytecode bytecode ) - throws IOException - { +public class BytecodeTools { + + public static byte[] writeBytecode(Bytecode bytecode) throws IOException { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream( buf ); - try - { + DataOutputStream out = new DataOutputStream(buf); + + try { // write the constant pool - new ConstPoolEditor( bytecode.getConstPool() ).writePool( out ); + new ConstPoolEditor(bytecode.getConstPool()).writePool(out); // write metadata - out.writeShort( bytecode.getMaxStack() ); - out.writeShort( bytecode.getMaxLocals() ); - out.writeShort( bytecode.getStackDepth() ); + out.writeShort(bytecode.getMaxStack()); + out.writeShort(bytecode.getMaxLocals()); + out.writeShort(bytecode.getStackDepth()); // write the code - out.writeShort( bytecode.getSize() ); - out.write( bytecode.get() ); + out.writeShort(bytecode.getSize()); + out.write(bytecode.get()); // write the exception table int numEntries = bytecode.getExceptionTable().size(); - out.writeShort( numEntries ); - for( int i=0; i attribute.getMaxLocals() ) - { - attribute.setMaxLocals( bytecode.getMaxLocals() ); + if (bytecode.getMaxLocals() > attribute.getMaxLocals()) { + attribute.setMaxLocals(bytecode.getMaxLocals()); } - if( bytecode.getMaxStack() > attribute.getMaxStack() ) - { - attribute.setMaxStack( bytecode.getMaxStack() ); + if (bytecode.getMaxStack() > attribute.getMaxStack()) { + attribute.setMaxStack(bytecode.getMaxStack()); } return bytecode; } - public static Bytecode copyBytecodeToConstPool( ConstPool dest, Bytecode bytecode ) - throws BadBytecode - { + public static Bytecode copyBytecodeToConstPool(ConstPool dest, Bytecode bytecode) throws BadBytecode { + // get the entries this bytecode needs from the const pool Set indices = Sets.newTreeSet(); - ConstPoolEditor editor = new ConstPoolEditor( bytecode.getConstPool() ); - BytecodeIndexIterator iterator = new BytecodeIndexIterator( bytecode ); - for( Index index : iterator.indices() ) - { - assert( index.isValid( bytecode ) ); - InfoType.gatherIndexTree( indices, editor, index.getIndex() ); + ConstPoolEditor editor = new ConstPoolEditor(bytecode.getConstPool()); + BytecodeIndexIterator iterator = new BytecodeIndexIterator(bytecode); + for (Index index : iterator.indices()) { + assert (index.isValid(bytecode)); + InfoType.gatherIndexTree(indices, editor, index.getIndex()); } Map indexMap = Maps.newTreeMap(); ConstPool src = bytecode.getConstPool(); - ConstPoolEditor editorSrc = new ConstPoolEditor( src ); - ConstPoolEditor editorDest = new ConstPoolEditor( dest ); + ConstPoolEditor editorSrc = new ConstPoolEditor(src); + ConstPoolEditor editorDest = new ConstPoolEditor(dest); // copy entries over in order of level so the index mapping is easier - for( InfoType type : InfoType.getSortedByLevel() ) - { - for( int index : indices ) - { - ConstInfoAccessor entry = editorSrc.getItem( index ); + for (InfoType type : InfoType.getSortedByLevel()) { + for (int index : indices) { + ConstInfoAccessor entry = editorSrc.getItem(index); // skip entries that aren't this type - if( entry.getType() != type ) - { + if (entry.getType() != type) { continue; } // make sure the source entry is valid before we copy it - assert( type.subIndicesAreValid( entry, editorSrc ) ); - assert( type.selfIndexIsValid( entry, editorSrc ) ); + assert (type.subIndicesAreValid(entry, editorSrc)); + assert (type.selfIndexIsValid(entry, editorSrc)); // make a copy of the entry so we can modify it safely - ConstInfoAccessor entryCopy = editorSrc.getItem( index ).copy(); - assert( type.subIndicesAreValid( entryCopy, editorSrc ) ); - assert( type.selfIndexIsValid( entryCopy, editorSrc ) ); + ConstInfoAccessor entryCopy = editorSrc.getItem(index).copy(); + assert (type.subIndicesAreValid(entryCopy, editorSrc)); + assert (type.selfIndexIsValid(entryCopy, editorSrc)); // remap the indices - type.remapIndices( indexMap, entryCopy ); - assert( type.subIndicesAreValid( entryCopy, editorDest ) ); + type.remapIndices(indexMap, entryCopy); + assert (type.subIndicesAreValid(entryCopy, editorDest)); // put the copy in the destination pool - int newIndex = editorDest.addItem( entryCopy.getItem() ); - entryCopy.setIndex( newIndex ); - assert( type.selfIndexIsValid( entryCopy, editorDest ) ) : type + ", self: " + entryCopy + " dest: " + editorDest.getItem( entryCopy.getIndex() ); + int newIndex = editorDest.addItem(entryCopy.getItem()); + entryCopy.setIndex(newIndex); + assert (type.selfIndexIsValid(entryCopy, editorDest)) : type + ", self: " + entryCopy + " dest: " + editorDest.getItem(entryCopy.getIndex()); // make sure the source entry is unchanged - assert( type.subIndicesAreValid( entry, editorSrc ) ); - assert( type.selfIndexIsValid( entry, editorSrc ) ); + assert (type.subIndicesAreValid(entry, editorSrc)); + assert (type.selfIndexIsValid(entry, editorSrc)); // add the index mapping so we can update the bytecode later - if( indexMap.containsKey( index ) ) - { - throw new Error( "Entry at index " + index + " already copied!" ); + if (indexMap.containsKey(index)) { + throw new Error("Entry at index " + index + " already copied!"); } - indexMap.put( index, newIndex ); + indexMap.put(index, newIndex); } } // make a new bytecode - Bytecode newBytecode = new Bytecode( dest, bytecode.getMaxStack(), bytecode.getMaxLocals() ); - bytecode.setStackDepth( bytecode.getStackDepth() ); - setBytecode( newBytecode, bytecode.get() ); - setExceptionTable( newBytecode, bytecode.getExceptionTable() ); + Bytecode newBytecode = new Bytecode(dest, bytecode.getMaxStack(), bytecode.getMaxLocals()); + bytecode.setStackDepth(bytecode.getStackDepth()); + setBytecode(newBytecode, bytecode.get()); + setExceptionTable(newBytecode, bytecode.getExceptionTable()); // apply the mappings to the bytecode - BytecodeIndexIterator iter = new BytecodeIndexIterator( newBytecode ); - for( Index index : iter.indices() ) - { + BytecodeIndexIterator iter = new BytecodeIndexIterator(newBytecode); + for (Index index : iter.indices()) { int oldIndex = index.getIndex(); - Integer newIndex = indexMap.get( oldIndex ); - if( newIndex != null ) - { + Integer newIndex = indexMap.get(oldIndex); + if (newIndex != null) { // make sure this mapping makes sense - InfoType typeSrc = editorSrc.getItem( oldIndex ).getType(); - InfoType typeDest = editorDest.getItem( newIndex ).getType(); - assert( typeSrc == typeDest ); + InfoType typeSrc = editorSrc.getItem(oldIndex).getType(); + InfoType typeDest = editorDest.getItem(newIndex).getType(); + assert (typeSrc == typeDest); // apply the mapping - index.setIndex( newIndex ); + index.setIndex(newIndex); } } iter.saveChangesToBytecode(); // make sure all the indices are valid - iter = new BytecodeIndexIterator( newBytecode ); - for( Index index : iter.indices() ) - { - assert( index.isValid( newBytecode ) ); + iter = new BytecodeIndexIterator(newBytecode); + for (Index index : iter.indices()) { + assert (index.isValid(newBytecode)); } return newBytecode; } - public static void setBytecode( Bytecode dest, byte[] src ) - { - if( src.length > dest.getSize() ) - { - dest.addGap( src.length - dest.getSize() ); + public static void setBytecode(Bytecode dest, byte[] src) { + if (src.length > dest.getSize()) { + dest.addGap(src.length - dest.getSize()); } - assert( dest.getSize() == src.length ); - for( int i=0; i=0; i-- ) - { - dest.getExceptionTable().remove( i ); + for (int i = size - 1; i >= 0; i--) { + dest.getExceptionTable().remove(i); } // copy the exception table - for( int i=0; i getParameterTypes( String signature ) - { + public static List getParameterTypes(String signature) { List types = Lists.newArrayList(); - for( int i=0; i 0 ) - { + while (arrayDim-- > 0) { type = "[" + type; } - types.add( type ); + types.add(type); } return types; } diff --git a/src/cuchaz/enigma/bytecode/CheckCastIterator.java b/src/cuchaz/enigma/bytecode/CheckCastIterator.java index 7ed5d7fb..b6efbd49 100644 --- a/src/cuchaz/enigma/bytecode/CheckCastIterator.java +++ b/src/cuchaz/enigma/bytecode/CheckCastIterator.java @@ -22,15 +22,14 @@ import cuchaz.enigma.bytecode.CheckCastIterator.CheckCast; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.MethodEntry; -public class CheckCastIterator implements Iterator -{ - public static class CheckCast - { +public class CheckCastIterator implements Iterator { + + public static class CheckCast { + public String className; public MethodEntry prevMethodEntry; - public CheckCast( String className, MethodEntry prevMethodEntry ) - { + public CheckCast(String className, MethodEntry prevMethodEntry) { this.className = className; this.prevMethodEntry = prevMethodEntry; } @@ -41,9 +40,7 @@ public class CheckCastIterator implements Iterator private CodeIterator m_iter; private CheckCast m_next; - public CheckCastIterator( CodeAttribute codeAttribute ) - throws BadBytecode - { + public CheckCastIterator(CodeAttribute codeAttribute) throws BadBytecode { m_constants = codeAttribute.getConstPool(); m_attribute = codeAttribute; m_iter = m_attribute.iterator(); @@ -52,52 +49,38 @@ public class CheckCastIterator implements Iterator } @Override - public boolean hasNext( ) - { + public boolean hasNext() { return m_next != null; } - + @Override - public CheckCast next( ) - { + public CheckCast next() { CheckCast out = m_next; - try - { + try { m_next = getNext(); - } - catch( BadBytecode ex ) - { - throw new Error( ex ); + } catch (BadBytecode ex) { + throw new Error(ex); } return out; } - + @Override - public void remove( ) - { + public void remove() { throw new UnsupportedOperationException(); } - private CheckCast getNext( ) - throws BadBytecode - { + private CheckCast getNext() throws BadBytecode { int prevPos = 0; - while( m_iter.hasNext() ) - { + while (m_iter.hasNext()) { int pos = m_iter.next(); - int opcode = m_iter.byteAt( pos ); - switch( opcode ) - { + int opcode = m_iter.byteAt(pos); + switch (opcode) { case Opcode.CHECKCAST: // get the type of this op code (next two bytes are a classinfo index) - MethodEntry prevMethodEntry = getMethodEntry( prevPos ); - if( prevMethodEntry != null ) - { - return new CheckCast( - m_constants.getClassInfo( m_iter.s16bitAt( pos + 1 ) ), - prevMethodEntry - ); + MethodEntry prevMethodEntry = getMethodEntry(prevPos); + if (prevMethodEntry != null) { + return new CheckCast(m_constants.getClassInfo(m_iter.s16bitAt(pos + 1)), prevMethodEntry); } break; } @@ -106,43 +89,36 @@ public class CheckCastIterator implements Iterator return null; } - private MethodEntry getMethodEntry( int pos ) - { - switch( m_iter.byteAt( pos ) ) - { + private MethodEntry getMethodEntry(int pos) { + switch (m_iter.byteAt(pos)) { case Opcode.INVOKEVIRTUAL: case Opcode.INVOKESTATIC: case Opcode.INVOKEDYNAMIC: - case Opcode.INVOKESPECIAL: - { - int index = m_iter.s16bitAt( pos + 1 ); + case Opcode.INVOKESPECIAL: { + int index = m_iter.s16bitAt(pos + 1); return new MethodEntry( - new ClassEntry( Descriptor.toJvmName( m_constants.getMethodrefClassName( index ) ) ), - m_constants.getMethodrefName( index ), - m_constants.getMethodrefType( index ) + new ClassEntry(Descriptor.toJvmName(m_constants.getMethodrefClassName(index))), + m_constants.getMethodrefName(index), + m_constants.getMethodrefType(index) ); } - case Opcode.INVOKEINTERFACE: - { - int index = m_iter.s16bitAt( pos + 1 ); + case Opcode.INVOKEINTERFACE: { + int index = m_iter.s16bitAt(pos + 1); return new MethodEntry( - new ClassEntry( Descriptor.toJvmName( m_constants.getInterfaceMethodrefClassName( index ) ) ), - m_constants.getInterfaceMethodrefName( index ), - m_constants.getInterfaceMethodrefType( index ) + new ClassEntry(Descriptor.toJvmName(m_constants.getInterfaceMethodrefClassName(index))), + m_constants.getInterfaceMethodrefName(index), + m_constants.getInterfaceMethodrefType(index) ); } } return null; } - - public Iterable casts( ) - { - return new Iterable( ) - { + + public Iterable casts() { + return new Iterable() { @Override - public Iterator iterator( ) - { + public Iterator iterator() { return CheckCastIterator.this; } }; diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index 849a3233..f8e63d16 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -27,55 +27,43 @@ import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.SignatureUpdater; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; -public class ClassRenamer -{ - public static void renameClasses( CtClass c, Map map ) - { +public class ClassRenamer { + + public static void renameClasses(CtClass c, Map map) { + // build the map used by javassist ClassMap nameMap = new ClassMap(); - for( Map.Entry entry : map.entrySet() ) - { - nameMap.put( entry.getKey().getName(), entry.getValue().getName() ); + for (Map.Entry entry : map.entrySet()) { + nameMap.put(entry.getKey().getName(), entry.getValue().getName()); } - c.replaceClassName( nameMap ); + c.replaceClassName(nameMap); // replace simple names in the InnerClasses attribute too ConstPool constants = c.getClassFile().getConstPool(); - InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); - if( attr != null ) - { - for( int i=0; i ATTR: %s,%s,%s", - classEntry, - attr.outerClass( i ), - attr.innerClass( i ), - attr.innerName( i ) - ) ); + System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); */ } } } - public static Set getAllClassEntries( final CtClass c ) - { + public static Set getAllClassEntries(final CtClass c) { + // get the classes that javassist knows about final Set entries = Sets.newHashSet(); - ClassMap map = new ClassMap( ) - { + ClassMap map = new ClassMap() { @Override - public Object get( Object obj ) - { - if( obj instanceof String ) - { + public Object get(Object obj) { + if (obj instanceof String) { String str = (String)obj; // javassist throws a lot of weird things at this map @@ -83,69 +71,60 @@ public class ClassRenamer // I'm opting to filter out the weirdness for now // skip anything with generic arguments - if( str.indexOf( '<' ) >= 0 || str.indexOf( '>' ) >= 0 || str.indexOf( ';' ) >= 0 ) - { + if (str.indexOf('<') >= 0 || str.indexOf('>') >= 0 || str.indexOf(';') >= 0) { return null; } // convert path/to/class.inner to path/to/class$inner - str = str.replace( '.', '$' ); + str = str.replace('.', '$'); // remember everything else - entries.add( new ClassEntry( str ) ); + entries.add(new ClassEntry(str)); } return null; } + private static final long serialVersionUID = -202160293602070641L; }; - c.replaceClassName( map ); + c.replaceClassName(map); return entries; } - public static void moveAllClassesOutOfDefaultPackage( CtClass c, String newPackageName ) - { + public static void moveAllClassesOutOfDefaultPackage(CtClass c, String newPackageName) { + // rename all classes Map map = Maps.newHashMap(); - for( ClassEntry classEntry : ClassRenamer.getAllClassEntries( c ) ) - { - if( classEntry.isInDefaultPackage() ) - { - map.put( classEntry, new ClassEntry( newPackageName + "/" + classEntry.getName() ) ); + for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { + if (classEntry.isInDefaultPackage()) { + map.put(classEntry, new ClassEntry(newPackageName + "/" + classEntry.getName())); } } - ClassRenamer.renameClasses( c, map ); + ClassRenamer.renameClasses(c, map); // TEMP - for( ClassEntry classEntry : ClassRenamer.getAllClassEntries( c ) ) - { - if( classEntry.isInDefaultPackage() ) - { - throw new Error( "!!! " + classEntry ); + for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { + if (classEntry.isInDefaultPackage()) { + throw new Error("!!! " + classEntry); } } // TEMP - for( CtBehavior behavior : c.getDeclaredBehaviors() ) - { - if( behavior.getSignature() == null ) - { + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + if (behavior.getSignature() == null) { continue; } - SignatureUpdater.update( behavior.getSignature(), new ClassNameUpdater( ) - { + SignatureUpdater.update(behavior.getSignature(), new ClassNameUpdater() { @Override - public String update( String className ) - { - ClassEntry classEntry = new ClassEntry( className ); - if( classEntry.isInDefaultPackage() ) - { - throw new Error( "!!! " + className ); + public String update(String className) { + ClassEntry classEntry = new ClassEntry(className); + if (classEntry.isInDefaultPackage()) { + throw new Error("!!! " + className); } return className; } - } ); + }); } } } diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 181fadb1..bc12405c 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -28,116 +28,102 @@ import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; -public class ClassTranslator -{ +public class ClassTranslator { + private Translator m_translator; - public ClassTranslator( Translator translator ) - { + public ClassTranslator(Translator translator) { m_translator = translator; } - public void translate( CtClass c ) - { + public void translate(CtClass c) { // NOTE: the order of these translations is very important // translate all the field and method references in the code by editing the constant pool ConstPool constants = c.getClassFile().getConstPool(); - ConstPoolEditor editor = new ConstPoolEditor( constants ); - for( int i=1; i map = Maps.newHashMap(); - for( ClassEntry obfClassEntry : ClassRenamer.getAllClassEntries( c ) ) - { - ClassEntry deobfClassEntry = m_translator.translateEntry( obfClassEntry ); - if( !obfClassEntry.equals( deobfClassEntry ) ) - { - map.put( obfClassEntry, deobfClassEntry ); + for (ClassEntry obfClassEntry : ClassRenamer.getAllClassEntries(c)) { + ClassEntry deobfClassEntry = m_translator.translateEntry(obfClassEntry); + if (!obfClassEntry.equals(deobfClassEntry)) { + map.put(obfClassEntry, deobfClassEntry); } } - ClassRenamer.renameClasses( c, map ); + ClassRenamer.renameClasses(c, map); } } diff --git a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java index aa6149c9..2dec3b76 100644 --- a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java +++ b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java @@ -23,8 +23,8 @@ import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor; import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; -public class ConstPoolEditor -{ +public class ConstPoolEditor { + private static Method m_getItem; private static Method m_addItem; private static Method m_addItem0; @@ -36,264 +36,213 @@ public class ConstPoolEditor private static Method m_methodWritePool; private static Constructor m_constructorPool; - static - { - try - { - m_getItem = ConstPool.class.getDeclaredMethod( "getItem", int.class ); - m_getItem.setAccessible( true ); + static { + try { + m_getItem = ConstPool.class.getDeclaredMethod("getItem", int.class); + m_getItem.setAccessible(true); - m_addItem = ConstPool.class.getDeclaredMethod( "addItem", Class.forName( "javassist.bytecode.ConstInfo" ) ); - m_addItem.setAccessible( true ); + m_addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo")); + m_addItem.setAccessible(true); - m_addItem0 = ConstPool.class.getDeclaredMethod( "addItem0", Class.forName( "javassist.bytecode.ConstInfo" ) ); - m_addItem0.setAccessible( true ); + m_addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo")); + m_addItem0.setAccessible(true); - m_items = ConstPool.class.getDeclaredField( "items" ); - m_items.setAccessible( true ); + m_items = ConstPool.class.getDeclaredField("items"); + m_items.setAccessible(true); - m_cache = ConstPool.class.getDeclaredField( "itemsCache" ); - m_cache.setAccessible( true ); + m_cache = ConstPool.class.getDeclaredField("itemsCache"); + m_cache.setAccessible(true); - m_numItems = ConstPool.class.getDeclaredField( "numOfItems" ); - m_numItems.setAccessible( true ); + m_numItems = ConstPool.class.getDeclaredField("numOfItems"); + m_numItems.setAccessible(true); - m_objects = Class.forName( "javassist.bytecode.LongVector" ).getDeclaredField( "objects" ); - m_objects.setAccessible( true ); + m_objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects"); + m_objects.setAccessible(true); - m_elements = Class.forName( "javassist.bytecode.LongVector" ).getDeclaredField( "elements" ); - m_elements.setAccessible( true ); + m_elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements"); + m_elements.setAccessible(true); - m_methodWritePool = ConstPool.class.getDeclaredMethod( "write", DataOutputStream.class ); - m_methodWritePool.setAccessible( true ); + m_methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class); + m_methodWritePool.setAccessible(true); - m_constructorPool = ConstPool.class.getDeclaredConstructor( DataInputStream.class ); - m_constructorPool.setAccessible( true ); - } - catch( Exception ex ) - { - throw new Error( ex ); + m_constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class); + m_constructorPool.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); } } private ConstPool m_pool; - public ConstPoolEditor( ConstPool pool ) - { + public ConstPoolEditor(ConstPool pool) { m_pool = pool; } - public void writePool( DataOutputStream out ) - { - try - { - m_methodWritePool.invoke( m_pool, out ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void writePool(DataOutputStream out) { + try { + m_methodWritePool.invoke(m_pool, out); + } catch (Exception ex) { + throw new Error(ex); } } - public static ConstPool readPool( DataInputStream in ) - { - try - { - return m_constructorPool.newInstance( in ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public static ConstPool readPool(DataInputStream in) { + try { + return m_constructorPool.newInstance(in); + } catch (Exception ex) { + throw new Error(ex); } } - public String getMemberrefClassname( int memberrefIndex ) - { - return Descriptor.toJvmName( m_pool.getClassInfo( m_pool.getMemberClass( memberrefIndex ) ) ); + public String getMemberrefClassname(int memberrefIndex) { + return Descriptor.toJvmName(m_pool.getClassInfo(m_pool.getMemberClass(memberrefIndex))); } - public String getMemberrefName( int memberrefIndex ) - { - return m_pool.getUtf8Info( m_pool.getNameAndTypeName( m_pool.getMemberNameAndType( memberrefIndex ) ) ); + public String getMemberrefName(int memberrefIndex) { + return m_pool.getUtf8Info(m_pool.getNameAndTypeName(m_pool.getMemberNameAndType(memberrefIndex))); } - public String getMemberrefType( int memberrefIndex ) - { - return m_pool.getUtf8Info( m_pool.getNameAndTypeDescriptor( m_pool.getMemberNameAndType( memberrefIndex ) ) ); + public String getMemberrefType(int memberrefIndex) { + return m_pool.getUtf8Info(m_pool.getNameAndTypeDescriptor(m_pool.getMemberNameAndType(memberrefIndex))); } - public ConstInfoAccessor getItem( int index ) - { - try - { - Object entry = m_getItem.invoke( m_pool, index ); - if( entry == null ) - { + public ConstInfoAccessor getItem(int index) { + try { + Object entry = m_getItem.invoke(m_pool, index); + if (entry == null) { return null; } - return new ConstInfoAccessor( entry ); - } - catch( Exception ex ) - { - throw new Error( ex ); + return new ConstInfoAccessor(entry); + } catch (Exception ex) { + throw new Error(ex); } } - public int addItem( Object item ) - { - try - { - return (Integer)m_addItem.invoke( m_pool, item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int addItem(Object item) { + try { + return (Integer)m_addItem.invoke(m_pool, item); + } catch (Exception ex) { + throw new Error(ex); } } - public int addItemForceNew( Object item ) - { - try - { - return (Integer)m_addItem0.invoke( m_pool, item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int addItemForceNew(Object item) { + try { + return (Integer)m_addItem0.invoke(m_pool, item); + } catch (Exception ex) { + throw new Error(ex); } } - @SuppressWarnings( "rawtypes" ) - public void removeLastItem( ) - { - try - { + + @SuppressWarnings("rawtypes") + public void removeLastItem() { + try { // remove the item from the cache HashMap cache = getCache(); - if( cache != null ) - { - Object item = getItem( m_pool.getSize() - 1 ); - cache.remove( item ); + if (cache != null) { + Object item = getItem(m_pool.getSize() - 1); + cache.remove(item); } // remove the actual item // based off of LongVector.addElement() - Object items = m_items.get( m_pool ); - Object[][] objects = (Object[][])m_objects.get( items ); - int numElements = (Integer)m_elements.get( items ) - 1; + Object items = m_items.get(m_pool); + Object[][] objects = (Object[][])m_objects.get(items); + int numElements = (Integer)m_elements.get(items) - 1; int nth = numElements >> 7; - int offset = numElements & (128 - 1); - objects[nth][offset] = null; + int offset = numElements & (128 - 1); + objects[nth][offset] = null; // decrement the number of items - m_elements.set( items, numElements ); - m_numItems.set( m_pool, (Integer)m_numItems.get( m_pool ) - 1 ); - } - catch( Exception ex ) - { - throw new Error( ex ); + m_elements.set(items, numElements); + m_numItems.set(m_pool, (Integer)m_numItems.get(m_pool) - 1); + } catch (Exception ex) { + throw new Error(ex); } } - @SuppressWarnings( "rawtypes" ) - /* TEMP */public HashMap getCache( ) - { - try - { - return (HashMap)m_cache.get( m_pool ); - } - catch( Exception ex ) - { - throw new Error( ex ); + @SuppressWarnings("rawtypes") + public HashMap getCache() { + try { + return (HashMap)m_cache.get(m_pool); + } catch (Exception ex) { + throw new Error(ex); } } - @SuppressWarnings( { "rawtypes", "unchecked" } ) - public void changeMemberrefNameAndType( int memberrefIndex, String newName, String newType ) - { + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) { // NOTE: when changing values, we always need to copy-on-write - try - { + try { // get the memberref item - Object item = getItem( memberrefIndex ).getItem(); + Object item = getItem(memberrefIndex).getItem(); // update the cache HashMap cache = getCache(); - if( cache != null ) - { - cache.remove( item ); + if (cache != null) { + cache.remove(item); } - new MemberRefInfoAccessor( item ).setNameAndTypeIndex( m_pool.addNameAndTypeInfo( newName, newType ) ); + new MemberRefInfoAccessor(item).setNameAndTypeIndex(m_pool.addNameAndTypeInfo(newName, newType)); // update the cache - if( cache != null ) - { - cache.put( item, item ); + if (cache != null) { + cache.put(item, item); } - } - catch( Exception ex ) - { - throw new Error( ex ); + } catch (Exception ex) { + throw new Error(ex); } // make sure the change worked - assert( newName.equals( getMemberrefName( memberrefIndex ) ) ); - assert( newType.equals( getMemberrefType( memberrefIndex ) ) ); + assert (newName.equals(getMemberrefName(memberrefIndex))); + assert (newType.equals(getMemberrefType(memberrefIndex))); } - @SuppressWarnings( { "rawtypes", "unchecked" } ) - public void changeClassName( int classNameIndex, String newName ) - { + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void changeClassName(int classNameIndex, String newName) { // NOTE: when changing values, we always need to copy-on-write - try - { + try { // get the class item - Object item = getItem( classNameIndex ).getItem(); + Object item = getItem(classNameIndex).getItem(); // update the cache HashMap cache = getCache(); - if( cache != null ) - { - cache.remove( item ); + if (cache != null) { + cache.remove(item); } // add the new name and repoint the name-and-type to it - new ClassInfoAccessor( item ).setNameIndex( m_pool.addUtf8Info( newName ) ); + new ClassInfoAccessor(item).setNameIndex(m_pool.addUtf8Info(newName)); // update the cache - if( cache != null ) - { - cache.put( item, item ); + if (cache != null) { + cache.put(item, item); } - } - catch( Exception ex ) - { - throw new Error( ex ); + } catch (Exception ex) { + throw new Error(ex); } } - public static ConstPool newConstPool( ) - { + public static ConstPool newConstPool() { // const pool expects the name of a class to initialize itself // but we want an empty pool // so give it a bogus name, and then clear the entries afterwards - ConstPool pool = new ConstPool( "a" ); + ConstPool pool = new ConstPool("a"); - ConstPoolEditor editor = new ConstPoolEditor( pool ); + ConstPoolEditor editor = new ConstPoolEditor(pool); int size = pool.getSize(); - for( int i=0; i indices, ConstPoolEditor editor, ConstInfoAccessor entry ) - { - ClassInfoAccessor accessor = new ClassInfoAccessor( entry.getItem() ); - gatherIndexTree( indices, editor, accessor.getNameIndex() ); + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getNameIndex()); } @Override - public void remapIndices( Map map, ConstInfoAccessor entry ) - { - ClassInfoAccessor accessor = new ClassInfoAccessor( entry.getItem() ); - accessor.setNameIndex( remapIndex( map, accessor.getNameIndex() ) ); + public void remapIndices(Map map, ConstInfoAccessor entry) { + ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); + accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); } @Override - public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) - { - ClassInfoAccessor accessor = new ClassInfoAccessor( entry.getItem() ); - ConstInfoAccessor nameEntry = pool.getItem( accessor.getNameIndex() ); + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); + ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag(); } }, - StringInfo( 8, 1 ) - { + StringInfo( 8, 1 ) { + @Override - public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) - { - StringInfoAccessor accessor = new StringInfoAccessor( entry.getItem() ); - gatherIndexTree( indices, editor, accessor.getStringIndex() ); + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getStringIndex()); } @Override - public void remapIndices( Map map, ConstInfoAccessor entry ) - { - StringInfoAccessor accessor = new StringInfoAccessor( entry.getItem() ); - accessor.setStringIndex( remapIndex( map, accessor.getStringIndex() ) ); + public void remapIndices(Map map, ConstInfoAccessor entry) { + StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); + accessor.setStringIndex(remapIndex(map, accessor.getStringIndex())); } @Override - public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) - { - StringInfoAccessor accessor = new StringInfoAccessor( entry.getItem() ); - ConstInfoAccessor stringEntry = pool.getItem( accessor.getStringIndex() ); + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); + ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex()); return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag(); } }, - FieldRefInfo( 9, 2 ) - { + FieldRefInfo( 9, 2 ) { + @Override - public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) - { - MemberRefInfoAccessor accessor = new MemberRefInfoAccessor( entry.getItem() ); - gatherIndexTree( indices, editor, accessor.getClassIndex() ); - gatherIndexTree( indices, editor, accessor.getNameAndTypeIndex() ); + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getClassIndex()); + gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); } @Override - public void remapIndices( Map map, ConstInfoAccessor entry ) - { - MemberRefInfoAccessor accessor = new MemberRefInfoAccessor( entry.getItem() ); - accessor.setClassIndex( remapIndex( map, accessor.getClassIndex() ) ); - accessor.setNameAndTypeIndex( remapIndex( map, accessor.getNameAndTypeIndex() ) ); + public void remapIndices(Map map, ConstInfoAccessor entry) { + MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); + accessor.setClassIndex(remapIndex(map, accessor.getClassIndex())); + accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); } - + @Override - public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) - { - MemberRefInfoAccessor accessor = new MemberRefInfoAccessor( entry.getItem() ); - ConstInfoAccessor classEntry = pool.getItem( accessor.getClassIndex() ); - ConstInfoAccessor nameAndTypeEntry = pool.getItem( accessor.getNameAndTypeIndex() ); - return classEntry != null && classEntry.getTag() == ClassInfo.getTag() - && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); + ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex()); + ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); + return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); } }, - MethodRefInfo( 10, 2 ) // same as FieldRefInfo - { + // same as FieldRefInfo + MethodRefInfo( 10, 2 ) { + @Override - public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) - { - FieldRefInfo.gatherIndexTree( indices, editor, entry ); + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + FieldRefInfo.gatherIndexTree(indices, editor, entry); } @Override - public void remapIndices( Map map, ConstInfoAccessor entry ) - { - FieldRefInfo.remapIndices( map, entry ); + public void remapIndices(Map map, ConstInfoAccessor entry) { + FieldRefInfo.remapIndices(map, entry); } - + @Override - public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) - { - return FieldRefInfo.subIndicesAreValid( entry, pool ); + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + return FieldRefInfo.subIndicesAreValid(entry, pool); } }, - InterfaceMethodRefInfo( 11, 2 ) // same as FieldRefInfo - { + // same as FieldRefInfo + InterfaceMethodRefInfo( 11, 2 ) { + @Override - public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) - { - FieldRefInfo.gatherIndexTree( indices, editor, entry ); + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + FieldRefInfo.gatherIndexTree(indices, editor, entry); } @Override - public void remapIndices( Map map, ConstInfoAccessor entry ) - { - FieldRefInfo.remapIndices( map, entry ); + public void remapIndices(Map map, ConstInfoAccessor entry) { + FieldRefInfo.remapIndices(map, entry); } - + @Override - public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) - { - return FieldRefInfo.subIndicesAreValid( entry, pool ); + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + return FieldRefInfo.subIndicesAreValid(entry, pool); } }, - NameAndTypeInfo( 12, 1 ) - { + NameAndTypeInfo( 12, 1 ) { + @Override - public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) - { - NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor( entry.getItem() ); - gatherIndexTree( indices, editor, accessor.getNameIndex() ); - gatherIndexTree( indices, editor, accessor.getTypeIndex() ); + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getNameIndex()); + gatherIndexTree(indices, editor, accessor.getTypeIndex()); } @Override - public void remapIndices( Map map, ConstInfoAccessor entry ) - { - NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor( entry.getItem() ); - accessor.setNameIndex( remapIndex( map, accessor.getNameIndex() ) ); - accessor.setTypeIndex( remapIndex( map, accessor.getTypeIndex() ) ); + public void remapIndices(Map map, ConstInfoAccessor entry) { + NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); + accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); + accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); } @Override - public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) - { - NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor( entry.getItem() ); - ConstInfoAccessor nameEntry = pool.getItem( accessor.getNameIndex() ); - ConstInfoAccessor typeEntry = pool.getItem( accessor.getTypeIndex() ); - return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() - && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); + ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); + ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); + return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); } }, - MethodHandleInfo( 15, 3 ) - { + MethodHandleInfo( 15, 3 ) { + @Override - public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) - { - MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor( entry.getItem() ); - gatherIndexTree( indices, editor, accessor.getTypeIndex() ); - gatherIndexTree( indices, editor, accessor.getMethodRefIndex() ); + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getTypeIndex()); + gatherIndexTree(indices, editor, accessor.getMethodRefIndex()); } @Override - public void remapIndices( Map map, ConstInfoAccessor entry ) - { - MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor( entry.getItem() ); - accessor.setTypeIndex( remapIndex( map, accessor.getTypeIndex() ) ); - accessor.setMethodRefIndex( remapIndex( map, accessor.getMethodRefIndex() ) ); + public void remapIndices(Map map, ConstInfoAccessor entry) { + MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); + accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); + accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex())); } - + @Override - public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) - { - MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor( entry.getItem() ); - ConstInfoAccessor typeEntry = pool.getItem( accessor.getTypeIndex() ); - ConstInfoAccessor methodRefEntry = pool.getItem( accessor.getMethodRefIndex() ); - return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() - && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag(); + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); + ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); + ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex()); + return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag(); } }, - MethodTypeInfo( 16, 1 ) - { + MethodTypeInfo( 16, 1 ) { + @Override - public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) - { - MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor( entry.getItem() ); - gatherIndexTree( indices, editor, accessor.getTypeIndex() ); + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getTypeIndex()); } @Override - public void remapIndices( Map map, ConstInfoAccessor entry ) - { - MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor( entry.getItem() ); - accessor.setTypeIndex( remapIndex( map, accessor.getTypeIndex() ) ); + public void remapIndices(Map map, ConstInfoAccessor entry) { + MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); + accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); } - + @Override - public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) - { - MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor( entry.getItem() ); - ConstInfoAccessor typeEntry = pool.getItem( accessor.getTypeIndex() ); + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); + ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); } }, - InvokeDynamicInfo( 18, 2 ) - { + InvokeDynamicInfo( 18, 2 ) { + @Override - public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) - { - InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor( entry.getItem() ); - gatherIndexTree( indices, editor, accessor.getBootstrapIndex() ); - gatherIndexTree( indices, editor, accessor.getNameAndTypeIndex() ); + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getBootstrapIndex()); + gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); } @Override - public void remapIndices( Map map, ConstInfoAccessor entry ) - { - InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor( entry.getItem() ); - accessor.setBootstrapIndex( remapIndex( map, accessor.getBootstrapIndex() ) ); - accessor.setNameAndTypeIndex( remapIndex( map, accessor.getNameAndTypeIndex() ) ); + public void remapIndices(Map map, ConstInfoAccessor entry) { + InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); + accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex())); + accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); } - + @Override - public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) - { - InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor( entry.getItem() ); - ConstInfoAccessor bootstrapEntry = pool.getItem( accessor.getBootstrapIndex() ); - ConstInfoAccessor nameAndTypeEntry = pool.getItem( accessor.getNameAndTypeIndex() ); - return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() - && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); + ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex()); + ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); + return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); } }; private static Map m_types; - static - { + static { m_types = Maps.newTreeMap(); - for( InfoType type : values() ) - { - m_types.put( type.getTag(), type ); + for (InfoType type : values()) { + m_types.put(type.getTag(), type); } } private int m_tag; private int m_level; - private InfoType( int tag, int level ) - { + private InfoType(int tag, int level) { m_tag = tag; m_level = level; } - public int getTag( ) - { + public int getTag() { return m_tag; } - public int getLevel( ) - { + public int getLevel() { return m_level; } - public void gatherIndexTree( Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry ) - { + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { // by default, do nothing } - public void remapIndices( Map map, ConstInfoAccessor entry ) - { + public void remapIndices(Map map, ConstInfoAccessor entry) { // by default, do nothing } - public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool ) - { + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { // by default, everything is good return true; } - public boolean selfIndexIsValid( ConstInfoAccessor entry, ConstPoolEditor pool ) - { - ConstInfoAccessor entryCheck = pool.getItem( entry.getIndex() ); - if( entryCheck == null ) - { + public boolean selfIndexIsValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + ConstInfoAccessor entryCheck = pool.getItem(entry.getIndex()); + if (entryCheck == null) { return false; } - return entryCheck.getItem().equals( entry.getItem() ); + return entryCheck.getItem().equals(entry.getItem()); } - public static InfoType getByTag( int tag ) - { - return m_types.get( tag ); + public static InfoType getByTag(int tag) { + return m_types.get(tag); } - public static List getByLevel( int level ) - { + public static List getByLevel(int level) { List types = Lists.newArrayList(); - for( InfoType type : values() ) - { - if( type.getLevel() == level ) - { - types.add( type ); + for (InfoType type : values()) { + if (type.getLevel() == level) { + types.add(type); } } return types; } - public static List getSortedByLevel( ) - { + public static List getSortedByLevel() { List types = Lists.newArrayList(); - types.addAll( getByLevel( 0 ) ); - types.addAll( getByLevel( 1 ) ); - types.addAll( getByLevel( 2 ) ); - types.addAll( getByLevel( 3 ) ); + types.addAll(getByLevel(0)); + types.addAll(getByLevel(1)); + types.addAll(getByLevel(2)); + types.addAll(getByLevel(3)); return types; } - public static void gatherIndexTree( Collection indices, ConstPoolEditor editor, int index ) - { + public static void gatherIndexTree(Collection indices, ConstPoolEditor editor, int index) { // add own index - indices.add( index ); + indices.add(index); // recurse - ConstInfoAccessor entry = editor.getItem( index ); - entry.getType().gatherIndexTree( indices, editor, entry ); + ConstInfoAccessor entry = editor.getItem(index); + entry.getType().gatherIndexTree(indices, editor, entry); } - private static int remapIndex( Map map, int index ) - { - Integer newIndex = map.get( index ); - if( newIndex == null ) - { + private static int remapIndex(Map map, int index) { + Integer newIndex = map.get(index); + if (newIndex == null) { newIndex = index; } return newIndex; diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 5e593078..f52c31aa 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -23,105 +23,80 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; -public class InnerClassWriter -{ +public class InnerClassWriter { + private JarIndex m_jarIndex; - public InnerClassWriter( JarIndex jarIndex ) - { + public InnerClassWriter(JarIndex jarIndex) { m_jarIndex = jarIndex; } - public void write( CtClass c ) - { + public void write(CtClass c) { + // is this an inner or outer class? - String obfInnerClassName = new ClassEntry( Descriptor.toJvmName( c.getName() ) ).getSimpleName(); - String obfOuterClassName = m_jarIndex.getOuterClass( obfInnerClassName ); - if( obfOuterClassName == null ) - { + String obfInnerClassName = new ClassEntry(Descriptor.toJvmName(c.getName())).getSimpleName(); + String obfOuterClassName = m_jarIndex.getOuterClass(obfInnerClassName); + if (obfOuterClassName == null) { // this is an outer class - obfOuterClassName = Descriptor.toJvmName( c.getName() ); - } - else - { + obfOuterClassName = Descriptor.toJvmName(c.getName()); + } else { // this is an inner class, rename it to outer$inner - ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfInnerClassName ); - c.setName( obfClassEntry.getName() ); + ClassEntry obfClassEntry = new ClassEntry(obfOuterClassName + "$" + obfInnerClassName); + c.setName(obfClassEntry.getName()); - BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller( obfInnerClassName ); - if( caller != null ) - { + BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller(obfInnerClassName); + if (caller != null) { // write the enclosing method attribute - if( caller.getName().equals( "" ) ) - { - c.getClassFile().addAttribute( new EnclosingMethodAttribute( - c.getClassFile().getConstPool(), - caller.getClassName() - ) ); - } - else - { - c.getClassFile().addAttribute( new EnclosingMethodAttribute( - c.getClassFile().getConstPool(), - caller.getClassName(), - caller.getName(), - caller.getSignature() - ) ); + if (caller.getName().equals("")) { + c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); + } else { + c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature())); } } } // write the inner classes if needed - Collection obfInnerClassNames = m_jarIndex.getInnerClasses( obfOuterClassName ); - if( obfInnerClassNames != null && !obfInnerClassNames.isEmpty() ) - { - writeInnerClasses( c, obfOuterClassName, obfInnerClassNames ); + Collection obfInnerClassNames = m_jarIndex.getInnerClasses(obfOuterClassName); + if (obfInnerClassNames != null && !obfInnerClassNames.isEmpty()) { + writeInnerClasses(c, obfOuterClassName, obfInnerClassNames); } } - private void writeInnerClasses( CtClass c, String obfOuterClassName, Collection obfInnerClassNames ) - { - InnerClassesAttribute attr = new InnerClassesAttribute( c.getClassFile().getConstPool() ); - c.getClassFile().addAttribute( attr ); - for( String obfInnerClassName : obfInnerClassNames ) - { + private void writeInnerClasses(CtClass c, String obfOuterClassName, Collection obfInnerClassNames) { + InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); + c.getClassFile().addAttribute(attr); + for (String obfInnerClassName : obfInnerClassNames) { // get the new inner class name - ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfInnerClassName ); + ClassEntry obfClassEntry = new ClassEntry(obfOuterClassName + "$" + obfInnerClassName); // here's what the JVM spec says about the InnerClasses attribute - // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); + // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); // update the attribute with this inner class ConstPool constPool = c.getClassFile().getConstPool(); - int innerClassIndex = constPool.addClassInfo( obfClassEntry.getName() ); + int innerClassIndex = constPool.addClassInfo(obfClassEntry.getName()); int outerClassIndex = 0; int innerClassSimpleNameIndex = 0; - if( !m_jarIndex.isAnonymousClass( obfInnerClassName ) ) - { - outerClassIndex = constPool.addClassInfo( obfClassEntry.getOuterClassName() ); - innerClassSimpleNameIndex = constPool.addUtf8Info( obfClassEntry.getInnerClassName() ); + if (!m_jarIndex.isAnonymousClass(obfInnerClassName)) { + outerClassIndex = constPool.addClassInfo(obfClassEntry.getOuterClassName()); + innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getInnerClassName()); } - attr.append( - innerClassIndex, - outerClassIndex, - innerClassSimpleNameIndex, - c.getClassFile().getAccessFlags() & ~AccessFlag.SUPER - ); + attr.append(innerClassIndex, outerClassIndex, innerClassSimpleNameIndex, c.getClassFile().getAccessFlags() & ~AccessFlag.SUPER); - /* DEBUG - System.out.println( String.format( "\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", + /* DEBUG + System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", obfClassEntry, - attr.outerClass( attr.tableLength() - 1 ), - attr.innerClass( attr.tableLength() - 1 ), - attr.innerName( attr.tableLength() - 1 ), + attr.outerClass(attr.tableLength() - 1), + attr.innerClass(attr.tableLength() - 1), + attr.innerName(attr.tableLength() - 1), Constants.NonePackage + "/" + obfInnerClassName, obfClassEntry.getName() - ) ); + )); */ // make sure the outer class references only the new inner class names - c.replaceClassName( Constants.NonePackage + "/" + obfInnerClassName, obfClassEntry.getName() ); + c.replaceClassName(Constants.NonePackage + "/" + obfInnerClassName, obfClassEntry.getName()); } } } diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java index adea7eae..5a11cd89 100644 --- a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -25,51 +25,42 @@ import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; -public class MethodParameterWriter -{ +public class MethodParameterWriter { + private Translator m_translator; - public MethodParameterWriter( Translator translator ) - { + public MethodParameterWriter(Translator translator) { m_translator = translator; } - public void writeMethodArguments( CtClass c ) - { + public void writeMethodArguments(CtClass c) { + // Procyon will read method arguments from the "MethodParameters" attribute, so write those - ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); - for( CtBehavior behavior : c.getDeclaredBehaviors() ) - { - int numParams = Descriptor.numOfParameters( behavior.getMethodInfo().getDescriptor() ); - if( numParams <= 0 ) - { + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + int numParams = Descriptor.numOfParameters(behavior.getMethodInfo().getDescriptor()); + if (numParams <= 0) { continue; } // get the behavior entry BehaviorEntry behaviorEntry; - if( behavior instanceof CtMethod ) - { - behaviorEntry = new MethodEntry( classEntry, behavior.getMethodInfo().getName(), behavior.getSignature() ); - } - else if( behavior instanceof CtConstructor ) - { - behaviorEntry = new ConstructorEntry( classEntry, behavior.getSignature() ); - } - else - { - throw new Error( "Unsupported behavior type: " + behavior.getClass().getName() ); + if (behavior instanceof CtMethod) { + behaviorEntry = new MethodEntry(classEntry, behavior.getMethodInfo().getName(), behavior.getSignature()); + } else if (behavior instanceof CtConstructor) { + behaviorEntry = new ConstructorEntry(classEntry, behavior.getSignature()); + } else { + throw new Error("Unsupported behavior type: " + behavior.getClass().getName()); } // get the list of parameter names - List names = new ArrayList( numParams ); - for( int i=0; i names = new ArrayList(numParams); + for (int i = 0; i < numParams; i++) { + names.add(m_translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); } // save the mappings to the class - MethodParametersAttribute.updateClass( behavior.getMethodInfo(), names ); + MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); } } } diff --git a/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java index baf1ac1e..bf959564 100644 --- a/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java +++ b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java @@ -20,45 +20,38 @@ import javassist.bytecode.AttributeInfo; import javassist.bytecode.ConstPool; import javassist.bytecode.MethodInfo; -public class MethodParametersAttribute extends AttributeInfo -{ - private MethodParametersAttribute( ConstPool pool, List parameterNameIndices ) - { - super( pool, "MethodParameters", writeStruct( parameterNameIndices ) ); +public class MethodParametersAttribute extends AttributeInfo { + + private MethodParametersAttribute(ConstPool pool, List parameterNameIndices) { + super(pool, "MethodParameters", writeStruct(parameterNameIndices)); } - public static void updateClass( MethodInfo info, List names ) - { + public static void updateClass(MethodInfo info, List names) { // add the names to the class const pool ConstPool constPool = info.getConstPool(); List parameterNameIndices = new ArrayList(); - for( String name : names ) - { - if( name != null ) - { - parameterNameIndices.add( constPool.addUtf8Info( name ) ); - } - else - { - parameterNameIndices.add( 0 ); + for (String name : names) { + if (name != null) { + parameterNameIndices.add(constPool.addUtf8Info(name)); + } else { + parameterNameIndices.add(0); } } // add the attribute to the method - info.addAttribute( new MethodParametersAttribute( constPool, parameterNameIndices ) ); + info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices)); } - private static byte[] writeStruct( List parameterNameIndices ) - { + private static byte[] writeStruct(List parameterNameIndices) { // JVM 8 Spec says the struct looks like this: // http://cr.openjdk.java.net/~mr/se/8/java-se-8-fr-spec-01/java-se-8-jvms-fr-diffs.pdf // uint8 num_params // for each param: - // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry - // uint16 access_flags -> don't care, just set to 0 + // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry + // uint16 access_flags -> don't care, just set to 0 ByteArrayOutputStream buf = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream( buf ); + DataOutputStream out = new DataOutputStream(buf); // NOTE: java hates unsigned integers, so we have to be careful here // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument @@ -66,31 +59,27 @@ public class MethodParametersAttribute extends AttributeInfo // if the int is out of range, the byte stream won't look the way we want and weird things will happen final int SIZEOF_UINT8 = 1; final int SIZEOF_UINT16 = 2; - final int MAX_UINT8 = ( 1 << 8 ) - 1; - final int MAX_UINT16 = ( 1 << 16 ) - 1; + final int MAX_UINT8 = (1 << 8) - 1; + final int MAX_UINT16 = (1 << 16) - 1; - try - { - assert( parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8 ); - out.writeByte( parameterNameIndices.size() ); + try { + assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8); + out.writeByte(parameterNameIndices.size()); - for( Integer index : parameterNameIndices ) - { - assert( index >= 0 && index <= MAX_UINT16 ); - out.writeShort( index ); + for (Integer index : parameterNameIndices) { + assert (index >= 0 && index <= MAX_UINT16); + out.writeShort(index); // just write 0 for the access flags - out.writeShort( 0 ); + out.writeShort(0); } out.close(); byte[] data = buf.toByteArray(); - assert( data.length == SIZEOF_UINT8 + parameterNameIndices.size()*( SIZEOF_UINT16 + SIZEOF_UINT16 ) ); + assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16)); return data; - } - catch( IOException ex ) - { - throw new Error( ex ); + } catch (IOException ex) { + throw new Error(ex); } } } diff --git a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java index 41e1d047..d76f0567 100644 --- a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java @@ -12,58 +12,44 @@ package cuchaz.enigma.bytecode.accessors; import java.lang.reflect.Field; -public class ClassInfoAccessor -{ +public class ClassInfoAccessor { + private static Class m_class; private static Field m_nameIndex; - static - { - try - { - m_class = Class.forName( "javassist.bytecode.ClassInfo" ); - m_nameIndex = m_class.getDeclaredField( "name" ); - m_nameIndex.setAccessible( true ); - } - catch( Exception ex ) - { - throw new Error( ex ); + static { + try { + m_class = Class.forName("javassist.bytecode.ClassInfo"); + m_nameIndex = m_class.getDeclaredField("name"); + m_nameIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); } } - public static boolean isType( ConstInfoAccessor accessor ) - { - return m_class.isAssignableFrom( accessor.getItem().getClass() ); + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); } private Object m_item; - public ClassInfoAccessor( Object item ) - { + public ClassInfoAccessor(Object item) { m_item = item; } - public int getNameIndex( ) - { - try - { - return (Integer)m_nameIndex.get( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getNameIndex() { + try { + return (Integer)m_nameIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public void setNameIndex( int val ) - { - try - { - m_nameIndex.set( m_item, val ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void setNameIndex(int val) { + try { + m_nameIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); } } } diff --git a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java index 3c3d3fa4..d00c1021 100644 --- a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java @@ -22,44 +22,35 @@ import java.lang.reflect.Method; import cuchaz.enigma.bytecode.InfoType; -public class ConstInfoAccessor -{ +public class ConstInfoAccessor { + private static Class m_class; private static Field m_index; private static Method m_getTag; - static - { - try - { - m_class = Class.forName( "javassist.bytecode.ConstInfo" ); - m_index = m_class.getDeclaredField( "index" ); - m_index.setAccessible( true ); - m_getTag = m_class.getMethod( "getTag" ); - m_getTag.setAccessible( true ); - } - catch( Exception ex ) - { - throw new Error( ex ); + static { + try { + m_class = Class.forName("javassist.bytecode.ConstInfo"); + m_index = m_class.getDeclaredField("index"); + m_index.setAccessible(true); + m_getTag = m_class.getMethod("getTag"); + m_getTag.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); } } private Object m_item; - public ConstInfoAccessor( Object item ) - { - if( item == null ) - { - throw new IllegalArgumentException( "item cannot be null!" ); + public ConstInfoAccessor(Object item) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null!"); } m_item = item; } - public ConstInfoAccessor( DataInputStream in ) - throws IOException - { - try - { + public ConstInfoAccessor(DataInputStream in) throws IOException { + try { // read the entry String className = in.readUTF(); int oldIndex = in.readInt(); @@ -68,132 +59,98 @@ public class ConstInfoAccessor // so we have to read it here in.readByte(); - Constructor constructor = Class.forName( className ).getConstructor( DataInputStream.class, int.class ); - constructor.setAccessible( true ); - m_item = constructor.newInstance( in, oldIndex ); - } - catch( IOException ex ) - { + Constructor constructor = Class.forName(className).getConstructor(DataInputStream.class, int.class); + constructor.setAccessible(true); + m_item = constructor.newInstance(in, oldIndex); + } catch (IOException ex) { throw ex; - } - catch( Exception ex ) - { - throw new Error( ex ); + } catch (Exception ex) { + throw new Error(ex); } } - public Object getItem( ) - { + public Object getItem() { return m_item; } - public int getIndex( ) - { - try - { - return (Integer)m_index.get( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getIndex() { + try { + return (Integer)m_index.get(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public void setIndex( int val ) - { - try - { - m_index.set( m_item, val ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void setIndex(int val) { + try { + m_index.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); } } - public int getTag( ) - { - try - { - return (Integer)m_getTag.invoke( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getTag() { + try { + return (Integer)m_getTag.invoke(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public ConstInfoAccessor copy( ) - { - return new ConstInfoAccessor( copyItem() ); + public ConstInfoAccessor copy() { + return new ConstInfoAccessor(copyItem()); } - public Object copyItem( ) - { + public Object copyItem() { // I don't know of a simpler way to copy one of these silly things... - try - { + try { // serialize the item ByteArrayOutputStream buf = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream( buf ); - write( out ); + DataOutputStream out = new DataOutputStream(buf); + write(out); // deserialize the item - DataInputStream in = new DataInputStream( new ByteArrayInputStream( buf.toByteArray() ) ); - Object item = new ConstInfoAccessor( in ).getItem(); + DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray())); + Object item = new ConstInfoAccessor(in).getItem(); in.close(); return item; - } - catch( Exception ex ) - { - throw new Error( ex ); + } catch (Exception ex) { + throw new Error(ex); } } - public void write( DataOutputStream out ) - throws IOException - { - try - { - out.writeUTF( m_item.getClass().getName() ); - out.writeInt( getIndex() ); + public void write(DataOutputStream out) throws IOException { + try { + out.writeUTF(m_item.getClass().getName()); + out.writeInt(getIndex()); - Method method = m_item.getClass().getMethod( "write", DataOutputStream.class ); - method.setAccessible( true ); - method.invoke( m_item, out ); - } - catch( IOException ex ) - { + Method method = m_item.getClass().getMethod("write", DataOutputStream.class); + method.setAccessible(true); + method.invoke(m_item, out); + } catch (IOException ex) { throw ex; - } - catch( Exception ex ) - { - throw new Error( ex ); + } catch (Exception ex) { + throw new Error(ex); } } @Override - public String toString( ) - { - try - { + public String toString() { + try { ByteArrayOutputStream buf = new ByteArrayOutputStream(); - PrintWriter out = new PrintWriter( buf ); - Method print = m_item.getClass().getMethod( "print", PrintWriter.class ); - print.setAccessible( true ); - print.invoke( m_item, out ); + PrintWriter out = new PrintWriter(buf); + Method print = m_item.getClass().getMethod("print", PrintWriter.class); + print.setAccessible(true); + print.invoke(m_item, out); out.close(); - return buf.toString().replace( "\n", "" ); - } - catch( Exception ex ) - { - throw new Error( ex ); + return buf.toString().replace("\n", ""); + } catch (Exception ex) { + throw new Error(ex); } } - public InfoType getType( ) - { - return InfoType.getByTag( getTag() ); + public InfoType getType() { + return InfoType.getByTag(getTag()); } } diff --git a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java index 169306a4..0d780ea6 100644 --- a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java @@ -12,85 +12,63 @@ package cuchaz.enigma.bytecode.accessors; import java.lang.reflect.Field; -public class InvokeDynamicInfoAccessor -{ +public class InvokeDynamicInfoAccessor { + private static Class m_class; private static Field m_bootstrapIndex; private static Field m_nameAndTypeIndex; - static - { - try - { - m_class = Class.forName( "javassist.bytecode.InvokeDynamicInfo" ); - m_bootstrapIndex = m_class.getDeclaredField( "bootstrap" ); - m_bootstrapIndex.setAccessible( true ); - m_nameAndTypeIndex = m_class.getDeclaredField( "nameAndType" ); - m_nameAndTypeIndex.setAccessible( true ); - } - catch( Exception ex ) - { - throw new Error( ex ); + static { + try { + m_class = Class.forName("javassist.bytecode.InvokeDynamicInfo"); + m_bootstrapIndex = m_class.getDeclaredField("bootstrap"); + m_bootstrapIndex.setAccessible(true); + m_nameAndTypeIndex = m_class.getDeclaredField("nameAndType"); + m_nameAndTypeIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); } } - public static boolean isType( ConstInfoAccessor accessor ) - { - return m_class.isAssignableFrom( accessor.getItem().getClass() ); + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); } private Object m_item; - public InvokeDynamicInfoAccessor( Object item ) - { + public InvokeDynamicInfoAccessor(Object item) { m_item = item; } - public int getBootstrapIndex( ) - { - try - { - return (Integer)m_bootstrapIndex.get( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getBootstrapIndex() { + try { + return (Integer)m_bootstrapIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public void setBootstrapIndex( int val ) - { - try - { - m_bootstrapIndex.set( m_item, val ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void setBootstrapIndex(int val) { + try { + m_bootstrapIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); } } - public int getNameAndTypeIndex( ) - { - try - { - return (Integer)m_nameAndTypeIndex.get( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getNameAndTypeIndex() { + try { + return (Integer)m_nameAndTypeIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public void setNameAndTypeIndex( int val ) - { - try - { - m_nameAndTypeIndex.set( m_item, val ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void setNameAndTypeIndex(int val) { + try { + m_nameAndTypeIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); } } } diff --git a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java index 2ee3aff8..9fe945f7 100644 --- a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java @@ -12,85 +12,63 @@ package cuchaz.enigma.bytecode.accessors; import java.lang.reflect.Field; -public class MemberRefInfoAccessor -{ +public class MemberRefInfoAccessor { + private static Class m_class; private static Field m_classIndex; private static Field m_nameAndTypeIndex; - static - { - try - { - m_class = Class.forName( "javassist.bytecode.MemberrefInfo" ); - m_classIndex = m_class.getDeclaredField( "classIndex" ); - m_classIndex.setAccessible( true ); - m_nameAndTypeIndex = m_class.getDeclaredField( "nameAndTypeIndex" ); - m_nameAndTypeIndex.setAccessible( true ); - } - catch( Exception ex ) - { - throw new Error( ex ); + static { + try { + m_class = Class.forName("javassist.bytecode.MemberrefInfo"); + m_classIndex = m_class.getDeclaredField("classIndex"); + m_classIndex.setAccessible(true); + m_nameAndTypeIndex = m_class.getDeclaredField("nameAndTypeIndex"); + m_nameAndTypeIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); } } - public static boolean isType( ConstInfoAccessor accessor ) - { - return m_class.isAssignableFrom( accessor.getItem().getClass() ); + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); } private Object m_item; - public MemberRefInfoAccessor( Object item ) - { + public MemberRefInfoAccessor(Object item) { m_item = item; } - public int getClassIndex( ) - { - try - { - return (Integer)m_classIndex.get( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getClassIndex() { + try { + return (Integer)m_classIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public void setClassIndex( int val ) - { - try - { - m_classIndex.set( m_item, val ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void setClassIndex(int val) { + try { + m_classIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); } } - public int getNameAndTypeIndex( ) - { - try - { - return (Integer)m_nameAndTypeIndex.get( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getNameAndTypeIndex() { + try { + return (Integer)m_nameAndTypeIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public void setNameAndTypeIndex( int val ) - { - try - { - m_nameAndTypeIndex.set( m_item, val ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void setNameAndTypeIndex(int val) { + try { + m_nameAndTypeIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); } } } diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java index 27b7aee8..4c95b226 100644 --- a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java @@ -12,85 +12,63 @@ package cuchaz.enigma.bytecode.accessors; import java.lang.reflect.Field; -public class MethodHandleInfoAccessor -{ +public class MethodHandleInfoAccessor { + private static Class m_class; private static Field m_kindIndex; private static Field m_indexIndex; - static - { - try - { - m_class = Class.forName( "javassist.bytecode.MethodHandleInfo" ); - m_kindIndex = m_class.getDeclaredField( "refKind" ); - m_kindIndex.setAccessible( true ); - m_indexIndex = m_class.getDeclaredField( "refIndex" ); - m_indexIndex.setAccessible( true ); - } - catch( Exception ex ) - { - throw new Error( ex ); + static { + try { + m_class = Class.forName("javassist.bytecode.MethodHandleInfo"); + m_kindIndex = m_class.getDeclaredField("refKind"); + m_kindIndex.setAccessible(true); + m_indexIndex = m_class.getDeclaredField("refIndex"); + m_indexIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); } } - public static boolean isType( ConstInfoAccessor accessor ) - { - return m_class.isAssignableFrom( accessor.getItem().getClass() ); + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); } private Object m_item; - public MethodHandleInfoAccessor( Object item ) - { + public MethodHandleInfoAccessor(Object item) { m_item = item; } - public int getTypeIndex( ) - { - try - { - return (Integer)m_kindIndex.get( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getTypeIndex() { + try { + return (Integer)m_kindIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public void setTypeIndex( int val ) - { - try - { - m_kindIndex.set( m_item, val ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void setTypeIndex(int val) { + try { + m_kindIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); } } - public int getMethodRefIndex( ) - { - try - { - return (Integer)m_indexIndex.get( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getMethodRefIndex() { + try { + return (Integer)m_indexIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public void setMethodRefIndex( int val ) - { - try - { - m_indexIndex.set( m_item, val ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void setMethodRefIndex(int val) { + try { + m_indexIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); } } } diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java index 4cba6a2a..e1511179 100644 --- a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java @@ -12,58 +12,44 @@ package cuchaz.enigma.bytecode.accessors; import java.lang.reflect.Field; -public class MethodTypeInfoAccessor -{ +public class MethodTypeInfoAccessor { + private static Class m_class; private static Field m_descriptorIndex; - static - { - try - { - m_class = Class.forName( "javassist.bytecode.MethodTypeInfo" ); - m_descriptorIndex = m_class.getDeclaredField( "descriptor" ); - m_descriptorIndex.setAccessible( true ); - } - catch( Exception ex ) - { - throw new Error( ex ); + static { + try { + m_class = Class.forName("javassist.bytecode.MethodTypeInfo"); + m_descriptorIndex = m_class.getDeclaredField("descriptor"); + m_descriptorIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); } } - public static boolean isType( ConstInfoAccessor accessor ) - { - return m_class.isAssignableFrom( accessor.getItem().getClass() ); + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); } private Object m_item; - public MethodTypeInfoAccessor( Object item ) - { + public MethodTypeInfoAccessor(Object item) { m_item = item; } - public int getTypeIndex( ) - { - try - { - return (Integer)m_descriptorIndex.get( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getTypeIndex() { + try { + return (Integer)m_descriptorIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public void setTypeIndex( int val ) - { - try - { - m_descriptorIndex.set( m_item, val ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void setTypeIndex(int val) { + try { + m_descriptorIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); } } } diff --git a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java index 03b4de3c..6e82f3e9 100644 --- a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java @@ -12,85 +12,63 @@ package cuchaz.enigma.bytecode.accessors; import java.lang.reflect.Field; -public class NameAndTypeInfoAccessor -{ +public class NameAndTypeInfoAccessor { + private static Class m_class; private static Field m_nameIndex; private static Field m_typeIndex; - static - { - try - { - m_class = Class.forName( "javassist.bytecode.NameAndTypeInfo" ); - m_nameIndex = m_class.getDeclaredField( "memberName" ); - m_nameIndex.setAccessible( true ); - m_typeIndex = m_class.getDeclaredField( "typeDescriptor" ); - m_typeIndex.setAccessible( true ); - } - catch( Exception ex ) - { - throw new Error( ex ); + static { + try { + m_class = Class.forName("javassist.bytecode.NameAndTypeInfo"); + m_nameIndex = m_class.getDeclaredField("memberName"); + m_nameIndex.setAccessible(true); + m_typeIndex = m_class.getDeclaredField("typeDescriptor"); + m_typeIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); } } - public static boolean isType( ConstInfoAccessor accessor ) - { - return m_class.isAssignableFrom( accessor.getItem().getClass() ); + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); } private Object m_item; - public NameAndTypeInfoAccessor( Object item ) - { + public NameAndTypeInfoAccessor(Object item) { m_item = item; } - public int getNameIndex( ) - { - try - { - return (Integer)m_nameIndex.get( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getNameIndex() { + try { + return (Integer)m_nameIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public void setNameIndex( int val ) - { - try - { - m_nameIndex.set( m_item, val ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void setNameIndex(int val) { + try { + m_nameIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); } } - public int getTypeIndex( ) - { - try - { - return (Integer)m_typeIndex.get( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getTypeIndex() { + try { + return (Integer)m_typeIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public void setTypeIndex( int val ) - { - try - { - m_typeIndex.set( m_item, val ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void setTypeIndex(int val) { + try { + m_typeIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); } } } diff --git a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java index 5cdfce4d..6665ffe4 100644 --- a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java @@ -12,58 +12,44 @@ package cuchaz.enigma.bytecode.accessors; import java.lang.reflect.Field; -public class StringInfoAccessor -{ +public class StringInfoAccessor { + private static Class m_class; private static Field m_stringIndex; - static - { - try - { - m_class = Class.forName( "javassist.bytecode.StringInfo" ); - m_stringIndex = m_class.getDeclaredField( "string" ); - m_stringIndex.setAccessible( true ); - } - catch( Exception ex ) - { - throw new Error( ex ); + static { + try { + m_class = Class.forName("javassist.bytecode.StringInfo"); + m_stringIndex = m_class.getDeclaredField("string"); + m_stringIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); } } - public static boolean isType( ConstInfoAccessor accessor ) - { - return m_class.isAssignableFrom( accessor.getItem().getClass() ); + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); } private Object m_item; - public StringInfoAccessor( Object item ) - { + public StringInfoAccessor(Object item) { m_item = item; } - public int getStringIndex( ) - { - try - { - return (Integer)m_stringIndex.get( m_item ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public int getStringIndex() { + try { + return (Integer)m_stringIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); } } - public void setStringIndex( int val ) - { - try - { - m_stringIndex.set( m_item, val ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void setStringIndex(int val) { + try { + m_stringIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); } } } diff --git a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java index 1cadd836..2abf60b4 100644 --- a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java @@ -10,24 +10,19 @@ ******************************************************************************/ package cuchaz.enigma.bytecode.accessors; -public class Utf8InfoAccessor -{ +public class Utf8InfoAccessor { + private static Class m_class; - static - { - try - { - m_class = Class.forName( "javassist.bytecode.Utf8Info" ); - } - catch( Exception ex ) - { - throw new Error( ex ); + static { + try { + m_class = Class.forName("javassist.bytecode.Utf8Info"); + } catch (Exception ex) { + throw new Error(ex); } } - public static boolean isType( ConstInfoAccessor accessor ) - { - return m_class.isAssignableFrom( accessor.getItem().getClass() ); + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); } } diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index 1de345ff..73404038 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -57,8 +57,8 @@ import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.SignatureUpdater; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; -public class ClassIdentity -{ +public class ClassIdentity { + private ClassEntry m_classEntry; private SidedClassNamer m_namer; private Multiset m_fields; @@ -70,419 +70,339 @@ public class ClassIdentity private Multiset m_implementations; private Multiset m_references; - public ClassIdentity( CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences ) - { + public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) { m_namer = namer; // stuff from the bytecode - m_classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); + m_classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); m_fields = HashMultiset.create(); - for( CtField field : c.getDeclaredFields() ) - { - m_fields.add( scrubSignature( field.getSignature() ) ); + for (CtField field : c.getDeclaredFields()) { + m_fields.add(scrubSignature(field.getSignature())); } m_methods = HashMultiset.create(); - for( CtMethod method : c.getDeclaredMethods() ) - { - m_methods.add( scrubSignature( method.getSignature() ) + "0x" + getBehaviorSignature( method ) ); + for (CtMethod method : c.getDeclaredMethods()) { + m_methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method)); } m_constructors = HashMultiset.create(); - for( CtConstructor constructor : c.getDeclaredConstructors() ) - { - m_constructors.add( scrubSignature( constructor.getSignature() ) + "0x" + getBehaviorSignature( constructor ) ); + for (CtConstructor constructor : c.getDeclaredConstructors()) { + m_constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor)); } m_staticInitializer = ""; - if( c.getClassInitializer() != null ) - { - m_staticInitializer = getBehaviorSignature( c.getClassInitializer() ); + if (c.getClassInitializer() != null) { + m_staticInitializer = getBehaviorSignature(c.getClassInitializer()); } m_extends = ""; - if( c.getClassFile().getSuperclass() != null ) - { - m_extends = scrubClassName( c.getClassFile().getSuperclass() ); + if (c.getClassFile().getSuperclass() != null) { + m_extends = scrubClassName(c.getClassFile().getSuperclass()); } m_implements = HashMultiset.create(); - for( String interfaceName : c.getClassFile().getInterfaces() ) - { - m_implements.add( scrubClassName( interfaceName ) ); + for (String interfaceName : c.getClassFile().getInterfaces()) { + m_implements.add(scrubClassName(interfaceName)); } // stuff from the jar index m_implementations = HashMultiset.create(); - ClassImplementationsTreeNode implementationsNode = index.getClassImplementations( null, m_classEntry ); - if( implementationsNode != null ) - { - @SuppressWarnings( "unchecked" ) + ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, m_classEntry); + if (implementationsNode != null) { + @SuppressWarnings("unchecked") Enumeration implementations = implementationsNode.children(); - while( implementations.hasMoreElements() ) - { + while (implementations.hasMoreElements()) { ClassImplementationsTreeNode node = implementations.nextElement(); - m_implementations.add( scrubClassName( node.getClassEntry().getName() ) ); + m_implementations.add(scrubClassName(node.getClassEntry().getName())); } } m_references = HashMultiset.create(); - if( useReferences ) - { - for( CtField field : c.getDeclaredFields() ) - { - FieldEntry fieldEntry = new FieldEntry( m_classEntry, field.getName() ); - for( EntryReference reference : index.getFieldReferences( fieldEntry ) ) - { - addReference( reference ); + if (useReferences) { + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = new FieldEntry(m_classEntry, field.getName()); + for (EntryReference reference : index.getFieldReferences(fieldEntry)) { + addReference(reference); } } - for( CtMethod method : c.getDeclaredMethods() ) - { - MethodEntry methodEntry = new MethodEntry( m_classEntry, method.getName(), method.getSignature() ); - for( EntryReference reference : index.getBehaviorReferences( methodEntry ) ) - { - addReference( reference ); + for (CtMethod method : c.getDeclaredMethods()) { + MethodEntry methodEntry = new MethodEntry(m_classEntry, method.getName(), method.getSignature()); + for (EntryReference reference : index.getBehaviorReferences(methodEntry)) { + addReference(reference); } } - for( CtConstructor constructor : c.getDeclaredConstructors() ) - { - ConstructorEntry constructorEntry = new ConstructorEntry( m_classEntry, constructor.getSignature() ); - for( EntryReference reference : index.getBehaviorReferences( constructorEntry ) ) - { - addReference( reference ); + for (CtConstructor constructor : c.getDeclaredConstructors()) { + ConstructorEntry constructorEntry = new ConstructorEntry(m_classEntry, constructor.getSignature()); + for (EntryReference reference : index.getBehaviorReferences(constructorEntry)) { + addReference(reference); } } } } - private void addReference( EntryReference reference ) - { - if( reference.context.getSignature() != null ) - { - m_references.add( String.format( "%s_%s", - scrubClassName( reference.context.getClassName() ), - scrubSignature( reference.context.getSignature() ) - ) ); - } - else - { - m_references.add( String.format( "%s_", - scrubClassName( reference.context.getClassName() ) - ) ); + private void addReference(EntryReference reference) { + if (reference.context.getSignature() != null) { + m_references.add(String.format("%s_%s", scrubClassName(reference.context.getClassName()), scrubSignature(reference.context.getSignature()))); + } else { + m_references.add(String.format("%s_", scrubClassName(reference.context.getClassName()))); } } - - public ClassEntry getClassEntry( ) - { + + public ClassEntry getClassEntry() { return m_classEntry; } @Override - public String toString( ) - { + public String toString() { StringBuilder buf = new StringBuilder(); - buf.append( "class: " ); - buf.append( m_classEntry.getName() ); - buf.append( " " ); - buf.append( hashCode() ); - buf.append( "\n" ); - for( String field : m_fields ) - { - buf.append( "\tfield " ); - buf.append( field ); - buf.append( "\n" ); + buf.append("class: "); + buf.append(m_classEntry.getName()); + buf.append(" "); + buf.append(hashCode()); + buf.append("\n"); + for (String field : m_fields) { + buf.append("\tfield "); + buf.append(field); + buf.append("\n"); } - for( String method : m_methods ) - { - buf.append( "\tmethod " ); - buf.append( method ); - buf.append( "\n" ); + for (String method : m_methods) { + buf.append("\tmethod "); + buf.append(method); + buf.append("\n"); } - for( String constructor : m_constructors ) - { - buf.append( "\tconstructor " ); - buf.append( constructor ); - buf.append( "\n" ); + for (String constructor : m_constructors) { + buf.append("\tconstructor "); + buf.append(constructor); + buf.append("\n"); } - if( m_staticInitializer.length() > 0 ) - { - buf.append( "\tinitializer " ); - buf.append( m_staticInitializer ); - buf.append( "\n" ); + if (m_staticInitializer.length() > 0) { + buf.append("\tinitializer "); + buf.append(m_staticInitializer); + buf.append("\n"); } - if( m_extends.length() > 0 ) - { - buf.append( "\textends " ); - buf.append( m_extends ); - buf.append( "\n" ); + if (m_extends.length() > 0) { + buf.append("\textends "); + buf.append(m_extends); + buf.append("\n"); } - for( String interfaceName : m_implements ) - { - buf.append( "\timplements " ); - buf.append( interfaceName ); - buf.append( "\n" ); + for (String interfaceName : m_implements) { + buf.append("\timplements "); + buf.append(interfaceName); + buf.append("\n"); } - for( String implementation : m_implementations ) - { - buf.append( "\timplemented by " ); - buf.append( implementation ); - buf.append( "\n" ); + for (String implementation : m_implementations) { + buf.append("\timplemented by "); + buf.append(implementation); + buf.append("\n"); } - for( String reference : m_references ) - { - buf.append( "\treference " ); - buf.append( reference ); - buf.append( "\n" ); + for (String reference : m_references) { + buf.append("\treference "); + buf.append(reference); + buf.append("\n"); } return buf.toString(); } - private String scrubClassName( String className ) - { - return scrubSignature( "L" + Descriptor.toJvmName( className ) + ";" ); + private String scrubClassName(String className) { + return scrubSignature("L" + Descriptor.toJvmName(className) + ";"); } - private String scrubSignature( String signature ) - { - return SignatureUpdater.update( signature, new ClassNameUpdater( ) - { + private String scrubSignature(String signature) { + return SignatureUpdater.update(signature, new ClassNameUpdater() { private Map m_classNames = Maps.newHashMap(); @Override - public String update( String className ) - { + public String update(String className) { // classes not in the none package can be passed through - ClassEntry classEntry = new ClassEntry( className ); - if( !classEntry.getPackageName().equals( Constants.NonePackage ) ) - { + ClassEntry classEntry = new ClassEntry(className); + if (!classEntry.getPackageName().equals(Constants.NonePackage)) { return className; } // is this class ourself? - if( className.equals( m_classEntry.getName() ) ) - { + if (className.equals(m_classEntry.getName())) { return "CSelf"; } // try the namer - if( m_namer != null ) - { - String newName = m_namer.getName( className ); - if( newName != null ) - { + if (m_namer != null) { + String newName = m_namer.getName(className); + if (newName != null) { return newName; } } // otherwise, use local naming - if( !m_classNames.containsKey( className ) ) - { - m_classNames.put( className, getNewClassName() ); + if (!m_classNames.containsKey(className)) { + m_classNames.put(className, getNewClassName()); } - return m_classNames.get( className ); + return m_classNames.get(className); } - - private String getNewClassName( ) - { - return String.format( "C%03d", m_classNames.size() ); + + private String getNewClassName() { + return String.format("C%03d", m_classNames.size()); } - } ); + }); } - private boolean isClassMatchedUniquely( String className ) - { - return m_namer != null && m_namer.getName( Descriptor.toJvmName( className ) ) != null; + private boolean isClassMatchedUniquely(String className) { + return m_namer != null && m_namer.getName(Descriptor.toJvmName(className)) != null; } - private String getBehaviorSignature( CtBehavior behavior ) - { - try - { + private String getBehaviorSignature(CtBehavior behavior) { + try { // does this method have an implementation? - if( behavior.getMethodInfo().getCodeAttribute() == null ) - { + if (behavior.getMethodInfo().getCodeAttribute() == null) { return "(none)"; } // compute the hash from the opcodes ConstPool constants = behavior.getMethodInfo().getConstPool(); - final MessageDigest digest = MessageDigest.getInstance( "MD5" ); + final MessageDigest digest = MessageDigest.getInstance("MD5"); CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); - while( iter.hasNext() ) - { + while (iter.hasNext()) { int pos = iter.next(); // update the hash with the opcode - int opcode = iter.byteAt( pos ); - digest.update( (byte)opcode ); + int opcode = iter.byteAt(pos); + digest.update((byte)opcode); - switch( opcode ) - { - case Opcode.LDC: - { - int constIndex = iter.byteAt( pos + 1 ); - updateHashWithConstant( digest, constants, constIndex ); + switch (opcode) { + case Opcode.LDC: { + int constIndex = iter.byteAt(pos + 1); + updateHashWithConstant(digest, constants, constIndex); } break; case Opcode.LDC_W: - case Opcode.LDC2_W: - { - int constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 ); - updateHashWithConstant( digest, constants, constIndex ); + case Opcode.LDC2_W: { + int constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2); + updateHashWithConstant(digest, constants, constIndex); } break; } } // update hash with method and field accesses - behavior.instrument( new ExprEditor( ) - { + behavior.instrument(new ExprEditor() { @Override - public void edit( MethodCall call ) - { - updateHashWithString( digest, scrubClassName( call.getClassName() ) ); - updateHashWithString( digest, scrubSignature( call.getSignature() ) ); - if( isClassMatchedUniquely( call.getClassName() ) ) - { - updateHashWithString( digest, call.getMethodName() ); + public void edit(MethodCall call) { + updateHashWithString(digest, scrubClassName(call.getClassName())); + updateHashWithString(digest, scrubSignature(call.getSignature())); + if (isClassMatchedUniquely(call.getClassName())) { + updateHashWithString(digest, call.getMethodName()); } } @Override - public void edit( FieldAccess access ) - { - updateHashWithString( digest, scrubClassName( access.getClassName() ) ); - updateHashWithString( digest, scrubSignature( access.getSignature() ) ); - if( isClassMatchedUniquely( access.getClassName() ) ) - { - updateHashWithString( digest, access.getFieldName() ); + public void edit(FieldAccess access) { + updateHashWithString(digest, scrubClassName(access.getClassName())); + updateHashWithString(digest, scrubSignature(access.getSignature())); + if (isClassMatchedUniquely(access.getClassName())) { + updateHashWithString(digest, access.getFieldName()); } } @Override - public void edit( ConstructorCall call ) - { - updateHashWithString( digest, scrubClassName( call.getClassName() ) ); - updateHashWithString( digest, scrubSignature( call.getSignature() ) ); + public void edit(ConstructorCall call) { + updateHashWithString(digest, scrubClassName(call.getClassName())); + updateHashWithString(digest, scrubSignature(call.getSignature())); } @Override - public void edit( NewExpr expr ) - { - updateHashWithString( digest, scrubClassName( expr.getClassName() ) ); + public void edit(NewExpr expr) { + updateHashWithString(digest, scrubClassName(expr.getClassName())); } - } ); + }); // convert the hash to a hex string - return toHex( digest.digest() ); - } - catch( BadBytecode | NoSuchAlgorithmException | CannotCompileException ex ) - { - throw new Error( ex ); + return toHex(digest.digest()); + } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) { + throw new Error(ex); } } - private void updateHashWithConstant( MessageDigest digest, ConstPool constants, int index ) - { - ConstPoolEditor editor = new ConstPoolEditor( constants ); - ConstInfoAccessor item = editor.getItem( index ); - if( item.getType() == InfoType.StringInfo ) - { - updateHashWithString( digest, constants.getStringInfo( index ) ); + private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) { + ConstPoolEditor editor = new ConstPoolEditor(constants); + ConstInfoAccessor item = editor.getItem(index); + if (item.getType() == InfoType.StringInfo) { + updateHashWithString(digest, constants.getStringInfo(index)); } // TODO: other constants } - private void updateHashWithString( MessageDigest digest, String val ) - { - try - { - digest.update( val.getBytes( "UTF8" ) ); - } - catch( UnsupportedEncodingException ex ) - { - throw new Error( ex ); + private void updateHashWithString(MessageDigest digest, String val) { + try { + digest.update(val.getBytes("UTF8")); + } catch (UnsupportedEncodingException ex) { + throw new Error(ex); } } - private String toHex( byte[] bytes ) - { + private String toHex(byte[] bytes) { // function taken from: // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java final char[] hexArray = "0123456789ABCDEF".toCharArray(); char[] hexChars = new char[bytes.length * 2]; - for( int j = 0; j < bytes.length; j++ ) - { + for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } - return new String( hexChars ); + return new String(hexChars); } - + @Override - public boolean equals( Object other ) - { - if( other instanceof ClassIdentity ) - { - return equals( (ClassIdentity)other ); + public boolean equals(Object other) { + if (other instanceof ClassIdentity) { + return equals((ClassIdentity)other); } return false; } - public boolean equals( ClassIdentity other ) - { - return m_fields.equals( other.m_fields ) - && m_methods.equals( other.m_methods ) - && m_constructors.equals( other.m_constructors ) - && m_staticInitializer.equals( other.m_staticInitializer ) - && m_extends.equals( other.m_extends ) - && m_implements.equals( other.m_implements ) - && m_implementations.equals( other.m_implementations ) - && m_references.equals( other.m_references ); + public boolean equals(ClassIdentity other) { + return m_fields.equals(other.m_fields) + && m_methods.equals(other.m_methods) + && m_constructors.equals(other.m_constructors) + && m_staticInitializer.equals(other.m_staticInitializer) + && m_extends.equals(other.m_extends) + && m_implements.equals(other.m_implements) + && m_implementations.equals(other.m_implementations) + && m_references.equals(other.m_references); } @Override - public int hashCode( ) - { + public int hashCode() { List objs = Lists.newArrayList(); - objs.addAll( m_fields ); - objs.addAll( m_methods ); - objs.addAll( m_constructors ); - objs.add( m_staticInitializer ); - objs.add( m_extends ); - objs.addAll( m_implements ); - objs.addAll( m_implementations ); - objs.addAll( m_references ); - return Util.combineHashesOrdered( objs ); + objs.addAll(m_fields); + objs.addAll(m_methods); + objs.addAll(m_constructors); + objs.add(m_staticInitializer); + objs.add(m_extends); + objs.addAll(m_implements); + objs.addAll(m_implementations); + objs.addAll(m_references); + return Util.combineHashesOrdered(objs); } - public int getMatchScore( ClassIdentity other ) - { - return getNumMatches( m_fields, other.m_fields ) - + getNumMatches( m_methods, other.m_methods ) - + getNumMatches( m_constructors, other.m_constructors ); + public int getMatchScore(ClassIdentity other) { + return getNumMatches(m_fields, other.m_fields) + + getNumMatches(m_methods, other.m_methods) + + getNumMatches(m_constructors, other.m_constructors); } - public int getMaxMatchScore( ) - { + public int getMaxMatchScore() { return m_fields.size() + m_methods.size() + m_constructors.size(); } - public boolean matches( CtClass c ) - { + public boolean matches(CtClass c) { // just compare declaration counts return m_fields.size() == c.getDeclaredFields().length && m_methods.size() == c.getDeclaredMethods().length && m_constructors.size() == c.getDeclaredConstructors().length; } - private int getNumMatches( Multiset a, Multiset b ) - { + private int getNumMatches(Multiset a, Multiset b) { int numMatches = 0; - for( String val : a ) - { - if( b.contains( val ) ) - { + for (String val : a) { + if (b.contains(val)) { numMatches++; } } diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index 290d90a7..fc39ed0c 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -49,102 +49,92 @@ import cuchaz.enigma.mapping.MappingsWriter; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.MethodMapping; -public class ClassMatcher -{ - public static void main( String[] args ) - throws IOException, MappingParseException - { +public class ClassMatcher { + + public static void main(String[] args) throws IOException, MappingParseException { // TEMP - JarFile sourceJar = new JarFile( new File( "input/1.8-pre3.jar" ) ); - JarFile destJar = new JarFile( new File( "input/1.8.jar" ) ); - File inMappingsFile = new File( "../Enigma Mappings/1.8-pre3.mappings" ); - File outMappingsFile = new File( "../Enigma Mappings/1.8.mappings" ); + JarFile sourceJar = new JarFile(new File("input/1.8-pre3.jar")); + JarFile destJar = new JarFile(new File("input/1.8.jar")); + File inMappingsFile = new File("../Enigma Mappings/1.8-pre3.mappings"); + File outMappingsFile = new File("../Enigma Mappings/1.8.mappings"); // define a matching to use when the automated system cannot find a match Map fallbackMatching = Maps.newHashMap(); - fallbackMatching.put( "none/ayb", "none/ayf" ); - fallbackMatching.put( "none/ayd", "none/ayd" ); - fallbackMatching.put( "none/bgk", "unknown/bgk" ); + fallbackMatching.put("none/ayb", "none/ayf"); + fallbackMatching.put("none/ayd", "none/ayd"); + fallbackMatching.put("none/bgk", "unknown/bgk"); // do the conversion - Mappings mappings = new MappingsReader().read( new FileReader( inMappingsFile ) ); - convertMappings( sourceJar, destJar, mappings, fallbackMatching ); + Mappings mappings = new MappingsReader().read(new FileReader(inMappingsFile)); + convertMappings(sourceJar, destJar, mappings, fallbackMatching); // write out the converted mappings - FileWriter writer = new FileWriter( outMappingsFile ); - new MappingsWriter().write( writer, mappings ); + FileWriter writer = new FileWriter(outMappingsFile); + new MappingsWriter().write(writer, mappings); writer.close(); - System.out.println( "Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath() ); + System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); } - private static void convertMappings( JarFile sourceJar, JarFile destJar, Mappings mappings, Map fallbackMatching ) - { + private static void convertMappings(JarFile sourceJar, JarFile destJar, Mappings mappings, Map fallbackMatching) { // index jars - System.out.println( "Indexing source jar..." ); + System.out.println("Indexing source jar..."); JarIndex sourceIndex = new JarIndex(); - sourceIndex.indexJar( sourceJar, false ); - System.out.println( "Indexing dest jar..." ); + sourceIndex.indexJar(sourceJar, false); + System.out.println("Indexing dest jar..."); JarIndex destIndex = new JarIndex(); - destIndex.indexJar( destJar, false ); - TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader( sourceJar, sourceIndex ); - TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex ); - + destIndex.indexJar(destJar, false); + TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader(sourceJar, sourceIndex); + TranslatingTypeLoader destLoader = new TranslatingTypeLoader(destJar, destIndex); + // compute the matching - ClassMatching matching = computeMatching( sourceIndex, sourceLoader, destIndex, destLoader ); + ClassMatching matching = computeMatching(sourceIndex, sourceLoader, destIndex, destLoader); Map>> matchingIndex = matching.getIndex(); // get all the obf class names used in the mappings Set usedClassNames = mappings.getAllObfClassNames(); Set allClassNames = Sets.newHashSet(); - for( ClassEntry classEntry : sourceIndex.getObfClassEntries() ) - { - allClassNames.add( classEntry.getName() ); + for (ClassEntry classEntry : sourceIndex.getObfClassEntries()) { + allClassNames.add(classEntry.getName()); } - usedClassNames.retainAll( allClassNames ); - System.out.println( "Used " + usedClassNames.size() + " classes in the mappings" ); + usedClassNames.retainAll(allClassNames); + System.out.println("Used " + usedClassNames.size() + " classes in the mappings"); // probabilistically match the non-uniquely-matched source classes - for( Map.Entry> entry : matchingIndex.values() ) - { + for (Map.Entry> entry : matchingIndex.values()) { ClassIdentity sourceClass = entry.getKey(); List destClasses = entry.getValue(); // skip classes that are uniquely matched - if( destClasses.size() == 1 ) - { + if (destClasses.size() == 1) { continue; } // skip classes that aren't used in the mappings - if( !usedClassNames.contains( sourceClass.getClassEntry().getName() ) ) - { + if (!usedClassNames.contains(sourceClass.getClassEntry().getName())) { continue; } - System.out.println( "No exact match for source class " + sourceClass.getClassEntry() ); + System.out.println("No exact match for source class " + sourceClass.getClassEntry()); // find the closest classes Multimap scoredMatches = ArrayListMultimap.create(); - for( ClassIdentity c : destClasses ) - { - scoredMatches.put( sourceClass.getMatchScore( c ), c ); + for (ClassIdentity c : destClasses) { + scoredMatches.put(sourceClass.getMatchScore(c), c); } - List scores = new ArrayList( scoredMatches.keySet() ); - Collections.sort( scores, Collections.reverseOrder() ); - printScoredMatches( sourceClass.getMaxMatchScore(), scores, scoredMatches ); + List scores = new ArrayList(scoredMatches.keySet()); + Collections.sort(scores, Collections.reverseOrder()); + printScoredMatches(sourceClass.getMaxMatchScore(), scores, scoredMatches); // does the best match have a non-zero score and the same name? - int bestScore = scores.get( 0 ); - Collection bestMatches = scoredMatches.get( bestScore ); - if( bestScore > 0 && bestMatches.size() == 1 ) - { + int bestScore = scores.get(0); + Collection bestMatches = scoredMatches.get(bestScore); + if (bestScore > 0 && bestMatches.size() == 1) { ClassIdentity bestMatch = bestMatches.iterator().next(); - if( bestMatch.getClassEntry().equals( sourceClass.getClassEntry() ) ) - { + if (bestMatch.getClassEntry().equals(sourceClass.getClassEntry())) { // use it - System.out.println( "\tAutomatically choosing likely match: " + bestMatch.getClassEntry().getName() ); + System.out.println("\tAutomatically choosing likely match: " + bestMatch.getClassEntry().getName()); destClasses.clear(); - destClasses.add( bestMatch ); + destClasses.add(bestMatch); } } } @@ -152,63 +142,46 @@ public class ClassMatcher // group the matching into unique and non-unique matches BiMap matchedClassNames = HashBiMap.create(); Set unmatchedSourceClassNames = Sets.newHashSet(); - for( String className : usedClassNames ) - { + for (String className : usedClassNames) { // is there a match for this class? - Map.Entry> entry = matchingIndex.get( className ); + Map.Entry> entry = matchingIndex.get(className); ClassIdentity sourceClass = entry.getKey(); List matches = entry.getValue(); - if( matches.size() == 1 ) - { + if (matches.size() == 1) { // unique match! We're good to go! - matchedClassNames.put( - sourceClass.getClassEntry().getName(), - matches.get( 0 ).getClassEntry().getName() - ); - } - else - { + matchedClassNames.put(sourceClass.getClassEntry().getName(), matches.get(0).getClassEntry().getName()); + } else { // no match, check the fallback matching - String fallbackMatch = fallbackMatching.get( className ); - if( fallbackMatch != null ) - { - matchedClassNames.put( - sourceClass.getClassEntry().getName(), - fallbackMatch - ); - } - else - { - unmatchedSourceClassNames.add( className ); + String fallbackMatch = fallbackMatching.get(className); + if (fallbackMatch != null) { + matchedClassNames.put(sourceClass.getClassEntry().getName(), fallbackMatch); + } else { + unmatchedSourceClassNames.add(className); } } } // report unmatched classes - if( !unmatchedSourceClassNames.isEmpty() ) - { - System.err.println( "ERROR: there were unmatched classes!" ); - for( String className : unmatchedSourceClassNames ) - { - System.err.println( "\t" + className ); + if (!unmatchedSourceClassNames.isEmpty()) { + System.err.println("ERROR: there were unmatched classes!"); + for (String className : unmatchedSourceClassNames) { + System.err.println("\t" + className); } return; } // get the class name changes from the matched class names Map classChanges = Maps.newHashMap(); - for( Map.Entry entry : matchedClassNames.entrySet() ) - { - if( !entry.getKey().equals( entry.getValue() ) ) - { - classChanges.put( entry.getKey(), entry.getValue() ); - System.out.println( String.format( "Class change: %s -> %s", entry.getKey(), entry.getValue() ) ); + for (Map.Entry entry : matchedClassNames.entrySet()) { + if (!entry.getKey().equals(entry.getValue())) { + classChanges.put(entry.getKey(), entry.getValue()); + System.out.println(String.format("Class change: %s -> %s", entry.getKey(), entry.getValue())); /* DEBUG - System.out.println( String.format( "\n%s\n%s", - new ClassIdentity( sourceLoader.loadClass( entry.getKey() ), null, sourceIndex, false, false ), - new ClassIdentity( destLoader.loadClass( entry.getValue() ), null, destIndex, false, false ) - ) ); + System.out.println(String.format("\n%s\n%s", + new ClassIdentity(sourceLoader.loadClass(entry.getKey()), null, sourceIndex, false, false), + new ClassIdentity( destLoader.loadClass(entry.getValue()), null, destIndex, false, false) + )); */ } } @@ -217,52 +190,42 @@ public class ClassMatcher // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b LinkedHashMap orderedClassChanges = Maps.newLinkedHashMap(); int numChangesLeft = classChanges.size(); - while( !classChanges.isEmpty() ) - { + while (!classChanges.isEmpty()) { Iterator> iter = classChanges.entrySet().iterator(); - while( iter.hasNext() ) - { + while (iter.hasNext()) { Map.Entry entry = iter.next(); - if( classChanges.get( entry.getValue() ) == null ) - { - orderedClassChanges.put( entry.getKey(), entry.getValue() ); + if (classChanges.get(entry.getValue()) == null) { + orderedClassChanges.put(entry.getKey(), entry.getValue()); iter.remove(); } } // did we remove any changes? - if( numChangesLeft - classChanges.size() > 0 ) - { + if (numChangesLeft - classChanges.size() > 0) { // keep going - numChangesLeft = classChanges.size(); - } - else - { + numChangesLeft = classChanges.size(); + } else { // can't sort anymore. There must be a loop break; } } - if( classChanges.size() > 0 ) - { - throw new Error( String.format( "Unable to sort %d/%d class changes!", classChanges.size(), matchedClassNames.size() ) ); + if (classChanges.size() > 0) { + throw new Error(String.format("Unable to sort %d/%d class changes!", classChanges.size(), matchedClassNames.size())); } // convert the mappings in the correct class order - for( Map.Entry entry : orderedClassChanges.entrySet() ) - { - mappings.renameObfClass( entry.getKey(), entry.getValue() ); + for (Map.Entry entry : orderedClassChanges.entrySet()) { + mappings.renameObfClass(entry.getKey(), entry.getValue()); } // check the method matches - System.out.println( "Checking methods..." ); - for( ClassMapping classMapping : mappings.classes() ) - { - ClassEntry classEntry = new ClassEntry( classMapping.getObfName() ); - for( MethodMapping methodMapping : classMapping.methods() ) - { + System.out.println("Checking methods..."); + for (ClassMapping classMapping : mappings.classes()) { + ClassEntry classEntry = new ClassEntry(classMapping.getObfName()); + for (MethodMapping methodMapping : classMapping.methods()) { + // skip constructors - if( methodMapping.getObfName().equals( "" ) ) - { + if (methodMapping.getObfName().equals("")) { continue; } @@ -271,56 +234,51 @@ public class ClassMatcher methodMapping.getObfName(), methodMapping.getObfSignature() ); - if( !destIndex.containsObfBehavior( methodEntry ) ) - { - System.err.println( "WARNING: method doesn't match: " + methodEntry ); + if (!destIndex.containsObfBehavior(methodEntry)) { + System.err.println("WARNING: method doesn't match: " + methodEntry); // show the available methods - System.err.println( "\tAvailable dest methods:" ); - CtClass c = destLoader.loadClass( classMapping.getObfName() ); - for( CtBehavior behavior : c.getDeclaredBehaviors() ) - { + System.err.println("\tAvailable dest methods:"); + CtClass c = destLoader.loadClass(classMapping.getObfName()); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { MethodEntry declaredMethodEntry = new MethodEntry( - new ClassEntry( classMapping.getObfName() ), + new ClassEntry(classMapping.getObfName()), behavior.getName(), behavior.getSignature() ); - System.err.println( "\t\t" + declaredMethodEntry ); + System.err.println("\t\t" + declaredMethodEntry); } - System.err.println( "\tAvailable source methods:" ); - c = sourceLoader.loadClass( matchedClassNames.inverse().get( classMapping.getObfName() ) ); - for( CtBehavior behavior : c.getDeclaredBehaviors() ) - { + System.err.println("\tAvailable source methods:"); + c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfName())); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { MethodEntry declaredMethodEntry = new MethodEntry( - new ClassEntry( classMapping.getObfName() ), + new ClassEntry(classMapping.getObfName()), behavior.getName(), behavior.getSignature() ); - System.err.println( "\t\t" + declaredMethodEntry ); + System.err.println("\t\t" + declaredMethodEntry); } } } } - System.out.println( "Done!" ); + System.out.println("Done!"); } - public static ClassMatching computeMatching( JarIndex sourceIndex, TranslatingTypeLoader sourceLoader, JarIndex destIndex, TranslatingTypeLoader destLoader ) - { - System.out.println( "Matching classes..." ); + public static ClassMatching computeMatching(JarIndex sourceIndex, TranslatingTypeLoader sourceLoader, JarIndex destIndex, TranslatingTypeLoader destLoader) { + + System.out.println("Matching classes..."); + ClassMatching matching = null; - for( boolean useReferences : Arrays.asList( false, true ) ) - { + for (boolean useReferences : Arrays.asList(false, true)) { int numMatches = 0; - do - { + do { SidedClassNamer sourceNamer = null; SidedClassNamer destNamer = null; - if( matching != null ) - { + if (matching != null) { // build a class namer - ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); + ClassNamer namer = new ClassNamer(matching.getUniqueMatches()); sourceNamer = namer.getSourceNamer(); destNamer = namer.getDestNamer(); @@ -331,158 +289,126 @@ public class ClassMatcher // get the entries left to match Set sourceClassEntries = Sets.newHashSet(); Set destClassEntries = Sets.newHashSet(); - if( matching == null ) - { - sourceClassEntries.addAll( sourceIndex.getObfClassEntries() ); - destClassEntries.addAll( destIndex.getObfClassEntries() ); + if (matching == null) { + sourceClassEntries.addAll(sourceIndex.getObfClassEntries()); + destClassEntries.addAll(destIndex.getObfClassEntries()); matching = new ClassMatching(); - } - else - { - for( Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet() ) - { - for( ClassIdentity c : entry.getKey() ) - { - sourceClassEntries.add( c.getClassEntry() ); - matching.removeSource( c ); + } else { + for (Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet()) { + for (ClassIdentity c : entry.getKey()) { + sourceClassEntries.add(c.getClassEntry()); + matching.removeSource(c); } - for( ClassIdentity c : entry.getValue() ) - { - destClassEntries.add( c.getClassEntry() ); - matching.removeDest( c ); + for (ClassIdentity c : entry.getValue()) { + destClassEntries.add(c.getClassEntry()); + matching.removeDest(c); } } - for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) - { - sourceClassEntries.add( c.getClassEntry() ); - matching.removeSource( c ); + for (ClassIdentity c : matching.getUnmatchedSourceClasses()) { + sourceClassEntries.add(c.getClassEntry()); + matching.removeSource(c); } - for( ClassIdentity c : matching.getUnmatchedDestClasses() ) - { - destClassEntries.add( c.getClassEntry() ); - matching.removeDest( c ); + for (ClassIdentity c : matching.getUnmatchedDestClasses()) { + destClassEntries.add(c.getClassEntry()); + matching.removeDest(c); } } // compute a matching for the classes - for( ClassEntry classEntry : sourceClassEntries ) - { - CtClass c = sourceLoader.loadClass( classEntry.getName() ); - ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences ); - matching.addSource( sourceClass ); + for (ClassEntry classEntry : sourceClassEntries) { + CtClass c = sourceLoader.loadClass(classEntry.getName()); + ClassIdentity sourceClass = new ClassIdentity(c, sourceNamer, sourceIndex, useReferences); + matching.addSource(sourceClass); } - for( ClassEntry classEntry : destClassEntries ) - { - CtClass c = destLoader.loadClass( classEntry.getName() ); - ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences ); - matching.matchDestClass( destClass ); + for (ClassEntry classEntry : destClassEntries) { + CtClass c = destLoader.loadClass(classEntry.getName()); + ClassIdentity destClass = new ClassIdentity(c, destNamer, destIndex, useReferences); + matching.matchDestClass(destClass); } // TEMP - System.out.println( matching ); - } - while( matching.getUniqueMatches().size() - numMatches > 0 ); + System.out.println(matching); + } while (matching.getUniqueMatches().size() - numMatches > 0); } // check the class matches - System.out.println( "Checking class matches..." ); - ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); + System.out.println("Checking class matches..."); + ClassNamer namer = new ClassNamer(matching.getUniqueMatches()); SidedClassNamer sourceNamer = namer.getSourceNamer(); SidedClassNamer destNamer = namer.getDestNamer(); - for( Map.Entry entry : matching.getUniqueMatches().entrySet() ) - { + for (Map.Entry entry : matching.getUniqueMatches().entrySet()) { + // check source ClassIdentity sourceClass = entry.getKey(); - CtClass sourceC = sourceLoader.loadClass( sourceClass.getClassEntry().getName() ); - assert( sourceC != null ) - : "Unable to load source class " + sourceClass.getClassEntry(); - assert( sourceClass.matches( sourceC ) ) - : "Source " + sourceClass + " doesn't match " + new ClassIdentity( sourceC, sourceNamer, sourceIndex, false ); + CtClass sourceC = sourceLoader.loadClass(sourceClass.getClassEntry().getName()); + assert (sourceC != null) : "Unable to load source class " + sourceClass.getClassEntry(); + assert (sourceClass.matches(sourceC)) : "Source " + sourceClass + " doesn't match " + new ClassIdentity(sourceC, sourceNamer, sourceIndex, false); // check dest ClassIdentity destClass = entry.getValue(); - CtClass destC = destLoader.loadClass( destClass.getClassEntry().getName() ); - assert( destC != null ) - : "Unable to load dest class " + destClass.getClassEntry(); - assert( destClass.matches( destC ) ) - : "Dest " + destClass + " doesn't match " + new ClassIdentity( destC, destNamer, destIndex, false ); + CtClass destC = destLoader.loadClass(destClass.getClassEntry().getName()); + assert (destC != null) : "Unable to load dest class " + destClass.getClassEntry(); + assert (destClass.matches(destC)) : "Dest " + destClass + " doesn't match " + new ClassIdentity(destC, destNamer, destIndex, false); } // warn about the ambiguous matchings - List,List>> ambiguousMatches = new ArrayList,List>>( matching.getAmbiguousMatches().entrySet() ); - Collections.sort( ambiguousMatches, new Comparator,List>>( ) - { + List,List>> ambiguousMatches = new ArrayList,List>>(matching.getAmbiguousMatches().entrySet()); + Collections.sort(ambiguousMatches, new Comparator,List>>() { @Override - public int compare( Map.Entry,List> a, Map.Entry,List> b ) - { - String aName = a.getKey().get( 0 ).getClassEntry().getName(); - String bName = b.getKey().get( 0 ).getClassEntry().getName(); - return aName.compareTo( bName ); + public int compare(Map.Entry,List> a, Map.Entry,List> b) { + String aName = a.getKey().get(0).getClassEntry().getName(); + String bName = b.getKey().get(0).getClassEntry().getName(); + return aName.compareTo(bName); } - } ); - for( Map.Entry,List> entry : ambiguousMatches ) - { - System.out.println( "Ambiguous matching:" ); - System.out.println( "\tSource: " + getClassNames( entry.getKey() ) ); - System.out.println( "\tDest: " + getClassNames( entry.getValue() ) ); + }); + for (Map.Entry,List> entry : ambiguousMatches) { + System.out.println("Ambiguous matching:"); + System.out.println("\tSource: " + getClassNames(entry.getKey())); + System.out.println("\tDest: " + getClassNames(entry.getValue())); } /* DEBUG Map.Entry,List> entry = ambiguousMatches.get( 7 ); - for( ClassIdentity c : entry.getKey() ) - { - System.out.println( c ); + for (ClassIdentity c : entry.getKey()) { + System.out.println(c); } - for( ClassIdentity c : entry.getKey() ) - { - System.out.println( decompile( sourceLoader, c.getClassEntry() ) ); + for(ClassIdentity c : entry.getKey()) { + System.out.println(decompile(sourceLoader, c.getClassEntry())); } */ return matching; } - private static void printScoredMatches( int maxScore, List scores, Multimap scoredMatches ) - { + private static void printScoredMatches(int maxScore, List scores, Multimap scoredMatches) { int numScoredMatchesShown = 0; - for( int score : scores ) - { - for( ClassIdentity scoredMatch : scoredMatches.get( score ) ) - { - System.out.println( String.format( "\tScore: %3d %3.0f%% %s", - score, - 100.0*score/maxScore, - scoredMatch.getClassEntry().getName() - ) ); - - if( numScoredMatchesShown++ > 10 ) - { + for (int score : scores) { + for (ClassIdentity scoredMatch : scoredMatches.get(score)) { + System.out.println(String.format("\tScore: %3d %3.0f%% %s", score, 100.0 * score / maxScore, scoredMatch.getClassEntry().getName())); + if (numScoredMatchesShown++ > 10) { return; } } } } - private static List getClassNames( Collection classes ) - { + private static List getClassNames(Collection classes) { List out = Lists.newArrayList(); - for( ClassIdentity c : classes ) - { - out.add( c.getClassEntry().getName() ); + for (ClassIdentity c : classes) { + out.add(c.getClassEntry().getName()); } - Collections.sort( out ); + Collections.sort(out); return out; } /* DEBUG - private static String decompile( TranslatingTypeLoader loader, ClassEntry classEntry ) - { + private static String decompile(TranslatingTypeLoader loader, ClassEntry classEntry) { PlainTextOutput output = new PlainTextOutput(); DecompilerSettings settings = DecompilerSettings.javaDefaults(); - settings.setForceExplicitImports( true ); - settings.setShowSyntheticMembers( true ); - settings.setTypeLoader( loader ); - Decompiler.decompile( classEntry.getName(), output, settings ); + settings.setForceExplicitImports(true); + settings.setShowSyntheticMembers(true); + settings.setTypeLoader(loader); + Decompiler.decompile(classEntry.getName(), output, settings); return output.toString(); } */ diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java index e45c0e1a..53b6f7f4 100644 --- a/src/cuchaz/enigma/convert/ClassMatching.java +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -24,180 +24,150 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; -public class ClassMatching -{ +public class ClassMatching { + private Multimap m_sourceClasses; private Multimap m_matchedDestClasses; private List m_unmatchedDestClasses; - public ClassMatching( ) - { + public ClassMatching() { m_sourceClasses = ArrayListMultimap.create(); m_matchedDestClasses = ArrayListMultimap.create(); m_unmatchedDestClasses = Lists.newArrayList(); } - - public void addSource( ClassIdentity c ) - { - m_sourceClasses.put( c, c ); + + public void addSource(ClassIdentity c) { + m_sourceClasses.put(c, c); } - public void matchDestClass( ClassIdentity destClass ) - { - Collection matchedSourceClasses = m_sourceClasses.get( destClass ); - if( matchedSourceClasses.isEmpty() ) - { + public void matchDestClass(ClassIdentity destClass) { + Collection matchedSourceClasses = m_sourceClasses.get(destClass); + if (matchedSourceClasses.isEmpty()) { // no match - m_unmatchedDestClasses.add( destClass ); - } - else - { + m_unmatchedDestClasses.add(destClass); + } else { // found a match - m_matchedDestClasses.put( destClass, destClass ); + m_matchedDestClasses.put(destClass, destClass); // DEBUG ClassIdentity sourceClass = matchedSourceClasses.iterator().next(); - assert( sourceClass.hashCode() == destClass.hashCode() ); - assert( sourceClass.equals( destClass ) ); + assert (sourceClass.hashCode() == destClass.hashCode()); + assert (sourceClass.equals(destClass)); } } - public void removeSource( ClassIdentity sourceClass ) - { - m_sourceClasses.remove( sourceClass, sourceClass ); + public void removeSource(ClassIdentity sourceClass) { + m_sourceClasses.remove(sourceClass, sourceClass); } - public void removeDest( ClassIdentity destClass ) - { - m_matchedDestClasses.remove( destClass, destClass ); - m_unmatchedDestClasses.remove( destClass ); + public void removeDest(ClassIdentity destClass) { + m_matchedDestClasses.remove(destClass, destClass); + m_unmatchedDestClasses.remove(destClass); } - public List getSourceClasses( ) - { - return new ArrayList( m_sourceClasses.values() ); + public List getSourceClasses() { + return new ArrayList(m_sourceClasses.values()); } - public List getDestClasses( ) - { + public List getDestClasses() { List classes = Lists.newArrayList(); - classes.addAll( m_matchedDestClasses.values() ); - classes.addAll( m_unmatchedDestClasses ); + classes.addAll(m_matchedDestClasses.values()); + classes.addAll(m_unmatchedDestClasses); return classes; } - public BiMap getUniqueMatches( ) - { + public BiMap getUniqueMatches() { BiMap uniqueMatches = HashBiMap.create(); - for( ClassIdentity sourceClass : m_sourceClasses.keySet() ) - { - Collection matchedSourceClasses = m_sourceClasses.get( sourceClass ); - Collection matchedDestClasses = m_matchedDestClasses.get( sourceClass ); - if( matchedSourceClasses.size() == 1 && matchedDestClasses.size() == 1 ) - { + for (ClassIdentity sourceClass : m_sourceClasses.keySet()) { + Collection matchedSourceClasses = m_sourceClasses.get(sourceClass); + Collection matchedDestClasses = m_matchedDestClasses.get(sourceClass); + if (matchedSourceClasses.size() == 1 && matchedDestClasses.size() == 1) { ClassIdentity matchedSourceClass = matchedSourceClasses.iterator().next(); ClassIdentity matchedDestClass = matchedDestClasses.iterator().next(); - uniqueMatches.put( matchedSourceClass, matchedDestClass ); + uniqueMatches.put(matchedSourceClass, matchedDestClass); } } return uniqueMatches; } - public BiMap,List> getAmbiguousMatches( ) - { + public BiMap,List> getAmbiguousMatches() { BiMap,List> ambiguousMatches = HashBiMap.create(); - for( ClassIdentity sourceClass : m_sourceClasses.keySet() ) - { - Collection matchedSourceClasses = m_sourceClasses.get( sourceClass ); - Collection matchedDestClasses = m_matchedDestClasses.get( sourceClass ); - if( matchedSourceClasses.size() > 1 && matchedDestClasses.size() > 1 ) - { + for (ClassIdentity sourceClass : m_sourceClasses.keySet()) { + Collection matchedSourceClasses = m_sourceClasses.get(sourceClass); + Collection matchedDestClasses = m_matchedDestClasses.get(sourceClass); + if (matchedSourceClasses.size() > 1 && matchedDestClasses.size() > 1) { ambiguousMatches.put( - new ArrayList( matchedSourceClasses ), - new ArrayList( matchedDestClasses ) + new ArrayList(matchedSourceClasses), + new ArrayList(matchedDestClasses) ); } } return ambiguousMatches; } - public int getNumAmbiguousSourceMatches( ) - { + public int getNumAmbiguousSourceMatches() { int num = 0; - for( Map.Entry,List> entry : getAmbiguousMatches().entrySet() ) - { + for (Map.Entry,List> entry : getAmbiguousMatches().entrySet()) { num += entry.getKey().size(); } return num; } - public int getNumAmbiguousDestMatches( ) - { + public int getNumAmbiguousDestMatches() { int num = 0; - for( Map.Entry,List> entry : getAmbiguousMatches().entrySet() ) - { + for (Map.Entry,List> entry : getAmbiguousMatches().entrySet()) { num += entry.getValue().size(); } return num; } - public List getUnmatchedSourceClasses( ) - { + public List getUnmatchedSourceClasses() { List classes = Lists.newArrayList(); - for( ClassIdentity sourceClass : getSourceClasses() ) - { - if( m_matchedDestClasses.get( sourceClass ).isEmpty() ) - { - classes.add( sourceClass ); + for (ClassIdentity sourceClass : getSourceClasses()) { + if (m_matchedDestClasses.get(sourceClass).isEmpty()) { + classes.add(sourceClass); } } return classes; } - public List getUnmatchedDestClasses( ) - { - return new ArrayList( m_unmatchedDestClasses ); + public List getUnmatchedDestClasses() { + return new ArrayList(m_unmatchedDestClasses); } - public Map>> getIndex( ) - { + public Map>> getIndex() { Map>> conversion = Maps.newHashMap(); - for( Map.Entry entry : getUniqueMatches().entrySet() ) - { + for (Map.Entry entry : getUniqueMatches().entrySet()) { conversion.put( entry.getKey().getClassEntry().getName(), - new AbstractMap.SimpleEntry>( entry.getKey(), Arrays.asList( entry.getValue() ) ) + new AbstractMap.SimpleEntry>(entry.getKey(), Arrays.asList(entry.getValue())) ); } - for( Map.Entry,List> entry : getAmbiguousMatches().entrySet() ) - { - for( ClassIdentity sourceClass : entry.getKey() ) - { + for (Map.Entry,List> entry : getAmbiguousMatches().entrySet()) { + for (ClassIdentity sourceClass : entry.getKey()) { conversion.put( sourceClass.getClassEntry().getName(), - new AbstractMap.SimpleEntry>( sourceClass, entry.getValue() ) + new AbstractMap.SimpleEntry>(sourceClass, entry.getValue()) ); } } - for( ClassIdentity sourceClass : getUnmatchedSourceClasses() ) - { + for (ClassIdentity sourceClass : getUnmatchedSourceClasses()) { conversion.put( sourceClass.getClassEntry().getName(), - new AbstractMap.SimpleEntry>( sourceClass, getUnmatchedDestClasses() ) + new AbstractMap.SimpleEntry>(sourceClass, getUnmatchedDestClasses()) ); } return conversion; } @Override - public String toString( ) - { + public String toString() { StringBuilder buf = new StringBuilder(); - buf.append( String.format( "%12s%8s%8s\n", "", "Source", "Dest" ) ); - buf.append( String.format( "%12s%8d%8d\n", "Classes", getSourceClasses().size(), getDestClasses().size() ) ); - buf.append( String.format( "%12s%8d%8d\n", "Unique", getUniqueMatches().size(), getUniqueMatches().size() ) ); - buf.append( String.format( "%12s%8d%8d\n", "Ambiguous", getNumAmbiguousSourceMatches(), getNumAmbiguousDestMatches() ) ); - buf.append( String.format( "%12s%8d%8d\n", "Unmatched", getUnmatchedSourceClasses().size(), getUnmatchedDestClasses().size() ) ); + buf.append(String.format("%12s%8s%8s\n", "", "Source", "Dest")); + buf.append(String.format("%12s%8d%8d\n", "Classes", getSourceClasses().size(), getDestClasses().size())); + buf.append(String.format("%12s%8d%8d\n", "Unique", getUniqueMatches().size(), getUniqueMatches().size())); + buf.append(String.format("%12s%8d%8d\n", "Ambiguous", getNumAmbiguousSourceMatches(), getNumAmbiguousDestMatches())); + buf.append(String.format("%12s%8d%8d\n", "Unmatched", getUnmatchedSourceClasses().size(), getUnmatchedDestClasses().size())); return buf.toString(); } } diff --git a/src/cuchaz/enigma/convert/ClassNamer.java b/src/cuchaz/enigma/convert/ClassNamer.java index a01aec5c..1b6e81c8 100644 --- a/src/cuchaz/enigma/convert/ClassNamer.java +++ b/src/cuchaz/enigma/convert/ClassNamer.java @@ -15,60 +15,49 @@ import java.util.Map; import com.google.common.collect.BiMap; import com.google.common.collect.Maps; -public class ClassNamer -{ - public interface SidedClassNamer - { - String getName( String name ); +public class ClassNamer { + + public interface SidedClassNamer { + String getName(String name); } private Map m_sourceNames; private Map m_destNames; - public ClassNamer( BiMap mappings ) - { + public ClassNamer(BiMap mappings) { // convert the identity mappings to name maps m_sourceNames = Maps.newHashMap(); m_destNames = Maps.newHashMap(); int i = 0; - for( Map.Entry entry : mappings.entrySet() ) - { - String name = String.format( "M%04d", i++ ); - m_sourceNames.put( entry.getKey().getClassEntry().getName(), name ); - m_destNames.put( entry.getValue().getClassEntry().getName(), name ); + for (Map.Entry entry : mappings.entrySet()) { + String name = String.format("M%04d", i++); + m_sourceNames.put(entry.getKey().getClassEntry().getName(), name); + m_destNames.put(entry.getValue().getClassEntry().getName(), name); } } - public String getSourceName( String name ) - { - return m_sourceNames.get( name ); + public String getSourceName(String name) { + return m_sourceNames.get(name); } - public String getDestName( String name ) - { - return m_destNames.get( name ); + public String getDestName(String name) { + return m_destNames.get(name); } - public SidedClassNamer getSourceNamer( ) - { - return new SidedClassNamer( ) - { + public SidedClassNamer getSourceNamer() { + return new SidedClassNamer() { @Override - public String getName( String name ) - { - return getSourceName( name ); + public String getName(String name) { + return getSourceName(name); } }; } - public SidedClassNamer getDestNamer( ) - { - return new SidedClassNamer( ) - { + public SidedClassNamer getDestNamer() { + return new SidedClassNamer() { @Override - public String getName( String name ) - { - return getDestName( name ); + public String getName(String name) { + return getDestName(name); } }; } diff --git a/src/cuchaz/enigma/gui/AboutDialog.java b/src/cuchaz/enigma/gui/AboutDialog.java index a245956e..2476b564 100644 --- a/src/cuchaz/enigma/gui/AboutDialog.java +++ b/src/cuchaz/enigma/gui/AboutDialog.java @@ -27,68 +27,60 @@ import javax.swing.WindowConstants; import cuchaz.enigma.Constants; import cuchaz.enigma.Util; -public class AboutDialog -{ - public static void show( JFrame parent ) - { +public class AboutDialog { + + public static void show(JFrame parent) { // init frame - final JFrame frame = new JFrame( Constants.Name + " - About" ); + final JFrame frame = new JFrame(Constants.Name + " - About"); final Container pane = frame.getContentPane(); - pane.setLayout( new FlowLayout() ); + pane.setLayout(new FlowLayout()); // load the content - try - { - String html = Util.readResourceToString( "/about.html" ); - html = String.format( html, Constants.Name, Constants.Version ); - JLabel label = new JLabel( html ); - label.setHorizontalAlignment( JLabel.CENTER ); - pane.add( label ); - } - catch( IOException ex ) - { - throw new Error( ex ); + try { + String html = Util.readResourceToString("/about.html"); + html = String.format(html, Constants.Name, Constants.Version); + JLabel label = new JLabel(html); + label.setHorizontalAlignment(JLabel.CENTER); + pane.add(label); + } catch (IOException ex) { + throw new Error(ex); } // show the link String html = "%s"; - html = String.format( html, Constants.Url, Constants.Url ); - JButton link = new JButton( html ); - link.addActionListener( new ActionListener( ) - { + html = String.format(html, Constants.Url, Constants.Url); + JButton link = new JButton(html); + link.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { - Util.openUrl( Constants.Url ); + public void actionPerformed(ActionEvent event) { + Util.openUrl(Constants.Url); } - } ); - link.setBorderPainted( false ); - link.setOpaque( false ); - link.setBackground( Color.WHITE ); - link.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - link.setFocusable( false ); + }); + link.setBorderPainted(false); + link.setOpaque(false); + link.setBackground(Color.WHITE); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.setFocusable(false); JPanel linkPanel = new JPanel(); - linkPanel.add( link ); - pane.add( linkPanel ); + linkPanel.add(link); + pane.add(linkPanel); // show ok button - JButton okButton = new JButton( "Ok" ); - pane.add( okButton ); - okButton.addActionListener( new ActionListener( ) - { + JButton okButton = new JButton("Ok"); + pane.add(okButton); + okButton.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent arg0 ) - { + public void actionPerformed(ActionEvent arg0) { frame.dispose(); } - } ); + }); // show the frame pane.doLayout(); - frame.setSize( 400, 220 ); - frame.setResizable( false ); - frame.setLocationRelativeTo( parent ); - frame.setVisible( true ); - frame.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE ); + frame.setSize(400, 220); + frame.setResizable(false); + frame.setLocationRelativeTo(parent); + frame.setVisible(true); + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); } } diff --git a/src/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/cuchaz/enigma/gui/BoxHighlightPainter.java index df63f5a8..db7c85b4 100644 --- a/src/cuchaz/enigma/gui/BoxHighlightPainter.java +++ b/src/cuchaz/enigma/gui/BoxHighlightPainter.java @@ -19,40 +19,35 @@ import javax.swing.text.BadLocationException; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; -public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter -{ +public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter { + private Color m_fillColor; private Color m_borderColor; - protected BoxHighlightPainter( Color fillColor, Color borderColor ) - { + protected BoxHighlightPainter(Color fillColor, Color borderColor) { m_fillColor = fillColor; m_borderColor = borderColor; } @Override - public void paint( Graphics g, int start, int end, Shape shape, JTextComponent text ) - { - Rectangle bounds = getBounds( text, start, end ); + public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { + Rectangle bounds = getBounds(text, start, end); // fill the area - if( m_fillColor != null ) - { - g.setColor( m_fillColor ); - g.fillRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); + if (m_fillColor != null) { + g.setColor(m_fillColor); + g.fillRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); } // draw a box around the area - g.setColor( m_borderColor ); - g.drawRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); + g.setColor(m_borderColor); + g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); } - protected static Rectangle getBounds( JTextComponent text, int start, int end ) - { - try - { + protected static Rectangle getBounds(JTextComponent text, int start, int end) { + try { // determine the bounds of the text - Rectangle bounds = text.modelToView( start ).union( text.modelToView( end ) ); + Rectangle bounds = text.modelToView(start).union(text.modelToView(end)); // adjust the box so it looks nice bounds.x -= 2; @@ -61,11 +56,9 @@ public abstract class BoxHighlightPainter implements Highlighter.HighlightPainte bounds.height -= 2; return bounds; - } - catch( BadLocationException ex ) - { + } catch (BadLocationException ex) { // don't care... just return something - return new Rectangle( 0, 0, 0, 0 ); + return new Rectangle(0, 0, 0, 0); } } } diff --git a/src/cuchaz/enigma/gui/BrowserCaret.java b/src/cuchaz/enigma/gui/BrowserCaret.java index f7e608bb..acee4833 100644 --- a/src/cuchaz/enigma/gui/BrowserCaret.java +++ b/src/cuchaz/enigma/gui/BrowserCaret.java @@ -17,34 +17,29 @@ import javax.swing.text.DefaultCaret; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; -public class BrowserCaret extends DefaultCaret -{ +public class BrowserCaret extends DefaultCaret { + private static final long serialVersionUID = 1158977422507969940L; - private static final Highlighter.HighlightPainter m_selectionPainter = new Highlighter.HighlightPainter( ) - { + private static final Highlighter.HighlightPainter m_selectionPainter = new Highlighter.HighlightPainter() { @Override - public void paint( Graphics g, int p0, int p1, Shape bounds, JTextComponent c ) - { + public void paint(Graphics g, int p0, int p1, Shape bounds, JTextComponent c) { // don't paint anything } }; @Override - public boolean isSelectionVisible( ) - { + public boolean isSelectionVisible() { return false; } - + @Override - public boolean isVisible( ) - { + public boolean isVisible() { return true; } @Override - public Highlighter.HighlightPainter getSelectionPainter( ) - { + public Highlighter.HighlightPainter getSelectionPainter() { return m_selectionPainter; } } diff --git a/src/cuchaz/enigma/gui/ClassListCellRenderer.java b/src/cuchaz/enigma/gui/ClassListCellRenderer.java index d9d65788..d0f01e6a 100644 --- a/src/cuchaz/enigma/gui/ClassListCellRenderer.java +++ b/src/cuchaz/enigma/gui/ClassListCellRenderer.java @@ -19,20 +19,18 @@ import javax.swing.JLabel; import javax.swing.JList; import javax.swing.ListCellRenderer; -public class ClassListCellRenderer implements ListCellRenderer -{ +public class ClassListCellRenderer implements ListCellRenderer { + private DefaultListCellRenderer m_defaultRenderer; - public ClassListCellRenderer( ) - { + public ClassListCellRenderer() { m_defaultRenderer = new DefaultListCellRenderer(); } @Override - public Component getListCellRendererComponent( JList list, String className, int index, boolean isSelected, boolean hasFocus ) - { - JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, className, index, isSelected, hasFocus ); - label.setText( Descriptor.toJavaName( className ) ); + public Component getListCellRendererComponent(JList list, String className, int index, boolean isSelected, boolean hasFocus) { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent(list, className, index, isSelected, hasFocus); + label.setText(Descriptor.toJavaName(className)); return label; } } diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java index 8365def1..654bfbed 100644 --- a/src/cuchaz/enigma/gui/ClassSelector.java +++ b/src/cuchaz/enigma/gui/ClassSelector.java @@ -30,39 +30,32 @@ import com.google.common.collect.Multimap; import cuchaz.enigma.mapping.ClassEntry; -public class ClassSelector extends JTree -{ +public class ClassSelector extends JTree { + private static final long serialVersionUID = -7632046902384775977L; - public interface ClassSelectionListener - { - void onSelectClass( ClassEntry classEntry ); + public interface ClassSelectionListener { + void onSelectClass(ClassEntry classEntry); } public static Comparator ObfuscatedClassEntryComparator; public static Comparator DeobfuscatedClassEntryComparator; - static - { - ObfuscatedClassEntryComparator = new Comparator( ) - { + static { + ObfuscatedClassEntryComparator = new Comparator() { @Override - public int compare( ClassEntry a, ClassEntry b ) - { - if( a.getName().length() != b.getName().length() ) - { + public int compare(ClassEntry a, ClassEntry b) { + if (a.getName().length() != b.getName().length()) { return a.getName().length() - b.getName().length(); } - return a.getName().compareTo( b.getName() ); + return a.getName().compareTo(b.getName()); } }; - DeobfuscatedClassEntryComparator = new Comparator( ) - { + DeobfuscatedClassEntryComparator = new Comparator() { @Override - public int compare( ClassEntry a, ClassEntry b ) - { - return a.getName().compareTo( b.getName() ); + public int compare(ClassEntry a, ClassEntry b) { + return a.getName().compareTo(b.getName()); } }; } @@ -70,122 +63,102 @@ public class ClassSelector extends JTree private ClassSelectionListener m_listener; private Comparator m_comparator; - public ClassSelector( Comparator comparator ) - { + public ClassSelector(Comparator comparator) { m_comparator = comparator; // configure the tree control - setRootVisible( false ); - setShowsRootHandles( false ); - setModel( null ); + setRootVisible(false); + setShowsRootHandles(false); + setModel(null); // hook events - addMouseListener( new MouseAdapter() - { + addMouseListener(new MouseAdapter() { @Override - public void mouseClicked( MouseEvent event ) - { - if( m_listener != null && event.getClickCount() == 2 ) - { + public void mouseClicked(MouseEvent event) { + if (m_listener != null && event.getClickCount() == 2) { // get the selected node TreePath path = getSelectionPath(); - if( path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode ) - { + if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { ClassSelectorClassNode node = (ClassSelectorClassNode)path.getLastPathComponent(); - m_listener.onSelectClass( node.getClassEntry() ); + m_listener.onSelectClass(node.getClassEntry()); } } } - } ); + }); // init defaults m_listener = null; } - public void setListener( ClassSelectionListener val ) - { + public void setListener(ClassSelectionListener val) { m_listener = val; } - public void setClasses( Collection classEntries ) - { - if( classEntries == null ) - { - setModel( null ); + public void setClasses(Collection classEntries) { + if (classEntries == null) { + setModel(null); return; } // build the package names Map packages = Maps.newHashMap(); - for( ClassEntry classEntry : classEntries ) - { - packages.put( classEntry.getPackageName(), null ); + for (ClassEntry classEntry : classEntries) { + packages.put(classEntry.getPackageName(), null); } // sort the packages - List sortedPackageNames = Lists.newArrayList( packages.keySet() ); - Collections.sort( sortedPackageNames, new Comparator( ) - { + List sortedPackageNames = Lists.newArrayList(packages.keySet()); + Collections.sort(sortedPackageNames, new Comparator() { @Override - public int compare( String a, String b ) - { + public int compare(String a, String b) { // I can never keep this rule straight when writing these damn things... // a < b => -1, a == b => 0, a > b => +1 - String[] aparts = a.split( "/" ); - String[] bparts = b.split( "/" ); - for( int i=0; true; i++ ) - { - if( i >= aparts.length ) - { + String[] aparts = a.split("/"); + String[] bparts = b.split("/"); + for (int i = 0; true; i++) { + if (i >= aparts.length) { return -1; - } - else if( i >= bparts.length ) - { + } else if (i >= bparts.length) { return 1; } - int result = aparts[i].compareTo( bparts[i] ); - if( result != 0 ) - { + int result = aparts[i].compareTo(bparts[i]); + if (result != 0) { return result; } } } - } ); + }); // create the root node and the package nodes DefaultMutableTreeNode root = new DefaultMutableTreeNode(); - for( String packageName : sortedPackageNames ) - { - ClassSelectorPackageNode node = new ClassSelectorPackageNode( packageName ); - packages.put( packageName, node ); - root.add( node ); + for (String packageName : sortedPackageNames) { + ClassSelectorPackageNode node = new ClassSelectorPackageNode(packageName); + packages.put(packageName, node); + root.add(node); } // put the classes into packages Multimap packagedClassEntries = ArrayListMultimap.create(); - for( ClassEntry classEntry : classEntries ) - { - packagedClassEntries.put( classEntry.getPackageName(), classEntry ); + for (ClassEntry classEntry : classEntries) { + packagedClassEntries.put(classEntry.getPackageName(), classEntry); } // build the class nodes - for( String packageName : packagedClassEntries.keySet() ) - { + for (String packageName : packagedClassEntries.keySet()) { // sort the class entries - List classEntriesInPackage = Lists.newArrayList( packagedClassEntries.get( packageName ) ); - Collections.sort( classEntriesInPackage, m_comparator ); + List classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); + Collections.sort(classEntriesInPackage, m_comparator); // create the nodes in order - for( ClassEntry classEntry : classEntriesInPackage ) - { - ClassSelectorPackageNode node = packages.get( packageName ); - node.add( new ClassSelectorClassNode( classEntry ) ); + for (ClassEntry classEntry : classEntriesInPackage) { + ClassSelectorPackageNode node = packages.get(packageName); + node.add(new ClassSelectorClassNode(classEntry)); } } // finally, update the tree control - setModel( new DefaultTreeModel( root ) ); + setModel(new DefaultTreeModel(root)); } } diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java index cffa7952..66e931b4 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java @@ -14,25 +14,22 @@ import javax.swing.tree.DefaultMutableTreeNode; import cuchaz.enigma.mapping.ClassEntry; -public class ClassSelectorClassNode extends DefaultMutableTreeNode -{ +public class ClassSelectorClassNode extends DefaultMutableTreeNode { + private static final long serialVersionUID = -8956754339813257380L; private ClassEntry m_classEntry; - public ClassSelectorClassNode( ClassEntry classEntry ) - { + public ClassSelectorClassNode(ClassEntry classEntry) { m_classEntry = classEntry; } - public ClassEntry getClassEntry( ) - { + public ClassEntry getClassEntry() { return m_classEntry; } @Override - public String toString( ) - { + public String toString() { return m_classEntry.getSimpleName(); } } diff --git a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java index ad88fb44..451d3809 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java @@ -12,25 +12,22 @@ package cuchaz.enigma.gui; import javax.swing.tree.DefaultMutableTreeNode; -public class ClassSelectorPackageNode extends DefaultMutableTreeNode -{ +public class ClassSelectorPackageNode extends DefaultMutableTreeNode { + private static final long serialVersionUID = -3730868701219548043L; private String m_packageName; - public ClassSelectorPackageNode( String packageName ) - { + public ClassSelectorPackageNode(String packageName) { m_packageName = packageName; } - public String getPackageName( ) - { + public String getPackageName() { return m_packageName; } @Override - public String toString( ) - { + public String toString() { return m_packageName; } } diff --git a/src/cuchaz/enigma/gui/CrashDialog.java b/src/cuchaz/enigma/gui/CrashDialog.java index 0eb9830c..360091ab 100644 --- a/src/cuchaz/enigma/gui/CrashDialog.java +++ b/src/cuchaz/enigma/gui/CrashDialog.java @@ -29,80 +29,73 @@ import javax.swing.WindowConstants; import cuchaz.enigma.Constants; -public class CrashDialog -{ +public class CrashDialog { + private static CrashDialog m_instance = null; private JFrame m_frame; private JTextArea m_text; - private CrashDialog( JFrame parent ) - { + private CrashDialog(JFrame parent) { // init frame - m_frame = new JFrame( Constants.Name + " - Crash Report" ); + m_frame = new JFrame(Constants.Name + " - Crash Report"); final Container pane = m_frame.getContentPane(); - pane.setLayout( new BorderLayout() ); + pane.setLayout(new BorderLayout()); - JLabel label = new JLabel( Constants.Name + " has crashed! =(" ); - label.setBorder( BorderFactory.createEmptyBorder( 10, 10, 10, 10 ) ); - pane.add( label, BorderLayout.NORTH ); + JLabel label = new JLabel(Constants.Name + " has crashed! =("); + label.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + pane.add(label, BorderLayout.NORTH); // report panel m_text = new JTextArea(); - m_text.setTabSize( 2 ); - pane.add( new JScrollPane( m_text ), BorderLayout.CENTER ); + m_text.setTabSize(2); + pane.add(new JScrollPane(m_text), BorderLayout.CENTER); // buttons panel JPanel buttonsPanel = new JPanel(); FlowLayout buttonsLayout = new FlowLayout(); - buttonsLayout.setAlignment( FlowLayout.RIGHT ); - buttonsPanel.setLayout( buttonsLayout ); - buttonsPanel.add( GuiTricks.unboldLabel( new JLabel( "If you choose exit, you will lose any unsaved work." ) ) ); - JButton ignoreButton = new JButton( "Ignore" ); - ignoreButton.addActionListener( new ActionListener( ) - { + buttonsLayout.setAlignment(FlowLayout.RIGHT); + buttonsPanel.setLayout(buttonsLayout); + buttonsPanel.add(GuiTricks.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work."))); + JButton ignoreButton = new JButton("Ignore"); + ignoreButton.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { + public void actionPerformed(ActionEvent event) { // close (hide) the dialog - m_frame.setVisible( false ); + m_frame.setVisible(false); } - } ); - buttonsPanel.add( ignoreButton ); - JButton exitButton = new JButton( "Exit" ); - exitButton.addActionListener( new ActionListener( ) - { + }); + buttonsPanel.add(ignoreButton); + JButton exitButton = new JButton("Exit"); + exitButton.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { + public void actionPerformed(ActionEvent event) { // exit enigma - System.exit( 1 ); + System.exit(1); } - } ); - buttonsPanel.add( exitButton ); - pane.add( buttonsPanel, BorderLayout.SOUTH ); + }); + buttonsPanel.add(exitButton); + pane.add(buttonsPanel, BorderLayout.SOUTH); // show the frame - m_frame.setSize( 600, 400 ); - m_frame.setLocationRelativeTo( parent ); - m_frame.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE ); + m_frame.setSize(600, 400); + m_frame.setLocationRelativeTo(parent); + m_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); } - public static void init( JFrame parent ) - { - m_instance = new CrashDialog( parent ); + public static void init(JFrame parent) { + m_instance = new CrashDialog(parent); } - public static void show( Throwable ex ) - { + public static void show(Throwable ex) { // get the error report StringWriter buf = new StringWriter(); - ex.printStackTrace( new PrintWriter( buf ) ); + ex.printStackTrace(new PrintWriter(buf)); String report = buf.toString(); // show it! - m_instance.m_text.setText( report ); + m_instance.m_text.setText(report); m_instance.m_frame.doLayout(); - m_instance.m_frame.setVisible( true ); + m_instance.m_frame.setVisible(true); } } diff --git a/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java index 6a428842..26a31639 100644 --- a/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java +++ b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java @@ -12,11 +12,10 @@ package cuchaz.enigma.gui; import java.awt.Color; -public class DeobfuscatedHighlightPainter extends BoxHighlightPainter -{ - public DeobfuscatedHighlightPainter( ) - { +public class DeobfuscatedHighlightPainter extends BoxHighlightPainter { + + public DeobfuscatedHighlightPainter() { // green ish - super( new Color( 220, 255, 220 ), new Color( 80, 160, 80 ) ); + super(new Color(220, 255, 220), new Color(80, 160, 80)); } } diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index faa9b7b1..86ba93b2 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -88,8 +88,8 @@ import cuchaz.enigma.mapping.IllegalNameException; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.MethodEntry; -public class Gui -{ +public class Gui { + private GuiController m_controller; // controls @@ -133,81 +133,74 @@ public class Gui private JFileChooser m_exportSourceFileChooser; private JFileChooser m_exportJarFileChooser; - public Gui( ) - { + public Gui() { + // init frame - m_frame = new JFrame( Constants.Name ); + m_frame = new JFrame(Constants.Name); final Container pane = m_frame.getContentPane(); - pane.setLayout( new BorderLayout() ); + pane.setLayout(new BorderLayout()); - if( Boolean.parseBoolean( System.getProperty( "enigma.catchExceptions", "true" ) ) ) - { + if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) { // install a global exception handler to the event thread - CrashDialog.init( m_frame ); - Thread.setDefaultUncaughtExceptionHandler( new UncaughtExceptionHandler( ) - { + CrashDialog.init(m_frame); + Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override - public void uncaughtException( Thread thread, Throwable ex ) - { - ex.printStackTrace( System.err ); - CrashDialog.show( ex ); + public void uncaughtException(Thread thread, Throwable ex) { + ex.printStackTrace(System.err); + CrashDialog.show(ex); } - } ); + }); } - m_controller = new GuiController( this ); + m_controller = new GuiController(this); // init file choosers m_jarFileChooser = new JFileChooser(); m_mappingsFileChooser = new JFileChooser(); m_exportSourceFileChooser = new JFileChooser(); - m_exportSourceFileChooser.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY ); + m_exportSourceFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); m_exportJarFileChooser = new JFileChooser(); // init obfuscated classes list - m_obfClasses = new ClassSelector( ClassSelector.ObfuscatedClassEntryComparator ); - m_obfClasses.setListener( new ClassSelectionListener( ) - { + m_obfClasses = new ClassSelector(ClassSelector.ObfuscatedClassEntryComparator); + m_obfClasses.setListener(new ClassSelectionListener() { @Override - public void onSelectClass( ClassEntry classEntry ) - { - navigateTo( classEntry ); + public void onSelectClass(ClassEntry classEntry) { + navigateTo(classEntry); } - } ); - JScrollPane obfScroller = new JScrollPane( m_obfClasses ); + }); + JScrollPane obfScroller = new JScrollPane(m_obfClasses); JPanel obfPanel = new JPanel(); - obfPanel.setLayout( new BorderLayout() ); - obfPanel.add( new JLabel( "Obfuscated Classes" ), BorderLayout.NORTH ); - obfPanel.add( obfScroller, BorderLayout.CENTER ); + obfPanel.setLayout(new BorderLayout()); + obfPanel.add(new JLabel("Obfuscated Classes"), BorderLayout.NORTH); + obfPanel.add(obfScroller, BorderLayout.CENTER); // init deobfuscated classes list - m_deobfClasses = new ClassSelector( ClassSelector.DeobfuscatedClassEntryComparator ); - m_deobfClasses.setListener( new ClassSelectionListener( ) - { + m_deobfClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); + m_deobfClasses.setListener(new ClassSelectionListener() { @Override - public void onSelectClass( ClassEntry classEntry ) - { - navigateTo( classEntry ); + public void onSelectClass(ClassEntry classEntry) { + navigateTo(classEntry); } - } ); - JScrollPane deobfScroller = new JScrollPane( m_deobfClasses ); + }); + JScrollPane deobfScroller = new JScrollPane(m_deobfClasses); JPanel deobfPanel = new JPanel(); - deobfPanel.setLayout( new BorderLayout() ); - deobfPanel.add( new JLabel( "De-obfuscated Classes" ), BorderLayout.NORTH ); - deobfPanel.add( deobfScroller, BorderLayout.CENTER ); + deobfPanel.setLayout(new BorderLayout()); + deobfPanel.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH); + deobfPanel.add(deobfScroller, BorderLayout.CENTER); // set up classes panel (don't add the splitter yet) - m_splitClasses = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); - m_splitClasses.setResizeWeight( 0.3 ); + m_splitClasses = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel); + m_splitClasses.setResizeWeight(0.3); m_classesPanel = new JPanel(); - m_classesPanel.setLayout( new BorderLayout() ); - m_classesPanel.setPreferredSize( new Dimension( 250, 0 ) ); + m_classesPanel.setLayout(new BorderLayout()); + m_classesPanel.setPreferredSize(new Dimension(250, 0)); // init info panel m_infoPanel = new JPanel(); - m_infoPanel.setLayout( new GridLayout( 4, 1, 0, 0 ) ); - m_infoPanel.setPreferredSize( new Dimension( 0, 100 ) ); - m_infoPanel.setBorder( BorderFactory.createTitledBorder( "Identifier Info" ) ); + m_infoPanel.setLayout(new GridLayout(4, 1, 0, 0)); + m_infoPanel.setPreferredSize(new Dimension(0, 100)); + m_infoPanel.setBorder(BorderFactory.createTitledBorder("Identifier Info")); clearReference(); // init editor @@ -217,25 +210,20 @@ public class Gui m_otherHighlightPainter = new OtherHighlightPainter(); m_selectionHighlightPainter = new SelectionHighlightPainter(); m_editor = new JEditorPane(); - m_editor.setEditable( false ); - m_editor.setCaret( new BrowserCaret() ); - JScrollPane sourceScroller = new JScrollPane( m_editor ); - m_editor.setContentType( "text/java" ); - m_editor.addCaretListener( new CaretListener( ) - { + m_editor.setEditable(false); + m_editor.setCaret(new BrowserCaret()); + JScrollPane sourceScroller = new JScrollPane(m_editor); + m_editor.setContentType("text/java"); + m_editor.addCaretListener(new CaretListener() { @Override - public void caretUpdate( CaretEvent event ) - { - onCaretMove( event.getDot() ); + public void caretUpdate(CaretEvent event) { + onCaretMove(event.getDot()); } - } ); - m_editor.addKeyListener( new KeyAdapter( ) - { + }); + m_editor.addKeyListener(new KeyAdapter() { @Override - public void keyPressed( KeyEvent event ) - { - switch( event.getKeyCode() ) - { + public void keyPressed(KeyEvent event) { + switch (event.getKeyCode()) { case KeyEvent.VK_R: m_renameMenu.doClick(); break; @@ -265,713 +253,594 @@ public class Gui break; } } - } ); + }); // turn off token highlighting (it's wrong most of the time anyway...) DefaultSyntaxKit kit = (DefaultSyntaxKit)m_editor.getEditorKit(); - kit.toggleComponent( m_editor, "jsyntaxpane.components.TokenMarker" ); + kit.toggleComponent(m_editor, "jsyntaxpane.components.TokenMarker"); // init editor popup menu JPopupMenu popupMenu = new JPopupMenu(); - m_editor.setComponentPopupMenu( popupMenu ); + m_editor.setComponentPopupMenu(popupMenu); { - JMenuItem menu = new JMenuItem( "Rename" ); - menu.addActionListener( new ActionListener( ) - { + JMenuItem menu = new JMenuItem("Rename"); + menu.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { + public void actionPerformed(ActionEvent event) { startRename(); } - } ); - menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_R, 0 ) ); - menu.setEnabled( false ); - popupMenu.add( menu ); + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0)); + menu.setEnabled(false); + popupMenu.add(menu); m_renameMenu = menu; } { - JMenuItem menu = new JMenuItem( "Show Inheritance" ); - menu.addActionListener( new ActionListener( ) - { + JMenuItem menu = new JMenuItem("Show Inheritance"); + menu.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { + public void actionPerformed(ActionEvent event) { showInheritance(); } - } ); - menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_I, 0 ) ); - menu.setEnabled( false ); - popupMenu.add( menu ); + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, 0)); + menu.setEnabled(false); + popupMenu.add(menu); m_showInheritanceMenu = menu; } { - JMenuItem menu = new JMenuItem( "Show Implementations" ); - menu.addActionListener( new ActionListener( ) - { + JMenuItem menu = new JMenuItem("Show Implementations"); + menu.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { + public void actionPerformed(ActionEvent event) { showImplementations(); } - } ); - menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_M, 0 ) ); - menu.setEnabled( false ); - popupMenu.add( menu ); + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, 0)); + menu.setEnabled(false); + popupMenu.add(menu); m_showImplementationsMenu = menu; } { - JMenuItem menu = new JMenuItem( "Show Calls" ); - menu.addActionListener( new ActionListener( ) - { + JMenuItem menu = new JMenuItem("Show Calls"); + menu.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { + public void actionPerformed(ActionEvent event) { showCalls(); } - } ); - menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_C, 0 ) ); - menu.setEnabled( false ); - popupMenu.add( menu ); + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0)); + menu.setEnabled(false); + popupMenu.add(menu); m_showCallsMenu = menu; } { - JMenuItem menu = new JMenuItem( "Go to Declaration" ); - menu.addActionListener( new ActionListener( ) - { + JMenuItem menu = new JMenuItem("Go to Declaration"); + menu.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { - navigateTo( m_reference.entry ); + public void actionPerformed(ActionEvent event) { + navigateTo(m_reference.entry); } - } ); - menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_N, 0 ) ); - menu.setEnabled( false ); - popupMenu.add( menu ); + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0)); + menu.setEnabled(false); + popupMenu.add(menu); m_openEntryMenu = menu; } { - JMenuItem menu = new JMenuItem( "Go to previous" ); - menu.addActionListener( new ActionListener( ) - { + JMenuItem menu = new JMenuItem("Go to previous"); + menu.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { + public void actionPerformed(ActionEvent event) { m_controller.openPreviousReference(); } - } ); - menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_P, 0 ) ); - menu.setEnabled( false ); - popupMenu.add( menu ); + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0)); + menu.setEnabled(false); + popupMenu.add(menu); m_openPreviousMenu = menu; } { - JMenuItem menu = new JMenuItem( "Mark as deobfuscated" ); - menu.addActionListener( new ActionListener( ) - { + JMenuItem menu = new JMenuItem("Mark as deobfuscated"); + menu.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { + public void actionPerformed(ActionEvent event) { toggleMapping(); } - } ); - menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_T, 0 ) ); - menu.setEnabled( false ); - popupMenu.add( menu ); + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0)); + menu.setEnabled(false); + popupMenu.add(menu); m_toggleMappingMenu = menu; } // init inheritance panel m_inheritanceTree = new JTree(); - m_inheritanceTree.setModel( null ); - m_inheritanceTree.addMouseListener( new MouseAdapter( ) - { + m_inheritanceTree.setModel(null); + m_inheritanceTree.addMouseListener(new MouseAdapter() { @Override - public void mouseClicked( MouseEvent event ) - { - if( event.getClickCount() == 2 ) - { + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { // get the selected node TreePath path = m_inheritanceTree.getSelectionPath(); - if( path == null ) - { + if (path == null) { return; } Object node = path.getLastPathComponent(); - if( node instanceof ClassInheritanceTreeNode ) - { + if (node instanceof ClassInheritanceTreeNode) { ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode)node; - navigateTo( new ClassEntry( classNode.getObfClassName() ) ); - } - else if( node instanceof MethodInheritanceTreeNode ) - { + navigateTo(new ClassEntry(classNode.getObfClassName())); + } else if (node instanceof MethodInheritanceTreeNode) { MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode)node; - if( methodNode.isImplemented() ) - { - navigateTo( methodNode.getMethodEntry() ); + if (methodNode.isImplemented()) { + navigateTo(methodNode.getMethodEntry()); } } } } - } ); + }); JPanel inheritancePanel = new JPanel(); - inheritancePanel.setLayout( new BorderLayout() ); - inheritancePanel.add( new JScrollPane( m_inheritanceTree ) ); + inheritancePanel.setLayout(new BorderLayout()); + inheritancePanel.add(new JScrollPane(m_inheritanceTree)); // init implementations panel m_implementationsTree = new JTree(); - m_implementationsTree.setModel( null ); - m_implementationsTree.addMouseListener( new MouseAdapter( ) - { + m_implementationsTree.setModel(null); + m_implementationsTree.addMouseListener(new MouseAdapter() { @Override - public void mouseClicked( MouseEvent event ) - { - if( event.getClickCount() == 2 ) - { + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { // get the selected node TreePath path = m_implementationsTree.getSelectionPath(); - if( path == null ) - { + if (path == null) { return; } Object node = path.getLastPathComponent(); - if( node instanceof ClassImplementationsTreeNode ) - { + if (node instanceof ClassImplementationsTreeNode) { ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode)node; - navigateTo( classNode.getClassEntry() ); - } - else if( node instanceof MethodImplementationsTreeNode ) - { + navigateTo(classNode.getClassEntry()); + } else if (node instanceof MethodImplementationsTreeNode) { MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode)node; - navigateTo( methodNode.getMethodEntry() ); + navigateTo(methodNode.getMethodEntry()); } } } - } ); + }); JPanel implementationsPanel = new JPanel(); - implementationsPanel.setLayout( new BorderLayout() ); - implementationsPanel.add( new JScrollPane( m_implementationsTree ) ); + implementationsPanel.setLayout(new BorderLayout()); + implementationsPanel.add(new JScrollPane(m_implementationsTree)); // init call panel m_callsTree = new JTree(); - m_callsTree.setModel( null ); - m_callsTree.addMouseListener( new MouseAdapter( ) - { - @SuppressWarnings( "unchecked" ) + m_callsTree.setModel(null); + m_callsTree.addMouseListener(new MouseAdapter() { + @SuppressWarnings("unchecked") @Override - public void mouseClicked( MouseEvent event ) - { - if( event.getClickCount() == 2 ) - { + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { // get the selected node TreePath path = m_callsTree.getSelectionPath(); - if( path == null ) - { + if (path == null) { return; } Object node = path.getLastPathComponent(); - if( node instanceof ReferenceTreeNode ) - { + if (node instanceof ReferenceTreeNode) { ReferenceTreeNode referenceNode = ((ReferenceTreeNode)node); - if( referenceNode.getReference() != null ) - { - navigateTo( referenceNode.getReference() ); - } - else - { - navigateTo( referenceNode.getEntry() ); + if (referenceNode.getReference() != null) { + navigateTo(referenceNode.getReference()); + } else { + navigateTo(referenceNode.getEntry()); } } } } - } ); + }); m_tokens = new JList(); - m_tokens.setCellRenderer( new TokenListCellRenderer( m_controller ) ); - m_tokens.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); - m_tokens.setLayoutOrientation( JList.VERTICAL ); - m_tokens.addMouseListener( new MouseAdapter() - { + m_tokens.setCellRenderer(new TokenListCellRenderer(m_controller)); + m_tokens.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + m_tokens.setLayoutOrientation(JList.VERTICAL); + m_tokens.addMouseListener(new MouseAdapter() { @Override - public void mouseClicked( MouseEvent event ) - { - if( event.getClickCount() == 2 ) - { + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { Token selected = m_tokens.getSelectedValue(); - if( selected != null ) - { - showToken( selected ); + if (selected != null) { + showToken(selected); } } } - } ); - m_tokens.setPreferredSize( new Dimension( 0, 200 ) ); - m_tokens.setMinimumSize( new Dimension( 0, 200 ) ); - JSplitPane callPanel = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, new JScrollPane( m_callsTree ), new JScrollPane( m_tokens ) ); - callPanel.setResizeWeight( 1 ); // let the top side take all the slack + }); + m_tokens.setPreferredSize(new Dimension(0, 200)); + m_tokens.setMinimumSize(new Dimension(0, 200)); + JSplitPane callPanel = new JSplitPane( + JSplitPane.VERTICAL_SPLIT, + true, + new JScrollPane(m_callsTree), + new JScrollPane(m_tokens) + ); + callPanel.setResizeWeight(1); // let the top side take all the slack callPanel.resetToPreferredSizes(); // layout controls JPanel centerPanel = new JPanel(); - centerPanel.setLayout( new BorderLayout() ); - centerPanel.add( m_infoPanel, BorderLayout.NORTH ); - centerPanel.add( sourceScroller, BorderLayout.CENTER ); + centerPanel.setLayout(new BorderLayout()); + centerPanel.add(m_infoPanel, BorderLayout.NORTH); + centerPanel.add(sourceScroller, BorderLayout.CENTER); m_tabs = new JTabbedPane(); - m_tabs.setPreferredSize( new Dimension( 250, 0 ) ); - m_tabs.addTab( "Inheritance", inheritancePanel ); - m_tabs.addTab( "Implementations", implementationsPanel ); - m_tabs.addTab( "Call Graph", callPanel ); - JSplitPane splitRight = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, m_tabs ); - splitRight.setResizeWeight( 1 ); // let the left side take all the slack + m_tabs.setPreferredSize(new Dimension(250, 0)); + m_tabs.addTab("Inheritance", inheritancePanel); + m_tabs.addTab("Implementations", implementationsPanel); + m_tabs.addTab("Call Graph", callPanel); + JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, m_tabs); + splitRight.setResizeWeight(1); // let the left side take all the slack splitRight.resetToPreferredSizes(); - JSplitPane splitCenter = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, m_classesPanel, splitRight ); - splitCenter.setResizeWeight( 0 ); // let the right side take all the slack - pane.add( splitCenter, BorderLayout.CENTER ); + JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, m_classesPanel, splitRight); + splitCenter.setResizeWeight(0); // let the right side take all the slack + pane.add(splitCenter, BorderLayout.CENTER); // init menus JMenuBar menuBar = new JMenuBar(); - m_frame.setJMenuBar( menuBar ); + m_frame.setJMenuBar(menuBar); { - JMenu menu = new JMenu( "File" ); - menuBar.add( menu ); + JMenu menu = new JMenu("File"); + menuBar.add(menu); { - JMenuItem item = new JMenuItem( "Open Jar..." ); - menu.add( item ); - item.addActionListener( new ActionListener( ) - { + JMenuItem item = new JMenuItem("Open Jar..."); + menu.add(item); + item.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { - if( m_jarFileChooser.showOpenDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) - { + public void actionPerformed(ActionEvent event) { + if (m_jarFileChooser.showOpenDialog(m_frame) == JFileChooser.APPROVE_OPTION) { // load the jar in a separate thread - new Thread( ) - { + new Thread() { @Override - public void run( ) - { - try - { - m_controller.openJar( m_jarFileChooser.getSelectedFile() ); - } - catch( IOException ex ) - { - throw new Error( ex ); + public void run() { + try { + m_controller.openJar(m_jarFileChooser.getSelectedFile()); + } catch (IOException ex) { + throw new Error(ex); } } }.start(); } } - } ); + }); } { - JMenuItem item = new JMenuItem( "Close Jar" ); - menu.add( item ); - item.addActionListener( new ActionListener( ) - { + JMenuItem item = new JMenuItem("Close Jar"); + menu.add(item); + item.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { + public void actionPerformed(ActionEvent event) { m_controller.closeJar(); } - } ); + }); m_closeJarMenu = item; } menu.addSeparator(); { - JMenuItem item = new JMenuItem( "Open Mappings..." ); - menu.add( item ); - item.addActionListener( new ActionListener( ) - { + JMenuItem item = new JMenuItem("Open Mappings..."); + menu.add(item); + item.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { - if( m_mappingsFileChooser.showOpenDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) - { - try - { - m_controller.openMappings( m_mappingsFileChooser.getSelectedFile() ); - } - catch( IOException ex ) - { - throw new Error( ex ); - } - catch( MappingParseException ex ) - { - JOptionPane.showMessageDialog( m_frame, ex.getMessage() ); + public void actionPerformed(ActionEvent event) { + if (m_mappingsFileChooser.showOpenDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + try { + m_controller.openMappings(m_mappingsFileChooser.getSelectedFile()); + } catch (IOException ex) { + throw new Error(ex); + } catch (MappingParseException ex) { + JOptionPane.showMessageDialog(m_frame, ex.getMessage()); } } } - } ); + }); m_openMappingsMenu = item; } { - JMenuItem item = new JMenuItem( "Save Mappings" ); - menu.add( item ); - item.addActionListener( new ActionListener( ) - { + JMenuItem item = new JMenuItem("Save Mappings"); + menu.add(item); + item.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { - try - { - m_controller.saveMappings( m_mappingsFileChooser.getSelectedFile() ); - } - catch( IOException ex ) - { - throw new Error( ex ); + public void actionPerformed(ActionEvent event) { + try { + m_controller.saveMappings(m_mappingsFileChooser.getSelectedFile()); + } catch (IOException ex) { + throw new Error(ex); } } - } ); - item.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK ) ); + }); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); m_saveMappingsMenu = item; } { - JMenuItem item = new JMenuItem( "Save Mappings As..." ); - menu.add( item ); - item.addActionListener( new ActionListener( ) - { + JMenuItem item = new JMenuItem("Save Mappings As..."); + menu.add(item); + item.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { - if( m_mappingsFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) - { - try - { - m_controller.saveMappings( m_mappingsFileChooser.getSelectedFile() ); - m_saveMappingsMenu.setEnabled( true ); - } - catch( IOException ex ) - { - throw new Error( ex ); + public void actionPerformed(ActionEvent event) { + if (m_mappingsFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + try { + m_controller.saveMappings(m_mappingsFileChooser.getSelectedFile()); + m_saveMappingsMenu.setEnabled(true); + } catch (IOException ex) { + throw new Error(ex); } } } - } ); - item.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK ) ); + }); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); m_saveMappingsAsMenu = item; } { - JMenuItem item = new JMenuItem( "Close Mappings" ); - menu.add( item ); - item.addActionListener( new ActionListener( ) - { + JMenuItem item = new JMenuItem("Close Mappings"); + menu.add(item); + item.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { + public void actionPerformed(ActionEvent event) { m_controller.closeMappings(); } - } ); + }); m_closeMappingsMenu = item; } menu.addSeparator(); { - JMenuItem item = new JMenuItem( "Export Source..." ); - menu.add( item ); - item.addActionListener( new ActionListener( ) - { + JMenuItem item = new JMenuItem("Export Source..."); + menu.add(item); + item.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { - if( m_exportSourceFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) - { - m_controller.exportSource( m_exportSourceFileChooser.getSelectedFile() ); + public void actionPerformed(ActionEvent event) { + if (m_exportSourceFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + m_controller.exportSource(m_exportSourceFileChooser.getSelectedFile()); } } - } ); + }); m_exportSourceMenu = item; } { - JMenuItem item = new JMenuItem( "Export Jar..." ); - menu.add( item ); - item.addActionListener( new ActionListener( ) - { + JMenuItem item = new JMenuItem("Export Jar..."); + menu.add(item); + item.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { - if( m_exportJarFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) - { - m_controller.exportJar( m_exportJarFileChooser.getSelectedFile() ); + public void actionPerformed(ActionEvent event) { + if (m_exportJarFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + m_controller.exportJar(m_exportJarFileChooser.getSelectedFile()); } } - } ); + }); m_exportJarMenu = item; } menu.addSeparator(); { - JMenuItem item = new JMenuItem( "Exit" ); - menu.add( item ); - item.addActionListener( new ActionListener( ) - { + JMenuItem item = new JMenuItem("Exit"); + menu.add(item); + item.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { + public void actionPerformed(ActionEvent event) { close(); } - } ); + }); } } { - JMenu menu = new JMenu( "Help" ); - menuBar.add( menu ); + JMenu menu = new JMenu("Help"); + menuBar.add(menu); { - JMenuItem item = new JMenuItem( "About" ); - menu.add( item ); - item.addActionListener( new ActionListener( ) - { + JMenuItem item = new JMenuItem("About"); + menu.add(item); + item.addActionListener(new ActionListener() { @Override - public void actionPerformed( ActionEvent event ) - { - AboutDialog.show( m_frame ); + public void actionPerformed(ActionEvent event) { + AboutDialog.show(m_frame); } - } ); + }); } } // init state onCloseJar(); - m_frame.addWindowListener( new WindowAdapter( ) - { + m_frame.addWindowListener(new WindowAdapter() { @Override - public void windowClosing( WindowEvent event ) - { + public void windowClosing(WindowEvent event) { close(); } - } ); + }); // show the frame pane.doLayout(); - m_frame.setSize( 1024, 576 ); - m_frame.setMinimumSize( new Dimension( 640, 480 ) ); - m_frame.setVisible( true ); - m_frame.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE ); + m_frame.setSize(1024, 576); + m_frame.setMinimumSize(new Dimension(640, 480)); + m_frame.setVisible(true); + m_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); } - public JFrame getFrame( ) - { + public JFrame getFrame() { return m_frame; } - public GuiController getController( ) - { + public GuiController getController() { return m_controller; } - public void onStartOpenJar( ) - { + public void onStartOpenJar() { m_classesPanel.removeAll(); JPanel panel = new JPanel(); - panel.setLayout( new FlowLayout() ); - panel.add( new JLabel( "Loading..." ) ); - m_classesPanel.add( panel ); + panel.setLayout(new FlowLayout()); + panel.add(new JLabel("Loading...")); + m_classesPanel.add(panel); redraw(); } - public void onFinishOpenJar( String jarName ) - { + public void onFinishOpenJar(String jarName) { // update gui - m_frame.setTitle( Constants.Name + " - " + jarName ); + m_frame.setTitle(Constants.Name + " - " + jarName); m_classesPanel.removeAll(); - m_classesPanel.add( m_splitClasses ); - setSource( null ); + m_classesPanel.add(m_splitClasses); + setSource(null); // update menu - m_closeJarMenu.setEnabled( true ); - m_openMappingsMenu.setEnabled( true ); - m_saveMappingsMenu.setEnabled( false ); - m_saveMappingsAsMenu.setEnabled( true ); - m_closeMappingsMenu.setEnabled( true ); - m_exportSourceMenu.setEnabled( true ); - m_exportJarMenu.setEnabled( true ); + m_closeJarMenu.setEnabled(true); + m_openMappingsMenu.setEnabled(true); + m_saveMappingsMenu.setEnabled(false); + m_saveMappingsAsMenu.setEnabled(true); + m_closeMappingsMenu.setEnabled(true); + m_exportSourceMenu.setEnabled(true); + m_exportJarMenu.setEnabled(true); redraw(); } - public void onCloseJar( ) - { + public void onCloseJar() { // update gui - m_frame.setTitle( Constants.Name ); - setObfClasses( null ); - setDeobfClasses( null ); - setSource( null ); + m_frame.setTitle(Constants.Name); + setObfClasses(null); + setDeobfClasses(null); + setSource(null); m_classesPanel.removeAll(); // update menu - m_closeJarMenu.setEnabled( false ); - m_openMappingsMenu.setEnabled( false ); - m_saveMappingsMenu.setEnabled( false ); - m_saveMappingsAsMenu.setEnabled( false ); - m_closeMappingsMenu.setEnabled( false ); - m_exportSourceMenu.setEnabled( false ); - m_exportJarMenu.setEnabled( false ); + m_closeJarMenu.setEnabled(false); + m_openMappingsMenu.setEnabled(false); + m_saveMappingsMenu.setEnabled(false); + m_saveMappingsAsMenu.setEnabled(false); + m_closeMappingsMenu.setEnabled(false); + m_exportSourceMenu.setEnabled(false); + m_exportJarMenu.setEnabled(false); redraw(); } - public void setObfClasses( Collection obfClasses ) - { - m_obfClasses.setClasses( obfClasses ); + public void setObfClasses(Collection obfClasses) { + m_obfClasses.setClasses(obfClasses); } - public void setDeobfClasses( Collection deobfClasses ) - { - m_deobfClasses.setClasses( deobfClasses ); + public void setDeobfClasses(Collection deobfClasses) { + m_deobfClasses.setClasses(deobfClasses); } - public void setMappingsFile( File file ) - { - m_mappingsFileChooser.setSelectedFile( file ); - m_saveMappingsMenu.setEnabled( file != null ); + public void setMappingsFile(File file) { + m_mappingsFileChooser.setSelectedFile(file); + m_saveMappingsMenu.setEnabled(file != null); } - public void setSource( String source ) - { + public void setSource(String source) { m_editor.getHighlighter().removeAllHighlights(); - m_editor.setText( source ); + m_editor.setText(source); } - public void showToken( final Token token ) - { - if( token == null ) - { - throw new IllegalArgumentException( "Token cannot be null!" ); + public void showToken(final Token token) { + if (token == null) { + throw new IllegalArgumentException("Token cannot be null!"); } // set the caret position to the token - m_editor.setCaretPosition( token.start ); + m_editor.setCaretPosition(token.start); m_editor.grabFocus(); - try - { + try { // make sure the token is visible in the scroll window - Rectangle start = m_editor.modelToView( token.start ); - Rectangle end = m_editor.modelToView( token.end ); - final Rectangle show = start.union( end ); - show.grow( start.width*10, start.height*6 ); - SwingUtilities.invokeLater( new Runnable( ) - { + Rectangle start = m_editor.modelToView(token.start); + Rectangle end = m_editor.modelToView(token.end); + final Rectangle show = start.union(end); + show.grow(start.width * 10, start.height * 6); + SwingUtilities.invokeLater(new Runnable() { @Override - public void run( ) - { - m_editor.scrollRectToVisible( show ); + public void run() { + m_editor.scrollRectToVisible(show); } - } ); - } - catch( BadLocationException ex ) - { - throw new Error( ex ); + }); + } catch (BadLocationException ex) { + throw new Error(ex); } // highlight the token momentarily - final Timer timer = new Timer( 200, new ActionListener( ) - { + final Timer timer = new Timer(200, new ActionListener() { private int m_counter = 0; private Object m_highlight = null; @Override - public void actionPerformed( ActionEvent event ) - { - if( m_counter % 2 == 0 ) - { - try - { - m_highlight = m_editor.getHighlighter().addHighlight( token.start, token.end, m_selectionHighlightPainter ); - } - catch( BadLocationException ex ) - { + public void actionPerformed(ActionEvent event) { + if (m_counter % 2 == 0) { + try { + m_highlight = m_editor.getHighlighter().addHighlight(token.start, token.end, m_selectionHighlightPainter); + } catch (BadLocationException ex) { // don't care } - } - else if( m_highlight != null ) - { - m_editor.getHighlighter().removeHighlight( m_highlight ); + } else if (m_highlight != null) { + m_editor.getHighlighter().removeHighlight(m_highlight); } - if( m_counter++ > 6 ) - { + if (m_counter++ > 6) { Timer timer = (Timer)event.getSource(); timer.stop(); } } - } ); + }); timer.start(); redraw(); } - public void showTokens( Collection tokens ) - { - Vector sortedTokens = new Vector( tokens ); - Collections.sort( sortedTokens ); - if( sortedTokens.size() > 1 ) - { + public void showTokens(Collection tokens) { + Vector sortedTokens = new Vector(tokens); + Collections.sort(sortedTokens); + if (sortedTokens.size() > 1) { // sort the tokens and update the tokens panel - m_tokens.setListData( sortedTokens ); - m_tokens.setSelectedIndex( 0 ); - } - else - { - m_tokens.setListData( new Vector() ); + m_tokens.setListData(sortedTokens); + m_tokens.setSelectedIndex(0); + } else { + m_tokens.setListData(new Vector()); } // show the first token - showToken( sortedTokens.get( 0 ) ); + showToken(sortedTokens.get(0)); } - public void setHighlightedTokens( Iterable obfuscatedTokens, Iterable deobfuscatedTokens, Iterable otherTokens ) - { + public void setHighlightedTokens(Iterable obfuscatedTokens, Iterable deobfuscatedTokens, Iterable otherTokens) { + // remove any old highlighters m_editor.getHighlighter().removeAllHighlights(); // color things based on the index - if( obfuscatedTokens != null ) - { - setHighlightedTokens( obfuscatedTokens, m_obfuscatedHighlightPainter ); + if (obfuscatedTokens != null) { + setHighlightedTokens(obfuscatedTokens, m_obfuscatedHighlightPainter); } - if( deobfuscatedTokens != null ) - { - setHighlightedTokens( deobfuscatedTokens, m_deobfuscatedHighlightPainter ); + if (deobfuscatedTokens != null) { + setHighlightedTokens(deobfuscatedTokens, m_deobfuscatedHighlightPainter); } - if( otherTokens != null ) - { - setHighlightedTokens( otherTokens, m_otherHighlightPainter ); + if (otherTokens != null) { + setHighlightedTokens(otherTokens, m_otherHighlightPainter); } redraw(); } - private void setHighlightedTokens( Iterable tokens, Highlighter.HighlightPainter painter ) - { - for( Token token : tokens ) - { - try - { - m_editor.getHighlighter().addHighlight( token.start, token.end, painter ); - } - catch( BadLocationException ex ) - { - throw new IllegalArgumentException( ex ); + private void setHighlightedTokens(Iterable tokens, Highlighter.HighlightPainter painter) { + for (Token token : tokens) { + try { + m_editor.getHighlighter().addHighlight(token.start, token.end, painter); + } catch (BadLocationException ex) { + throw new IllegalArgumentException(ex); } } } - private void clearReference( ) - { + private void clearReference() { m_infoPanel.removeAll(); - JLabel label = new JLabel( "No identifier selected" ); - GuiTricks.unboldLabel( label ); - label.setHorizontalAlignment( JLabel.CENTER ); - m_infoPanel.add( label ); + JLabel label = new JLabel("No identifier selected"); + GuiTricks.unboldLabel(label); + label.setHorizontalAlignment(JLabel.CENTER); + m_infoPanel.add(label); redraw(); } - private void showReference( EntryReference reference ) - { - if( reference == null ) - { + private void showReference(EntryReference reference) { + if (reference == null) { clearReference(); return; } @@ -979,383 +848,300 @@ public class Gui m_reference = reference; m_infoPanel.removeAll(); - if( reference.entry instanceof ClassEntry ) - { - showClassEntry( (ClassEntry)m_reference.entry ); - } - else if( m_reference.entry instanceof FieldEntry ) - { - showFieldEntry( (FieldEntry)m_reference.entry ); - } - else if( m_reference.entry instanceof MethodEntry ) - { - showMethodEntry( (MethodEntry)m_reference.entry ); - } - else if( m_reference.entry instanceof ConstructorEntry ) - { - showConstructorEntry( (ConstructorEntry)m_reference.entry ); - } - else if( m_reference.entry instanceof ArgumentEntry ) - { - showArgumentEntry( (ArgumentEntry)m_reference.entry ); - } - else - { - throw new Error( "Unknown entry type: " + m_reference.entry.getClass().getName() ); + if (reference.entry instanceof ClassEntry) { + showClassEntry((ClassEntry)m_reference.entry); + } else if (m_reference.entry instanceof FieldEntry) { + showFieldEntry((FieldEntry)m_reference.entry); + } else if (m_reference.entry instanceof MethodEntry) { + showMethodEntry((MethodEntry)m_reference.entry); + } else if (m_reference.entry instanceof ConstructorEntry) { + showConstructorEntry((ConstructorEntry)m_reference.entry); + } else if (m_reference.entry instanceof ArgumentEntry) { + showArgumentEntry((ArgumentEntry)m_reference.entry); + } else { + throw new Error("Unknown entry type: " + m_reference.entry.getClass().getName()); } redraw(); } - private void showClassEntry( ClassEntry entry ) - { - addNameValue( m_infoPanel, "Class", entry.getName() ); + private void showClassEntry(ClassEntry entry) { + addNameValue(m_infoPanel, "Class", entry.getName()); } - private void showFieldEntry( FieldEntry entry ) - { - addNameValue( m_infoPanel, "Field", entry.getName() ); - addNameValue( m_infoPanel, "Class", entry.getClassEntry().getName() ); + private void showFieldEntry(FieldEntry entry) { + addNameValue(m_infoPanel, "Field", entry.getName()); + addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); } - private void showMethodEntry( MethodEntry entry ) - { - addNameValue( m_infoPanel, "Method", entry.getName() ); - addNameValue( m_infoPanel, "Class", entry.getClassEntry().getName() ); - addNameValue( m_infoPanel, "Signature", entry.getSignature() ); + private void showMethodEntry(MethodEntry entry) { + addNameValue(m_infoPanel, "Method", entry.getName()); + addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); + addNameValue(m_infoPanel, "Signature", entry.getSignature()); } - private void showConstructorEntry( ConstructorEntry entry ) - { - addNameValue( m_infoPanel, "Constructor", entry.getClassEntry().getName() ); - addNameValue( m_infoPanel, "Signature", entry.getSignature() ); + private void showConstructorEntry(ConstructorEntry entry) { + addNameValue(m_infoPanel, "Constructor", entry.getClassEntry().getName()); + addNameValue(m_infoPanel, "Signature", entry.getSignature()); } - private void showArgumentEntry( ArgumentEntry entry ) - { - addNameValue( m_infoPanel, "Argument", entry.getName() ); - addNameValue( m_infoPanel, "Class", entry.getClassEntry().getName() ); - addNameValue( m_infoPanel, "Method", entry.getBehaviorEntry().getName() ); - addNameValue( m_infoPanel, "Index", Integer.toString( entry.getIndex() ) ); + private void showArgumentEntry(ArgumentEntry entry) { + addNameValue(m_infoPanel, "Argument", entry.getName()); + addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); + addNameValue(m_infoPanel, "Method", entry.getBehaviorEntry().getName()); + addNameValue(m_infoPanel, "Index", Integer.toString(entry.getIndex())); } - private void addNameValue( JPanel container, String name, String value ) - { + private void addNameValue(JPanel container, String name, String value) { JPanel panel = new JPanel(); - panel.setLayout( new FlowLayout( FlowLayout.LEFT, 6, 0 ) ); - container.add( panel ); + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); + container.add(panel); - JLabel label = new JLabel( name + ":", JLabel.RIGHT ); - label.setPreferredSize( new Dimension( 100, label.getPreferredSize().height ) ); - panel.add( label ); + JLabel label = new JLabel(name + ":", JLabel.RIGHT); + label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); + panel.add(label); - panel.add( GuiTricks.unboldLabel( new JLabel( value, JLabel.LEFT ) ) ); + panel.add(GuiTricks.unboldLabel(new JLabel(value, JLabel.LEFT))); } - private void onCaretMove( int pos ) - { - Token token = m_controller.getToken( pos ); + private void onCaretMove(int pos) { + + Token token = m_controller.getToken(pos); boolean isToken = token != null; - m_reference = m_controller.getDeobfReference( token ); + m_reference = m_controller.getDeobfReference(token); boolean isClassEntry = isToken && m_reference.entry instanceof ClassEntry; boolean isFieldEntry = isToken && m_reference.entry instanceof FieldEntry; boolean isMethodEntry = isToken && m_reference.entry instanceof MethodEntry; boolean isConstructorEntry = isToken && m_reference.entry instanceof ConstructorEntry; - boolean isInJar = isToken && m_controller.entryIsInJar( m_reference.entry ); - boolean isRenameable = isToken && m_controller.referenceIsRenameable( m_reference ); + boolean isInJar = isToken && m_controller.entryIsInJar(m_reference.entry); + boolean isRenameable = isToken && m_controller.referenceIsRenameable(m_reference); - if( isToken ) - { - showReference( m_reference ); - } - else - { + if (isToken) { + showReference(m_reference); + } else { clearReference(); } - m_renameMenu.setEnabled( isRenameable && isToken ); - m_showInheritanceMenu.setEnabled( isClassEntry || isMethodEntry || isConstructorEntry ); - m_showImplementationsMenu.setEnabled( isClassEntry || isMethodEntry ); - m_showCallsMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ); - m_openEntryMenu.setEnabled( isInJar && ( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ) ); - m_openPreviousMenu.setEnabled( m_controller.hasPreviousLocation() ); - m_toggleMappingMenu.setEnabled( isRenameable && isToken ); + m_renameMenu.setEnabled(isRenameable && isToken); + m_showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); + m_showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); + m_showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); + m_openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); + m_openPreviousMenu.setEnabled(m_controller.hasPreviousLocation()); + m_toggleMappingMenu.setEnabled(isRenameable && isToken); - if( isToken && m_controller.entryHasDeobfuscatedName( m_reference.entry ) ) - { - m_toggleMappingMenu.setText( "Reset to obfuscated" ); - } - else - { - m_toggleMappingMenu.setText( "Mark as deobfuscated" ); + if (isToken && m_controller.entryHasDeobfuscatedName(m_reference.entry)) { + m_toggleMappingMenu.setText("Reset to obfuscated"); + } else { + m_toggleMappingMenu.setText("Mark as deobfuscated"); } } - private void navigateTo( Entry entry ) - { - if( !m_controller.entryIsInJar( entry ) ) - { + private void navigateTo(Entry entry) { + if (!m_controller.entryIsInJar(entry)) { // entry is not in the jar. Ignore it return; } - if( m_reference != null ) - { - m_controller.savePreviousReference( m_reference ); + if (m_reference != null) { + m_controller.savePreviousReference(m_reference); } - m_controller.openDeclaration( entry ); + m_controller.openDeclaration(entry); } - private void navigateTo( EntryReference reference ) - { - if( !m_controller.entryIsInJar( reference.getLocationClassEntry() ) ) - { + private void navigateTo(EntryReference reference) { + if (!m_controller.entryIsInJar(reference.getLocationClassEntry())) { // reference is not in the jar. Ignore it return; } - if( m_reference != null ) - { - m_controller.savePreviousReference( m_reference ); + if (m_reference != null) { + m_controller.savePreviousReference(m_reference); } - m_controller.openReference( reference ); + m_controller.openReference(reference); } - private void startRename( ) - { + private void startRename() { + // init the text box final JTextField text = new JTextField(); - text.setText( m_reference.getNamableName() ); - text.setPreferredSize( new Dimension( 360, text.getPreferredSize().height ) ); - text.addKeyListener( new KeyAdapter( ) - { + text.setText(m_reference.getNamableName()); + text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); + text.addKeyListener(new KeyAdapter() { @Override - public void keyPressed( KeyEvent event ) - { - switch( event.getKeyCode() ) - { + public void keyPressed(KeyEvent event) { + switch (event.getKeyCode()) { case KeyEvent.VK_ENTER: - finishRename( text, true ); + finishRename(text, true); break; case KeyEvent.VK_ESCAPE: - finishRename( text, false ); + finishRename(text, false); break; } } - } ); + }); // find the label with the name and replace it with the text box - JPanel panel = (JPanel)m_infoPanel.getComponent( 0 ); - panel.remove( panel.getComponentCount() - 1 ); - panel.add( text ); + JPanel panel = (JPanel)m_infoPanel.getComponent(0); + panel.remove(panel.getComponentCount() - 1); + panel.add(text); text.grabFocus(); text.selectAll(); redraw(); } - private void finishRename( JTextField text, boolean saveName ) - { + private void finishRename(JTextField text, boolean saveName) { String newName = text.getText(); - if( saveName && newName != null && newName.length() > 0 ) - { - try - { - m_controller.rename( m_reference, newName ); - } - catch( IllegalNameException ex ) - { - text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) ); - text.setToolTipText( ex.getReason() ); - GuiTricks.showToolTipNow( text ); + if (saveName && newName != null && newName.length() > 0) { + try { + m_controller.rename(m_reference, newName); + } catch (IllegalNameException ex) { + text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); + text.setToolTipText(ex.getReason()); + GuiTricks.showToolTipNow(text); } return; } // abort the rename - JPanel panel = (JPanel)m_infoPanel.getComponent( 0 ); - panel.remove( panel.getComponentCount() - 1 ); - panel.add( GuiTricks.unboldLabel( new JLabel( m_reference.getNamableName(), JLabel.LEFT ) ) ); + JPanel panel = (JPanel)m_infoPanel.getComponent(0); + panel.remove(panel.getComponentCount() - 1); + panel.add(GuiTricks.unboldLabel(new JLabel(m_reference.getNamableName(), JLabel.LEFT))); m_editor.grabFocus(); redraw(); } - private void showInheritance( ) - { - if( m_reference == null ) - { + private void showInheritance() { + + if (m_reference == null) { return; } - m_inheritanceTree.setModel( null ); + m_inheritanceTree.setModel(null); - if( m_reference.entry instanceof ClassEntry ) - { + if (m_reference.entry instanceof ClassEntry) { // get the class inheritance - ClassInheritanceTreeNode classNode = m_controller.getClassInheritance( (ClassEntry)m_reference.entry ); + ClassInheritanceTreeNode classNode = m_controller.getClassInheritance((ClassEntry)m_reference.entry); // show the tree at the root - TreePath path = getPathToRoot( classNode ); - m_inheritanceTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) ); - m_inheritanceTree.expandPath( path ); - m_inheritanceTree.setSelectionRow( m_inheritanceTree.getRowForPath( path ) ); - } - else if( m_reference.entry instanceof MethodEntry ) - { + TreePath path = getPathToRoot(classNode); + m_inheritanceTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0))); + m_inheritanceTree.expandPath(path); + m_inheritanceTree.setSelectionRow(m_inheritanceTree.getRowForPath(path)); + } else if (m_reference.entry instanceof MethodEntry) { // get the method inheritance - MethodInheritanceTreeNode classNode = m_controller.getMethodInheritance( (MethodEntry)m_reference.entry ); + MethodInheritanceTreeNode classNode = m_controller.getMethodInheritance((MethodEntry)m_reference.entry); // show the tree at the root - TreePath path = getPathToRoot( classNode ); - m_inheritanceTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) ); - m_inheritanceTree.expandPath( path ); - m_inheritanceTree.setSelectionRow( m_inheritanceTree.getRowForPath( path ) ); + TreePath path = getPathToRoot(classNode); + m_inheritanceTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0))); + m_inheritanceTree.expandPath(path); + m_inheritanceTree.setSelectionRow(m_inheritanceTree.getRowForPath(path)); } - m_tabs.setSelectedIndex( 0 ); + m_tabs.setSelectedIndex(0); redraw(); } - private void showImplementations( ) - { - if( m_reference == null ) - { + private void showImplementations() { + + if (m_reference == null) { return; } - m_implementationsTree.setModel( null ); + m_implementationsTree.setModel(null); - if( m_reference.entry instanceof ClassEntry ) - { + if (m_reference.entry instanceof ClassEntry) { // get the class implementations - ClassImplementationsTreeNode node = m_controller.getClassImplementations( (ClassEntry)m_reference.entry ); - if( node != null ) - { + ClassImplementationsTreeNode node = m_controller.getClassImplementations((ClassEntry)m_reference.entry); + if (node != null) { // show the tree at the root - TreePath path = getPathToRoot( node ); - m_implementationsTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) ); - m_implementationsTree.expandPath( path ); - m_implementationsTree.setSelectionRow( m_implementationsTree.getRowForPath( path ) ); + TreePath path = getPathToRoot(node); + m_implementationsTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0))); + m_implementationsTree.expandPath(path); + m_implementationsTree.setSelectionRow(m_implementationsTree.getRowForPath(path)); } - } - else if( m_reference.entry instanceof MethodEntry ) - { + } else if (m_reference.entry instanceof MethodEntry) { // get the method implementations - MethodImplementationsTreeNode node = m_controller.getMethodImplementations( (MethodEntry)m_reference.entry ); - if( node != null ) - { + MethodImplementationsTreeNode node = m_controller.getMethodImplementations((MethodEntry)m_reference.entry); + if (node != null) { // show the tree at the root - TreePath path = getPathToRoot( node ); - m_implementationsTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) ); - m_implementationsTree.expandPath( path ); - m_implementationsTree.setSelectionRow( m_implementationsTree.getRowForPath( path ) ); + TreePath path = getPathToRoot(node); + m_implementationsTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0))); + m_implementationsTree.expandPath(path); + m_implementationsTree.setSelectionRow(m_implementationsTree.getRowForPath(path)); } } - m_tabs.setSelectedIndex( 1 ); + m_tabs.setSelectedIndex(1); redraw(); } - private void showCalls( ) - { - if( m_reference == null ) - { + private void showCalls() { + + if (m_reference == null) { return; } - if( m_reference.entry instanceof ClassEntry ) - { + if (m_reference.entry instanceof ClassEntry) { // look for calls to the default constructor // TODO: get a list of all the constructors and find calls to all of them - BehaviorReferenceTreeNode node = m_controller.getMethodReferences( new ConstructorEntry( (ClassEntry)m_reference.entry, "()V" ) ); - m_callsTree.setModel( new DefaultTreeModel( node ) ); - } - else if( m_reference.entry instanceof FieldEntry ) - { - FieldReferenceTreeNode node = m_controller.getFieldReferences( (FieldEntry)m_reference.entry ); - m_callsTree.setModel( new DefaultTreeModel( node ) ); - } - else if( m_reference.entry instanceof MethodEntry ) - { - BehaviorReferenceTreeNode node = m_controller.getMethodReferences( (MethodEntry)m_reference.entry ); - m_callsTree.setModel( new DefaultTreeModel( node ) ); - } - else if( m_reference.entry instanceof ConstructorEntry ) - { - BehaviorReferenceTreeNode node = m_controller.getMethodReferences( (ConstructorEntry)m_reference.entry ); - m_callsTree.setModel( new DefaultTreeModel( node ) ); + BehaviorReferenceTreeNode node = m_controller.getMethodReferences(new ConstructorEntry((ClassEntry)m_reference.entry, "()V")); + m_callsTree.setModel(new DefaultTreeModel(node)); + } else if (m_reference.entry instanceof FieldEntry) { + FieldReferenceTreeNode node = m_controller.getFieldReferences((FieldEntry)m_reference.entry); + m_callsTree.setModel(new DefaultTreeModel(node)); + } else if (m_reference.entry instanceof MethodEntry) { + BehaviorReferenceTreeNode node = m_controller.getMethodReferences((MethodEntry)m_reference.entry); + m_callsTree.setModel(new DefaultTreeModel(node)); + } else if (m_reference.entry instanceof ConstructorEntry) { + BehaviorReferenceTreeNode node = m_controller.getMethodReferences((ConstructorEntry)m_reference.entry); + m_callsTree.setModel(new DefaultTreeModel(node)); } - m_tabs.setSelectedIndex( 2 ); + m_tabs.setSelectedIndex(2); redraw(); } - private void toggleMapping() - { - if( m_controller.entryHasDeobfuscatedName( m_reference.entry ) ) - { - m_controller.removeMapping( m_reference ); - } - else - { - m_controller.markAsDeobfuscated( m_reference ); + private void toggleMapping() { + if (m_controller.entryHasDeobfuscatedName(m_reference.entry)) { + m_controller.removeMapping(m_reference); + } else { + m_controller.markAsDeobfuscated(m_reference); } } - private TreePath getPathToRoot( TreeNode node ) - { + private TreePath getPathToRoot(TreeNode node) { List nodes = Lists.newArrayList(); TreeNode n = node; - do - { - nodes.add( n ); + do { + nodes.add(n); n = n.getParent(); - } - while( n != null ); - Collections.reverse( nodes ); - return new TreePath( nodes.toArray() ); + } while (n != null); + Collections.reverse(nodes); + return new TreePath(nodes.toArray()); } - private void close( ) - { - if( !m_controller.isDirty() ) - { + private void close() { + if (!m_controller.isDirty()) { // everything is saved, we can exit safely m_frame.dispose(); - } - else - { + } else { // ask to save before closing - String[] options = { - "Save and exit", - "Discard changes", - "Cancel" - }; - int response = JOptionPane.showOptionDialog( - m_frame, - "Your mappings have not been saved yet. Do you want to save?", - "Save your changes?", - JOptionPane.YES_NO_CANCEL_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - options[2] - ); - switch( response ) - { + String[] options = { "Save and exit", "Discard changes", "Cancel" }; + int response = JOptionPane.showOptionDialog(m_frame, "Your mappings have not been saved yet. Do you want to save?", "Save your changes?", JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, null, options, options[2]); + switch (response) { case JOptionPane.YES_OPTION: // save and exit - if( m_mappingsFileChooser.getSelectedFile() != null || m_mappingsFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) - { - try - { - m_controller.saveMappings( m_mappingsFileChooser.getSelectedFile() ); + if (m_mappingsFileChooser.getSelectedFile() != null || m_mappingsFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + try { + m_controller.saveMappings(m_mappingsFileChooser.getSelectedFile()); m_frame.dispose(); - } - catch( IOException ex ) - { - throw new Error( ex ); + } catch (IOException ex) { + throw new Error(ex); } } break; @@ -1364,14 +1150,13 @@ public class Gui // don't save, exit m_frame.dispose(); break; - + // cancel means do nothing } } } - - private void redraw( ) - { + + private void redraw() { m_frame.validate(); m_frame.repaint(); } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 2862ebed..908c16fa 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -44,8 +44,8 @@ import cuchaz.enigma.mapping.MappingsWriter; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.TranslationDirection; -public class GuiController -{ +public class GuiController { + private Deobfuscator m_deobfuscator; private Gui m_gui; private SourceIndex m_index; @@ -53,8 +53,7 @@ public class GuiController private boolean m_isDirty; private Deque> m_referenceStack; - public GuiController( Gui gui ) - { + public GuiController(Gui gui) { m_gui = gui; m_deobfuscator = null; m_index = null; @@ -63,358 +62,292 @@ public class GuiController m_referenceStack = Queues.newArrayDeque(); } - public boolean isDirty( ) - { + public boolean isDirty() { return m_isDirty; } - public void openJar( final File file ) - throws IOException - { + public void openJar(final File file) throws IOException { m_gui.onStartOpenJar(); - m_deobfuscator = new Deobfuscator( file ); - m_gui.onFinishOpenJar( m_deobfuscator.getJarName() ); + m_deobfuscator = new Deobfuscator(file); + m_gui.onFinishOpenJar(m_deobfuscator.getJarName()); refreshClasses(); } - public void closeJar( ) - { + public void closeJar() { m_deobfuscator = null; m_gui.onCloseJar(); } - public void openMappings( File file ) - throws IOException, MappingParseException - { - FileReader in = new FileReader( file ); - m_deobfuscator.setMappings( new MappingsReader().read( in ) ); + public void openMappings(File file) throws IOException, MappingParseException { + FileReader in = new FileReader(file); + m_deobfuscator.setMappings(new MappingsReader().read(in)); in.close(); m_isDirty = false; - m_gui.setMappingsFile( file ); + m_gui.setMappingsFile(file); refreshClasses(); refreshCurrentClass(); } - - public void saveMappings( File file ) - throws IOException - { - FileWriter out = new FileWriter( file ); - new MappingsWriter().write( out, m_deobfuscator.getMappings() ); + + public void saveMappings(File file) throws IOException { + FileWriter out = new FileWriter(file); + new MappingsWriter().write(out, m_deobfuscator.getMappings()); out.close(); m_isDirty = false; } - - public void closeMappings( ) - { - m_deobfuscator.setMappings( null ); - m_gui.setMappingsFile( null ); + + public void closeMappings() { + m_deobfuscator.setMappings(null); + m_gui.setMappingsFile(null); refreshClasses(); refreshCurrentClass(); } - public void exportSource( final File dirOut ) - { - ProgressDialog.runInThread( m_gui.getFrame(), new ProgressRunnable( ) - { + public void exportSource(final File dirOut) { + ProgressDialog.runInThread(m_gui.getFrame(), new ProgressRunnable() { @Override - public void run( ProgressListener progress ) - throws Exception - { - m_deobfuscator.writeSources( dirOut, progress ); + public void run(ProgressListener progress) throws Exception { + m_deobfuscator.writeSources(dirOut, progress); } - } ); + }); } - public void exportJar( final File fileOut ) - { - ProgressDialog.runInThread( m_gui.getFrame(), new ProgressRunnable( ) - { + public void exportJar(final File fileOut) { + ProgressDialog.runInThread(m_gui.getFrame(), new ProgressRunnable() { @Override - public void run( ProgressListener progress ) - { - m_deobfuscator.writeJar( fileOut, progress ); + public void run(ProgressListener progress) { + m_deobfuscator.writeJar(fileOut, progress); } - } ); + }); } - public Token getToken( int pos ) - { - if( m_index == null ) - { + public Token getToken(int pos) { + if (m_index == null) { return null; } - return m_index.getReferenceToken( pos ); + return m_index.getReferenceToken(pos); } - public EntryReference getDeobfReference( Token token ) - { - if( m_index == null ) - { + public EntryReference getDeobfReference(Token token) { + if (m_index == null) { return null; } - return m_index.getDeobfReference( token ); + return m_index.getDeobfReference(token); } - public ReadableToken getReadableToken( Token token ) - { - if( m_index == null ) - { + public ReadableToken getReadableToken(Token token) { + if (m_index == null) { return null; } return new ReadableToken( - m_index.getLineNumber( token.start ), - m_index.getColumnNumber( token.start ), - m_index.getColumnNumber( token.end ) + m_index.getLineNumber(token.start), + m_index.getColumnNumber(token.start), + m_index.getColumnNumber(token.end) ); } - public boolean entryHasDeobfuscatedName( Entry deobfEntry ) - { - return m_deobfuscator.hasDeobfuscatedName( m_deobfuscator.obfuscateEntry( deobfEntry ) ); + public boolean entryHasDeobfuscatedName(Entry deobfEntry) { + return m_deobfuscator.hasDeobfuscatedName(m_deobfuscator.obfuscateEntry(deobfEntry)); } - public boolean entryIsInJar( Entry deobfEntry ) - { - return m_deobfuscator.isObfuscatedIdentifier( m_deobfuscator.obfuscateEntry( deobfEntry ) ); + public boolean entryIsInJar(Entry deobfEntry) { + return m_deobfuscator.isObfuscatedIdentifier(m_deobfuscator.obfuscateEntry(deobfEntry)); } - public boolean referenceIsRenameable( EntryReference deobfReference ) - { - return m_deobfuscator.isRenameable( m_deobfuscator.obfuscateReference( deobfReference ) ); + public boolean referenceIsRenameable(EntryReference deobfReference) { + return m_deobfuscator.isRenameable(m_deobfuscator.obfuscateReference(deobfReference)); } - public ClassInheritanceTreeNode getClassInheritance( ClassEntry deobfClassEntry ) - { - ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry( deobfClassEntry ); + public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { + ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry(deobfClassEntry); ClassInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getClassInheritance( - m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry ); - return ClassInheritanceTreeNode.findNode( rootNode, obfClassEntry ); + return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); } - public ClassImplementationsTreeNode getClassImplementations( ClassEntry deobfClassEntry ) - { - ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry( deobfClassEntry ); + public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { + ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry(deobfClassEntry); return m_deobfuscator.getJarIndex().getClassImplementations( - m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry ); } - public MethodInheritanceTreeNode getMethodInheritance( MethodEntry deobfMethodEntry ) - { - MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry( deobfMethodEntry ); + public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { + MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry(deobfMethodEntry); MethodInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodInheritance( - m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry ); - return MethodInheritanceTreeNode.findNode( rootNode, obfMethodEntry ); + return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); } - public MethodImplementationsTreeNode getMethodImplementations( MethodEntry deobfMethodEntry ) - { - MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry( deobfMethodEntry ); + public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { + MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry(deobfMethodEntry); MethodImplementationsTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodImplementations( - m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry ); - if( rootNode == null ) - { + if (rootNode == null) { return null; } - return MethodImplementationsTreeNode.findNode( rootNode, obfMethodEntry ); + return MethodImplementationsTreeNode.findNode(rootNode, obfMethodEntry); } - public FieldReferenceTreeNode getFieldReferences( FieldEntry deobfFieldEntry ) - { - FieldEntry obfFieldEntry = m_deobfuscator.obfuscateEntry( deobfFieldEntry ); + public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { + FieldEntry obfFieldEntry = m_deobfuscator.obfuscateEntry(deobfFieldEntry); FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode( - m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfFieldEntry ); - rootNode.load( m_deobfuscator.getJarIndex(), true ); + rootNode.load(m_deobfuscator.getJarIndex(), true); return rootNode; } - public BehaviorReferenceTreeNode getMethodReferences( BehaviorEntry deobfBehaviorEntry ) - { - BehaviorEntry obfBehaviorEntry = m_deobfuscator.obfuscateEntry( deobfBehaviorEntry ); + public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) { + BehaviorEntry obfBehaviorEntry = m_deobfuscator.obfuscateEntry(deobfBehaviorEntry); BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode( - m_deobfuscator.getTranslator( TranslationDirection.Deobfuscating ), + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfBehaviorEntry ); - rootNode.load( m_deobfuscator.getJarIndex(), true ); + rootNode.load(m_deobfuscator.getJarIndex(), true); return rootNode; } - public void rename( EntryReference deobfReference, String newName ) - { - EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); - m_deobfuscator.rename( obfReference.getNameableEntry(), newName ); + public void rename(EntryReference deobfReference, String newName) { + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + m_deobfuscator.rename(obfReference.getNameableEntry(), newName); m_isDirty = true; refreshClasses(); - refreshCurrentClass( obfReference ); + refreshCurrentClass(obfReference); } - public void removeMapping( EntryReference deobfReference ) - { - EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); - m_deobfuscator.removeMapping( obfReference.getNameableEntry() ); + public void removeMapping(EntryReference deobfReference) { + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + m_deobfuscator.removeMapping(obfReference.getNameableEntry()); m_isDirty = true; refreshClasses(); - refreshCurrentClass( obfReference ); + refreshCurrentClass(obfReference); } - public void markAsDeobfuscated( EntryReference deobfReference ) - { - EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); - m_deobfuscator.markAsDeobfuscated( obfReference.getNameableEntry() ); + public void markAsDeobfuscated(EntryReference deobfReference) { + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + m_deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); m_isDirty = true; refreshClasses(); - refreshCurrentClass( obfReference ); + refreshCurrentClass(obfReference); } - public void openDeclaration( Entry deobfEntry ) - { - if( deobfEntry == null ) - { - throw new IllegalArgumentException( "Entry cannot be null!" ); + public void openDeclaration(Entry deobfEntry) { + if (deobfEntry == null) { + throw new IllegalArgumentException("Entry cannot be null!"); } - openReference( new EntryReference( deobfEntry, deobfEntry.getName() ) ); + openReference(new EntryReference(deobfEntry, deobfEntry.getName())); } - public void openReference( EntryReference deobfReference ) - { - if( deobfReference == null ) - { - throw new IllegalArgumentException( "Reference cannot be null!" ); + public void openReference(EntryReference deobfReference) { + if (deobfReference == null) { + throw new IllegalArgumentException("Reference cannot be null!"); } // get the reference target class - EntryReference obfReference = m_deobfuscator.obfuscateReference( deobfReference ); + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOuterClassEntry(); - if( !m_deobfuscator.isObfuscatedIdentifier( obfClassEntry ) ) - { - throw new IllegalArgumentException( "Obfuscated class " + obfClassEntry + " was not found in the jar!" ); + if (!m_deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { + throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); } - if( m_currentObfClass == null || !m_currentObfClass.equals( obfClassEntry ) ) - { + if (m_currentObfClass == null || !m_currentObfClass.equals(obfClassEntry)) { // deobfuscate the class, then navigate to the reference m_currentObfClass = obfClassEntry; - deobfuscate( m_currentObfClass, obfReference ); - } - else - { - showReference( obfReference ); + deobfuscate(m_currentObfClass, obfReference); + } else { + showReference(obfReference); } } - private void showReference( EntryReference obfReference ) - { - EntryReference deobfReference = m_deobfuscator.deobfuscateReference( obfReference ); - Collection tokens = m_index.getReferenceTokens( deobfReference ); - if( tokens.isEmpty() ) - { + private void showReference(EntryReference obfReference) { + EntryReference deobfReference = m_deobfuscator.deobfuscateReference(obfReference); + Collection tokens = m_index.getReferenceTokens(deobfReference); + if (tokens.isEmpty()) { // DEBUG - System.err.println( String.format( "WARNING: no tokens found for %s in %s", deobfReference, m_currentObfClass ) ); - } - else - { - m_gui.showTokens( tokens ); + System.err.println(String.format("WARNING: no tokens found for %s in %s", deobfReference, m_currentObfClass)); + } else { + m_gui.showTokens(tokens); } } - public void savePreviousReference( EntryReference deobfReference ) - { - m_referenceStack.push( m_deobfuscator.obfuscateReference( deobfReference ) ); + public void savePreviousReference(EntryReference deobfReference) { + m_referenceStack.push(m_deobfuscator.obfuscateReference(deobfReference)); } - public void openPreviousReference( ) - { - if( hasPreviousLocation() ) - { - openReference( m_deobfuscator.deobfuscateReference( m_referenceStack.pop() ) ); + public void openPreviousReference() { + if (hasPreviousLocation()) { + openReference(m_deobfuscator.deobfuscateReference(m_referenceStack.pop())); } } - public boolean hasPreviousLocation( ) - { + public boolean hasPreviousLocation() { return !m_referenceStack.isEmpty(); } - private void refreshClasses( ) - { + private void refreshClasses() { List obfClasses = Lists.newArrayList(); List deobfClasses = Lists.newArrayList(); - m_deobfuscator.getSeparatedClasses( obfClasses, deobfClasses ); - m_gui.setObfClasses( obfClasses ); - m_gui.setDeobfClasses( deobfClasses ); + m_deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); + m_gui.setObfClasses(obfClasses); + m_gui.setDeobfClasses(deobfClasses); } - private void refreshCurrentClass( ) - { - refreshCurrentClass( null ); + private void refreshCurrentClass() { + refreshCurrentClass(null); } - private void refreshCurrentClass( EntryReference obfReference ) - { - if( m_currentObfClass != null ) - { - deobfuscate( m_currentObfClass, obfReference ); + private void refreshCurrentClass(EntryReference obfReference) { + if (m_currentObfClass != null) { + deobfuscate(m_currentObfClass, obfReference); } } - private void deobfuscate( final ClassEntry classEntry, final EntryReference obfReference ) - { - m_gui.setSource( "(deobfuscating...)" ); + private void deobfuscate(final ClassEntry classEntry, final EntryReference obfReference) { + + m_gui.setSource("(deobfuscating...)"); // run the deobfuscator in a separate thread so we don't block the GUI event queue - new Thread( ) - { + new Thread() { @Override - public void run( ) - { + public void run() { // decompile,deobfuscate the bytecode - CompilationUnit sourceTree = m_deobfuscator.getSourceTree( classEntry.getClassName() ); - if( sourceTree == null ) - { + CompilationUnit sourceTree = m_deobfuscator.getSourceTree(classEntry.getClassName()); + if (sourceTree == null) { // decompilation of this class is not supported m_gui.setSource("Unable to find class: " + classEntry); return; } - String source = m_deobfuscator.getSource( sourceTree ); - m_index = m_deobfuscator.getSourceIndex( sourceTree, source ); - m_gui.setSource( m_index.getSource() ); - if( obfReference != null ) - { - showReference( obfReference ); + String source = m_deobfuscator.getSource(sourceTree); + m_index = m_deobfuscator.getSourceIndex(sourceTree, source); + m_gui.setSource(m_index.getSource()); + if (obfReference != null) { + showReference(obfReference); } // set the highlighted tokens List obfuscatedTokens = Lists.newArrayList(); List deobfuscatedTokens = Lists.newArrayList(); List otherTokens = Lists.newArrayList(); - for( Token token : m_index.referenceTokens() ) - { - EntryReference reference = m_index.getDeobfReference( token ); - if( referenceIsRenameable( reference ) ) - { - if( entryHasDeobfuscatedName( reference.getNameableEntry() ) ) - { - deobfuscatedTokens.add( token ); + for (Token token : m_index.referenceTokens()) { + EntryReference reference = m_index.getDeobfReference(token); + if (referenceIsRenameable(reference)) { + if (entryHasDeobfuscatedName(reference.getNameableEntry())) { + deobfuscatedTokens.add(token); + } else { + obfuscatedTokens.add(token); } - else - { - obfuscatedTokens.add( token ); - } - } - else - { - otherTokens.add( token ); + } else { + otherTokens.add(token); } } - m_gui.setHighlightedTokens( obfuscatedTokens, deobfuscatedTokens, otherTokens ); + m_gui.setHighlightedTokens(obfuscatedTokens, deobfuscatedTokens, otherTokens); } }.start(); } diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java index 9b889ef4..df9e2215 100644 --- a/src/cuchaz/enigma/gui/GuiTricks.java +++ b/src/cuchaz/enigma/gui/GuiTricks.java @@ -17,27 +17,20 @@ import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.ToolTipManager; -public class GuiTricks -{ - public static JLabel unboldLabel( JLabel label ) - { +public class GuiTricks { + + public static JLabel unboldLabel(JLabel label) { Font font = label.getFont(); - label.setFont( font.deriveFont( font.getStyle() & ~Font.BOLD ) ); + label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); return label; } - public static void showToolTipNow( JComponent component ) - { + public static void showToolTipNow(JComponent component) { // HACKHACK: trick the tooltip manager into showing the tooltip right now ToolTipManager manager = ToolTipManager.sharedInstance(); int oldDelay = manager.getInitialDelay(); - manager.setInitialDelay( 0 ); - manager.mouseMoved( new MouseEvent( - component, - MouseEvent.MOUSE_MOVED, - System.currentTimeMillis(), - 0, 0, 0, 0, false - ) ); - manager.setInitialDelay( oldDelay ); + manager.setInitialDelay(0); + manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); + manager.setInitialDelay(oldDelay); } } diff --git a/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java index 724be34e..177835f4 100644 --- a/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java +++ b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java @@ -12,11 +12,10 @@ package cuchaz.enigma.gui; import java.awt.Color; -public class ObfuscatedHighlightPainter extends BoxHighlightPainter -{ - public ObfuscatedHighlightPainter( ) - { +public class ObfuscatedHighlightPainter extends BoxHighlightPainter { + + public ObfuscatedHighlightPainter() { // red ish - super( new Color( 255, 220, 220 ), new Color( 160, 80, 80 ) ); + super(new Color(255, 220, 220), new Color(160, 80, 80)); } } diff --git a/src/cuchaz/enigma/gui/OtherHighlightPainter.java b/src/cuchaz/enigma/gui/OtherHighlightPainter.java index 78de7325..4e9c8709 100644 --- a/src/cuchaz/enigma/gui/OtherHighlightPainter.java +++ b/src/cuchaz/enigma/gui/OtherHighlightPainter.java @@ -12,11 +12,10 @@ package cuchaz.enigma.gui; import java.awt.Color; -public class OtherHighlightPainter extends BoxHighlightPainter -{ - public OtherHighlightPainter( ) - { +public class OtherHighlightPainter extends BoxHighlightPainter { + + public OtherHighlightPainter() { // grey - super( null, new Color( 180, 180, 180 ) ); + super(null, new Color(180, 180, 180)); } } diff --git a/src/cuchaz/enigma/gui/ProgressDialog.java b/src/cuchaz/enigma/gui/ProgressDialog.java index 7f954314..b864fdbf 100644 --- a/src/cuchaz/enigma/gui/ProgressDialog.java +++ b/src/cuchaz/enigma/gui/ProgressDialog.java @@ -25,89 +25,79 @@ import javax.swing.WindowConstants; import cuchaz.enigma.Constants; import cuchaz.enigma.Deobfuscator.ProgressListener; -public class ProgressDialog implements ProgressListener, AutoCloseable -{ +public class ProgressDialog implements ProgressListener, AutoCloseable { + private JFrame m_frame; private JLabel m_title; private JLabel m_text; private JProgressBar m_progress; - public ProgressDialog( JFrame parent ) - { + public ProgressDialog(JFrame parent) { + // init frame - m_frame = new JFrame( Constants.Name + " - Operation in progress" ); + m_frame = new JFrame(Constants.Name + " - Operation in progress"); final Container pane = m_frame.getContentPane(); FlowLayout layout = new FlowLayout(); - layout.setAlignment( FlowLayout.LEFT ); - pane.setLayout( layout ); + layout.setAlignment(FlowLayout.LEFT); + pane.setLayout(layout); m_title = new JLabel(); - pane.add( m_title ); + pane.add(m_title); // set up the progress bar JPanel panel = new JPanel(); - pane.add( panel ); - panel.setLayout( new BorderLayout() ); - m_text = GuiTricks.unboldLabel( new JLabel() ); + pane.add(panel); + panel.setLayout(new BorderLayout()); + m_text = GuiTricks.unboldLabel(new JLabel()); m_progress = new JProgressBar(); - m_text.setBorder( BorderFactory.createEmptyBorder( 0, 0, 10, 0 ) ); - panel.add( m_text, BorderLayout.NORTH ); - panel.add( m_progress, BorderLayout.CENTER ); - panel.setPreferredSize( new Dimension( 360, 50 ) ); + m_text.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); + panel.add(m_text, BorderLayout.NORTH); + panel.add(m_progress, BorderLayout.CENTER); + panel.setPreferredSize(new Dimension(360, 50)); // show the frame pane.doLayout(); - m_frame.setSize( 400, 120 ); - m_frame.setResizable( false ); - m_frame.setLocationRelativeTo( parent ); - m_frame.setVisible( true ); - m_frame.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE ); + m_frame.setSize(400, 120); + m_frame.setResizable(false); + m_frame.setLocationRelativeTo(parent); + m_frame.setVisible(true); + m_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); } - public void close( ) - { + public void close() { m_frame.dispose(); } - + @Override - public void init( int totalWork, String title ) - { - m_title.setText( title ); - m_progress.setMinimum( 0 ); - m_progress.setMaximum( totalWork ); - m_progress.setValue( 0 ); + public void init(int totalWork, String title) { + m_title.setText(title); + m_progress.setMinimum(0); + m_progress.setMaximum(totalWork); + m_progress.setValue(0); } - + @Override - public void onProgress( int numDone, String message ) - { - m_text.setText( message ); - m_progress.setValue( numDone ); + public void onProgress(int numDone, String message) { + m_text.setText(message); + m_progress.setValue(numDone); // update the frame m_frame.validate(); m_frame.repaint(); } - public static interface ProgressRunnable - { - void run( ProgressListener listener ) throws Exception; + public static interface ProgressRunnable { + void run(ProgressListener listener) throws Exception; } - public static void runInThread( final JFrame parent, final ProgressRunnable runnable ) - { - new Thread( ) - { + public static void runInThread(final JFrame parent, final ProgressRunnable runnable) { + new Thread() { @Override - public void run( ) - { - try( ProgressDialog progress = new ProgressDialog( parent ) ) - { - runnable.run( progress ); - } - catch( Exception ex ) - { - throw new Error( ex ); + public void run() { + try (ProgressDialog progress = new ProgressDialog(parent)) { + runnable.run(progress); + } catch (Exception ex) { + throw new Error(ex); } } }.start(); diff --git a/src/cuchaz/enigma/gui/ReadableToken.java b/src/cuchaz/enigma/gui/ReadableToken.java index 3f430453..66bcbc2a 100644 --- a/src/cuchaz/enigma/gui/ReadableToken.java +++ b/src/cuchaz/enigma/gui/ReadableToken.java @@ -10,29 +10,27 @@ ******************************************************************************/ package cuchaz.enigma.gui; -public class ReadableToken -{ +public class ReadableToken { + public int line; public int startColumn; public int endColumn; - public ReadableToken( int line, int startColumn, int endColumn ) - { + public ReadableToken(int line, int startColumn, int endColumn) { this.line = line; this.startColumn = startColumn; this.endColumn = endColumn; } @Override - public String toString( ) - { + public String toString() { StringBuilder buf = new StringBuilder(); - buf.append( "line " ); - buf.append( line ); - buf.append( " columns " ); - buf.append( startColumn ); - buf.append( "-" ); - buf.append( endColumn ); + buf.append("line "); + buf.append(line); + buf.append(" columns "); + buf.append(startColumn); + buf.append("-"); + buf.append(endColumn); return buf.toString(); } } diff --git a/src/cuchaz/enigma/gui/RenameListener.java b/src/cuchaz/enigma/gui/RenameListener.java index 7d45505b..abeda0ce 100644 --- a/src/cuchaz/enigma/gui/RenameListener.java +++ b/src/cuchaz/enigma/gui/RenameListener.java @@ -12,7 +12,6 @@ package cuchaz.enigma.gui; import cuchaz.enigma.mapping.Entry; -public interface RenameListener -{ - void rename( Entry obfEntry, String newName ); +public interface RenameListener { + void rename(Entry obfEntry, String newName); } diff --git a/src/cuchaz/enigma/gui/SelectionHighlightPainter.java b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java index 35f94518..5e189d2e 100644 --- a/src/cuchaz/enigma/gui/SelectionHighlightPainter.java +++ b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java @@ -20,16 +20,15 @@ import java.awt.Shape; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; -public class SelectionHighlightPainter implements Highlighter.HighlightPainter -{ +public class SelectionHighlightPainter implements Highlighter.HighlightPainter { + @Override - public void paint( Graphics g, int start, int end, Shape shape, JTextComponent text ) - { + public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { // draw a thick border Graphics2D g2d = (Graphics2D)g; - Rectangle bounds = BoxHighlightPainter.getBounds( text, start, end ); - g2d.setColor( Color.black ); - g2d.setStroke( new BasicStroke( 2.0f ) ); - g2d.drawRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); + Rectangle bounds = BoxHighlightPainter.getBounds(text, start, end); + g2d.setColor(Color.black); + g2d.setStroke(new BasicStroke(2.0f)); + g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); } } diff --git a/src/cuchaz/enigma/gui/TokenListCellRenderer.java b/src/cuchaz/enigma/gui/TokenListCellRenderer.java index 9247c066..a49be37b 100644 --- a/src/cuchaz/enigma/gui/TokenListCellRenderer.java +++ b/src/cuchaz/enigma/gui/TokenListCellRenderer.java @@ -19,22 +19,20 @@ import javax.swing.ListCellRenderer; import cuchaz.enigma.analysis.Token; -public class TokenListCellRenderer implements ListCellRenderer -{ +public class TokenListCellRenderer implements ListCellRenderer { + private GuiController m_controller; private DefaultListCellRenderer m_defaultRenderer; - public TokenListCellRenderer( GuiController controller ) - { + public TokenListCellRenderer(GuiController controller) { m_controller = controller; m_defaultRenderer = new DefaultListCellRenderer(); } @Override - public Component getListCellRendererComponent( JList list, Token token, int index, boolean isSelected, boolean hasFocus ) - { - JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, token, index, isSelected, hasFocus ); - label.setText( m_controller.getReadableToken( token ).toString() ); + public Component getListCellRendererComponent(JList list, Token token, int index, boolean isSelected, boolean hasFocus) { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent(list, token, index, isSelected, hasFocus); + label.setText(m_controller.getReadableToken(token).toString()); return label; } } diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java index 7ed3d328..2c15f4e8 100644 --- a/src/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -14,27 +14,23 @@ import java.io.Serializable; import cuchaz.enigma.Util; -public class ArgumentEntry implements Entry, Serializable -{ +public class ArgumentEntry implements Entry, Serializable { + private static final long serialVersionUID = 4472172468162696006L; private BehaviorEntry m_behaviorEntry; private int m_index; private String m_name; - public ArgumentEntry( BehaviorEntry behaviorEntry, int index, String name ) - { - if( behaviorEntry == null ) - { - throw new IllegalArgumentException( "Behavior cannot be null!" ); + public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) { + if (behaviorEntry == null) { + throw new IllegalArgumentException("Behavior cannot be null!"); } - if( index < 0 ) - { - throw new IllegalArgumentException( "Index must be non-negative!" ); + if (index < 0) { + throw new IllegalArgumentException("Index must be non-negative!"); } - if( name == null ) - { - throw new IllegalArgumentException( "Argument name cannot be null!" ); + if (name == null) { + throw new IllegalArgumentException("Argument name cannot be null!"); } m_behaviorEntry = behaviorEntry; @@ -42,90 +38,79 @@ public class ArgumentEntry implements Entry, Serializable m_name = name; } - public ArgumentEntry( ArgumentEntry other ) - { - m_behaviorEntry = (BehaviorEntry)m_behaviorEntry.cloneToNewClass( getClassEntry() ); + public ArgumentEntry(ArgumentEntry other) { + m_behaviorEntry = (BehaviorEntry)m_behaviorEntry.cloneToNewClass(getClassEntry()); m_index = other.m_index; m_name = other.m_name; } - public ArgumentEntry( ArgumentEntry other, String newClassName ) - { - m_behaviorEntry = (BehaviorEntry)other.m_behaviorEntry.cloneToNewClass( new ClassEntry( newClassName ) ); + public ArgumentEntry(ArgumentEntry other, String newClassName) { + m_behaviorEntry = (BehaviorEntry)other.m_behaviorEntry.cloneToNewClass(new ClassEntry(newClassName)); m_index = other.m_index; m_name = other.m_name; } - public BehaviorEntry getBehaviorEntry( ) - { + public BehaviorEntry getBehaviorEntry() { return m_behaviorEntry; } - public int getIndex( ) - { + public int getIndex() { return m_index; } @Override - public String getName( ) - { + public String getName() { return m_name; } @Override - public ClassEntry getClassEntry( ) - { + public ClassEntry getClassEntry() { return m_behaviorEntry.getClassEntry(); } @Override - public String getClassName( ) - { + public String getClassName() { return m_behaviorEntry.getClassName(); } @Override - public ArgumentEntry cloneToNewClass( ClassEntry classEntry ) - { - return new ArgumentEntry( this, classEntry.getName() ); + public ArgumentEntry cloneToNewClass(ClassEntry classEntry) { + return new ArgumentEntry(this, classEntry.getName()); } - public String getMethodName( ) - { + public String getMethodName() { return m_behaviorEntry.getName(); } - public String getMethodSignature( ) - { + public String getMethodSignature() { return m_behaviorEntry.getSignature(); } @Override - public int hashCode( ) - { - return Util.combineHashesOrdered( m_behaviorEntry, Integer.valueOf( m_index ).hashCode(), m_name.hashCode() ); + public int hashCode() { + return Util.combineHashesOrdered( + m_behaviorEntry, + Integer.valueOf(m_index).hashCode(), + m_name.hashCode() + ); } @Override - public boolean equals( Object other ) - { - if( other instanceof ArgumentEntry ) - { - return equals( (ArgumentEntry)other ); + public boolean equals(Object other) { + if (other instanceof ArgumentEntry) { + return equals((ArgumentEntry)other); } return false; } - public boolean equals( ArgumentEntry other ) - { - return m_behaviorEntry.equals( other.m_behaviorEntry ) + public boolean equals(ArgumentEntry other) { + return m_behaviorEntry.equals(other.m_behaviorEntry) && m_index == other.m_index - && m_name.equals( other.m_name ); + && m_name.equals(other.m_name); } @Override - public String toString( ) - { + public String toString() { return m_behaviorEntry.toString() + "(" + m_index + ":" + m_name + ")"; } } diff --git a/src/cuchaz/enigma/mapping/ArgumentMapping.java b/src/cuchaz/enigma/mapping/ArgumentMapping.java index 168306a2..f4d8e774 100644 --- a/src/cuchaz/enigma/mapping/ArgumentMapping.java +++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java @@ -12,37 +12,33 @@ package cuchaz.enigma.mapping; import java.io.Serializable; -public class ArgumentMapping implements Serializable, Comparable -{ +public class ArgumentMapping implements Serializable, Comparable { + private static final long serialVersionUID = 8610742471440861315L; private int m_index; private String m_name; // NOTE: this argument order is important for the MethodReader/MethodWriter - public ArgumentMapping( int index, String name ) - { + public ArgumentMapping(int index, String name) { m_index = index; - m_name = NameValidator.validateArgumentName( name ); + m_name = NameValidator.validateArgumentName(name); } - public int getIndex( ) - { + public int getIndex() { return m_index; } - public String getName( ) - { + public String getName() { return m_name; } - public void setName( String val ) - { - m_name = NameValidator.validateArgumentName( val ); + + public void setName(String val) { + m_name = NameValidator.validateArgumentName(val); } @Override - public int compareTo( ArgumentMapping other ) - { - return Integer.compare( m_index, other.m_index ); + public int compareTo(ArgumentMapping other) { + return Integer.compare(m_index, other.m_index); } } diff --git a/src/cuchaz/enigma/mapping/BehaviorEntry.java b/src/cuchaz/enigma/mapping/BehaviorEntry.java index 8fc4eaf0..f4200b8b 100644 --- a/src/cuchaz/enigma/mapping/BehaviorEntry.java +++ b/src/cuchaz/enigma/mapping/BehaviorEntry.java @@ -10,7 +10,6 @@ ******************************************************************************/ package cuchaz.enigma.mapping; -public interface BehaviorEntry extends Entry -{ +public interface BehaviorEntry extends Entry { String getSignature(); } diff --git a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java index d3cfb938..386faeb8 100644 --- a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java +++ b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java @@ -15,62 +15,43 @@ import javassist.CtConstructor; import javassist.CtMethod; import javassist.bytecode.Descriptor; - -public class BehaviorEntryFactory -{ - public static BehaviorEntry create( String className, String name, String signature ) - { - return create( new ClassEntry( className ), name, signature ); +public class BehaviorEntryFactory { + + public static BehaviorEntry create(String className, String name, String signature) { + return create(new ClassEntry(className), name, signature); } - public static BehaviorEntry create( ClassEntry classEntry, String name, String signature ) - { - if( name.equals( "" ) ) - { - return new ConstructorEntry( classEntry, signature ); - } - else if( name.equals( "" ) ) - { - return new ConstructorEntry( classEntry ); - } - else - { - return new MethodEntry( classEntry, name, signature ); + public static BehaviorEntry create(ClassEntry classEntry, String name, String signature) { + if (name.equals("")) { + return new ConstructorEntry(classEntry, signature); + } else if (name.equals("")) { + return new ConstructorEntry(classEntry); + } else { + return new MethodEntry(classEntry, name, signature); } } - public static BehaviorEntry create( CtBehavior behavior ) - { - String className = Descriptor.toJvmName( behavior.getDeclaringClass().getName() ); - if( behavior instanceof CtMethod ) - { - return create( className, behavior.getName(), behavior.getSignature() ); - } - else if( behavior instanceof CtConstructor ) - { + public static BehaviorEntry create(CtBehavior behavior) { + String className = Descriptor.toJvmName(behavior.getDeclaringClass().getName()); + if (behavior instanceof CtMethod) { + return create(className, behavior.getName(), behavior.getSignature()); + } else if (behavior instanceof CtConstructor) { CtConstructor constructor = (CtConstructor)behavior; - if( constructor.isClassInitializer() ) - { - return create( className, "", null ); - } - else - { - return create( className, "", constructor.getSignature() ); + if (constructor.isClassInitializer()) { + return create(className, "", null); + } else { + return create(className, "", constructor.getSignature()); } - } - else - { - throw new IllegalArgumentException( "Unable to create BehaviorEntry from " + behavior ); + } else { + throw new IllegalArgumentException("Unable to create BehaviorEntry from " + behavior); } } - public static BehaviorEntry createObf( ClassEntry classEntry, MethodMapping methodMapping ) - { - return create( classEntry, methodMapping.getObfName(), methodMapping.getObfSignature() ); + public static BehaviorEntry createObf(ClassEntry classEntry, MethodMapping methodMapping) { + return create(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); } - public static BehaviorEntry createDeobf( ClassEntry classEntry, MethodMapping methodMapping ) - { - return create( classEntry, methodMapping.getDeobfName(), methodMapping.getObfSignature() ); + public static BehaviorEntry createDeobf(ClassEntry classEntry, MethodMapping methodMapping) { + return create(classEntry, methodMapping.getDeobfName(), methodMapping.getObfSignature()); } } diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 2c708f2a..cf410012 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -12,137 +12,111 @@ package cuchaz.enigma.mapping; import java.io.Serializable; - -public class ClassEntry implements Entry, Serializable -{ +public class ClassEntry implements Entry, Serializable { + private static final long serialVersionUID = 4235460580973955811L; private String m_name; - public ClassEntry( String className ) - { - if( className == null ) - { - throw new IllegalArgumentException( "Class name cannot be null!" ); + public ClassEntry(String className) { + if (className == null) { + throw new IllegalArgumentException("Class name cannot be null!"); } - if( className.indexOf( '.' ) >= 0 ) - { - throw new IllegalArgumentException( "Class name must be in JVM format. ie, path/to/package/class$inner : " + className ); + if (className.indexOf('.') >= 0) { + throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); } m_name = className; - if( isInnerClass() && getInnerClassName().indexOf( '/' ) >= 0 ) - { - throw new IllegalArgumentException( "Inner class must not have a package: " + className ); + if (isInnerClass() && getInnerClassName().indexOf('/') >= 0) { + throw new IllegalArgumentException("Inner class must not have a package: " + className); } } - public ClassEntry( ClassEntry other ) - { + public ClassEntry(ClassEntry other) { m_name = other.m_name; } @Override - public String getName( ) - { + public String getName() { return m_name; } @Override - public String getClassName( ) - { + public String getClassName() { return m_name; } @Override - public ClassEntry getClassEntry( ) - { + public ClassEntry getClassEntry() { return this; } @Override - public ClassEntry cloneToNewClass( ClassEntry classEntry ) - { + public ClassEntry cloneToNewClass(ClassEntry classEntry) { return classEntry; } - + @Override - public int hashCode( ) - { + public int hashCode() { return m_name.hashCode(); } @Override - public boolean equals( Object other ) - { - if( other instanceof ClassEntry ) - { - return equals( (ClassEntry)other ); + public boolean equals(Object other) { + if (other instanceof ClassEntry) { + return equals((ClassEntry)other); } return false; } - public boolean equals( ClassEntry other ) - { - return m_name.equals( other.m_name ); + public boolean equals(ClassEntry other) { + return m_name.equals(other.m_name); } @Override - public String toString( ) - { + public String toString() { return m_name; } - public boolean isInnerClass( ) - { - return m_name.lastIndexOf( '$' ) >= 0; + public boolean isInnerClass() { + return m_name.lastIndexOf('$') >= 0; } - public String getOuterClassName( ) - { - if( isInnerClass() ) - { - return m_name.substring( 0, m_name.lastIndexOf( '$' ) ); + public String getOuterClassName() { + if (isInnerClass()) { + return m_name.substring(0, m_name.lastIndexOf('$')); } return m_name; } - public String getInnerClassName( ) - { - if( !isInnerClass() ) - { - throw new Error( "This is not an inner class!" ); + public String getInnerClassName() { + if (!isInnerClass()) { + throw new Error("This is not an inner class!"); } - return m_name.substring( m_name.lastIndexOf( '$' ) + 1 ); + return m_name.substring(m_name.lastIndexOf('$') + 1); } - public ClassEntry getOuterClassEntry( ) - { - return new ClassEntry( getOuterClassName() ); + public ClassEntry getOuterClassEntry() { + return new ClassEntry(getOuterClassName()); } - public boolean isInDefaultPackage( ) - { - return m_name.indexOf( '/' ) < 0; + public boolean isInDefaultPackage() { + return m_name.indexOf('/') < 0; } - - public String getPackageName( ) - { - int pos = m_name.lastIndexOf( '/' ); - if( pos > 0 ) - { - return m_name.substring( 0, pos ); + + public String getPackageName() { + int pos = m_name.lastIndexOf('/'); + if (pos > 0) { + return m_name.substring(0, pos); } return null; } - - public String getSimpleName( ) - { - int pos = m_name.lastIndexOf( '/' ); - if( pos > 0 ) - { - return m_name.substring( pos + 1 ); + + public String getSimpleName() { + int pos = m_name.lastIndexOf('/'); + if (pos > 0) { + return m_name.substring(pos + 1); } return m_name; } diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index e084d4df..dbb8717f 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -16,8 +16,8 @@ import java.util.Map; import com.google.common.collect.Maps; -public class ClassMapping implements Serializable, Comparable -{ +public class ClassMapping implements Serializable, Comparable { + private static final long serialVersionUID = -5148491146902340107L; private String m_obfName; @@ -29,15 +29,13 @@ public class ClassMapping implements Serializable, Comparable private Map m_methodsByObf; private Map m_methodsByDeobf; - public ClassMapping( String obfName ) - { - this( obfName, null ); + public ClassMapping(String obfName) { + this(obfName, null); } - public ClassMapping( String obfName, String deobfName ) - { + public ClassMapping(String obfName, String deobfName) { m_obfName = obfName; - m_deobfName = NameValidator.validateClassName( deobfName, false ); + m_deobfName = NameValidator.validateClassName(deobfName, false); m_innerClassesByObf = Maps.newHashMap(); m_innerClassesByDeobf = Maps.newHashMap(); m_fieldsByObf = Maps.newHashMap(); @@ -45,439 +43,363 @@ public class ClassMapping implements Serializable, Comparable m_methodsByObf = Maps.newHashMap(); m_methodsByDeobf = Maps.newHashMap(); } - - public String getObfName( ) - { + + public String getObfName() { return m_obfName; } - public String getDeobfName( ) - { + public String getDeobfName() { return m_deobfName; } - public void setDeobfName( String val ) - { - m_deobfName = NameValidator.validateClassName( val, false ); + + public void setDeobfName(String val) { + m_deobfName = NameValidator.validateClassName(val, false); } //// INNER CLASSES //////// - public Iterable innerClasses( ) - { - assert( m_innerClassesByObf.size() >= m_innerClassesByDeobf.size() ); + public Iterable innerClasses() { + assert (m_innerClassesByObf.size() >= m_innerClassesByDeobf.size()); return m_innerClassesByObf.values(); } - public void addInnerClassMapping( ClassMapping classMapping ) - { - assert( isSimpleClassName( classMapping.getObfName() ) ); - boolean obfWasAdded = m_innerClassesByObf.put( classMapping.getObfName(), classMapping ) == null; - assert( obfWasAdded ); - if( classMapping.getDeobfName() != null ) - { - assert( isSimpleClassName( classMapping.getDeobfName() ) ); - boolean deobfWasAdded = m_innerClassesByDeobf.put( classMapping.getDeobfName(), classMapping ) == null; - assert( deobfWasAdded ); + public void addInnerClassMapping(ClassMapping classMapping) { + assert (isSimpleClassName(classMapping.getObfName())); + boolean obfWasAdded = m_innerClassesByObf.put(classMapping.getObfName(), classMapping) == null; + assert (obfWasAdded); + if (classMapping.getDeobfName() != null) { + assert (isSimpleClassName(classMapping.getDeobfName())); + boolean deobfWasAdded = m_innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping) == null; + assert (deobfWasAdded); } } - public void removeInnerClassMapping( ClassMapping classMapping ) - { - boolean obfWasRemoved = m_innerClassesByObf.remove( classMapping.getObfName() ) != null; - assert( obfWasRemoved ); - if( classMapping.getDeobfName() != null ) - { - boolean deobfWasRemoved = m_innerClassesByDeobf.remove( classMapping.getDeobfName() ) != null; - assert( deobfWasRemoved ); + public void removeInnerClassMapping(ClassMapping classMapping) { + boolean obfWasRemoved = m_innerClassesByObf.remove(classMapping.getObfName()) != null; + assert (obfWasRemoved); + if (classMapping.getDeobfName() != null) { + boolean deobfWasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (deobfWasRemoved); } } - public ClassMapping getOrCreateInnerClass( String obfName ) - { - assert( isSimpleClassName( obfName ) ); - ClassMapping classMapping = m_innerClassesByObf.get( obfName ); - if( classMapping == null ) - { - classMapping = new ClassMapping( obfName ); - boolean wasAdded = m_innerClassesByObf.put( obfName, classMapping ) == null; - assert( wasAdded ); + public ClassMapping getOrCreateInnerClass(String obfName) { + assert (isSimpleClassName(obfName)); + ClassMapping classMapping = m_innerClassesByObf.get(obfName); + if (classMapping == null) { + classMapping = new ClassMapping(obfName); + boolean wasAdded = m_innerClassesByObf.put(obfName, classMapping) == null; + assert (wasAdded); } return classMapping; } - public ClassMapping getInnerClassByObf( String obfName ) - { - assert( isSimpleClassName( obfName ) ); - return m_innerClassesByObf.get( obfName ); + public ClassMapping getInnerClassByObf(String obfName) { + assert (isSimpleClassName(obfName)); + return m_innerClassesByObf.get(obfName); } - public ClassMapping getInnerClassByDeobf( String deobfName ) - { - assert( isSimpleClassName( deobfName ) ); - return m_innerClassesByDeobf.get( deobfName ); + public ClassMapping getInnerClassByDeobf(String deobfName) { + assert (isSimpleClassName(deobfName)); + return m_innerClassesByDeobf.get(deobfName); } - public ClassMapping getInnerClassByDeobfThenObf( String name ) - { - ClassMapping classMapping = getInnerClassByDeobf( name ); - if( classMapping == null ) - { - classMapping = getInnerClassByObf( name ); + public ClassMapping getInnerClassByDeobfThenObf(String name) { + ClassMapping classMapping = getInnerClassByDeobf(name); + if (classMapping == null) { + classMapping = getInnerClassByObf(name); } return classMapping; } - public String getObfInnerClassName( String deobfName ) - { - assert( isSimpleClassName( deobfName ) ); - ClassMapping classMapping = m_innerClassesByDeobf.get( deobfName ); - if( classMapping != null ) - { + public String getObfInnerClassName(String deobfName) { + assert (isSimpleClassName(deobfName)); + ClassMapping classMapping = m_innerClassesByDeobf.get(deobfName); + if (classMapping != null) { return classMapping.getObfName(); } return null; } - public String getDeobfInnerClassName( String obfName ) - { - assert( isSimpleClassName( obfName ) ); - ClassMapping classMapping = m_innerClassesByObf.get( obfName ); - if( classMapping != null ) - { + public String getDeobfInnerClassName(String obfName) { + assert (isSimpleClassName(obfName)); + ClassMapping classMapping = m_innerClassesByObf.get(obfName); + if (classMapping != null) { return classMapping.getDeobfName(); } return null; } - public void setInnerClassName( String obfName, String deobfName ) - { - assert( isSimpleClassName( obfName ) ); - ClassMapping classMapping = getOrCreateInnerClass( obfName ); - if( classMapping.getDeobfName() != null ) - { - boolean wasRemoved = m_innerClassesByDeobf.remove( classMapping.getDeobfName() ) != null; - assert( wasRemoved ); + public void setInnerClassName(String obfName, String deobfName) { + assert (isSimpleClassName(obfName)); + ClassMapping classMapping = getOrCreateInnerClass(obfName); + if (classMapping.getDeobfName() != null) { + boolean wasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (wasRemoved); } - classMapping.setDeobfName( deobfName ); - if( deobfName != null ) - { - assert( isSimpleClassName( deobfName ) ); - boolean wasAdded = m_innerClassesByDeobf.put( deobfName, classMapping ) == null; - assert( wasAdded ); + classMapping.setDeobfName(deobfName); + if (deobfName != null) { + assert (isSimpleClassName(deobfName)); + boolean wasAdded = m_innerClassesByDeobf.put(deobfName, classMapping) == null; + assert (wasAdded); } } //// FIELDS //////// - public Iterable fields( ) - { - assert( m_fieldsByObf.size() == m_fieldsByDeobf.size() ); + public Iterable fields() { + assert (m_fieldsByObf.size() == m_fieldsByDeobf.size()); return m_fieldsByObf.values(); } - public boolean containsObfField( String obfName ) - { - return m_fieldsByObf.containsKey( obfName ); + public boolean containsObfField(String obfName) { + return m_fieldsByObf.containsKey(obfName); } - public boolean containsDeobfField( String deobfName ) - { - return m_fieldsByDeobf.containsKey( deobfName ); + public boolean containsDeobfField(String deobfName) { + return m_fieldsByDeobf.containsKey(deobfName); } - public void addFieldMapping( FieldMapping fieldMapping ) - { - if( m_fieldsByObf.containsKey( fieldMapping.getObfName() ) ) - { - throw new Error( "Already have mapping for " + m_obfName + "." + fieldMapping.getObfName() ); + public void addFieldMapping(FieldMapping fieldMapping) { + if (m_fieldsByObf.containsKey(fieldMapping.getObfName())) { + throw new Error("Already have mapping for " + m_obfName + "." + fieldMapping.getObfName()); } - if( m_fieldsByDeobf.containsKey( fieldMapping.getDeobfName() ) ) - { - throw new Error( "Already have mapping for " + m_deobfName + "." + fieldMapping.getDeobfName() ); + if (m_fieldsByDeobf.containsKey(fieldMapping.getDeobfName())) { + throw new Error("Already have mapping for " + m_deobfName + "." + fieldMapping.getDeobfName()); } - boolean obfWasAdded = m_fieldsByObf.put( fieldMapping.getObfName(), fieldMapping ) == null; - assert( obfWasAdded ); - boolean deobfWasAdded = m_fieldsByDeobf.put( fieldMapping.getDeobfName(), fieldMapping ) == null; - assert( deobfWasAdded ); - assert( m_fieldsByObf.size() == m_fieldsByDeobf.size() ); - } - - public void removeFieldMapping( FieldMapping fieldMapping ) - { - boolean obfWasRemoved = m_fieldsByObf.remove( fieldMapping.getObfName() ) != null; - assert( obfWasRemoved ); - if( fieldMapping.getDeobfName() != null ) - { - boolean deobfWasRemoved = m_fieldsByDeobf.remove( fieldMapping.getDeobfName() ) != null; - assert( deobfWasRemoved ); + boolean obfWasAdded = m_fieldsByObf.put(fieldMapping.getObfName(), fieldMapping) == null; + assert (obfWasAdded); + boolean deobfWasAdded = m_fieldsByDeobf.put(fieldMapping.getDeobfName(), fieldMapping) == null; + assert (deobfWasAdded); + assert (m_fieldsByObf.size() == m_fieldsByDeobf.size()); + } + + public void removeFieldMapping(FieldMapping fieldMapping) { + boolean obfWasRemoved = m_fieldsByObf.remove(fieldMapping.getObfName()) != null; + assert (obfWasRemoved); + if (fieldMapping.getDeobfName() != null) { + boolean deobfWasRemoved = m_fieldsByDeobf.remove(fieldMapping.getDeobfName()) != null; + assert (deobfWasRemoved); } } - public FieldMapping getFieldByObf( String obfName ) - { - return m_fieldsByObf.get( obfName ); + public FieldMapping getFieldByObf(String obfName) { + return m_fieldsByObf.get(obfName); } - public FieldMapping getFieldByDeobf( String deobfName ) - { - return m_fieldsByDeobf.get( deobfName ); + public FieldMapping getFieldByDeobf(String deobfName) { + return m_fieldsByDeobf.get(deobfName); } - - public String getObfFieldName( String deobfName ) - { - FieldMapping fieldMapping = m_fieldsByDeobf.get( deobfName ); - if( fieldMapping != null ) - { + + public String getObfFieldName(String deobfName) { + FieldMapping fieldMapping = m_fieldsByDeobf.get(deobfName); + if (fieldMapping != null) { return fieldMapping.getObfName(); } return null; } - public String getDeobfFieldName( String obfName ) - { - FieldMapping fieldMapping = m_fieldsByObf.get( obfName ); - if( fieldMapping != null ) - { + public String getDeobfFieldName(String obfName) { + FieldMapping fieldMapping = m_fieldsByObf.get(obfName); + if (fieldMapping != null) { return fieldMapping.getDeobfName(); } return null; } - public void setFieldName( String obfName, String deobfName ) - { - FieldMapping fieldMapping = m_fieldsByObf.get( obfName ); - if( fieldMapping == null ) - { - fieldMapping = new FieldMapping( obfName, deobfName ); - boolean obfWasAdded = m_fieldsByObf.put( obfName, fieldMapping ) == null; - assert( obfWasAdded ); + public void setFieldName(String obfName, String deobfName) { + FieldMapping fieldMapping = m_fieldsByObf.get(obfName); + if (fieldMapping == null) { + fieldMapping = new FieldMapping(obfName, deobfName); + boolean obfWasAdded = m_fieldsByObf.put(obfName, fieldMapping) == null; + assert (obfWasAdded); + } else { + boolean wasRemoved = m_fieldsByDeobf.remove(fieldMapping.getDeobfName()) != null; + assert (wasRemoved); } - else - { - boolean wasRemoved = m_fieldsByDeobf.remove( fieldMapping.getDeobfName() ) != null; - assert( wasRemoved ); - } - fieldMapping.setDeobfName( deobfName ); - if( deobfName != null ) - { - boolean wasAdded = m_fieldsByDeobf.put( deobfName, fieldMapping ) == null; - assert( wasAdded ); + fieldMapping.setDeobfName(deobfName); + if (deobfName != null) { + boolean wasAdded = m_fieldsByDeobf.put(deobfName, fieldMapping) == null; + assert (wasAdded); } } //// METHODS //////// - public Iterable methods( ) - { - assert( m_methodsByObf.size() >= m_methodsByDeobf.size() ); + public Iterable methods() { + assert (m_methodsByObf.size() >= m_methodsByDeobf.size()); return m_methodsByObf.values(); } - public boolean containsObfMethod( String obfName, String obfSignature ) - { - return m_methodsByObf.containsKey( getMethodKey( obfName, obfSignature ) ); + public boolean containsObfMethod(String obfName, String obfSignature) { + return m_methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); } - public boolean containsDeobfMethod( String deobfName, String deobfSignature ) - { - return m_methodsByDeobf.containsKey( getMethodKey( deobfName, deobfSignature ) ); + public boolean containsDeobfMethod(String deobfName, String deobfSignature) { + return m_methodsByDeobf.containsKey(getMethodKey(deobfName, deobfSignature)); } - public void addMethodMapping( MethodMapping methodMapping ) - { - String obfKey = getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ); - if( m_methodsByObf.containsKey( obfKey ) ) - { - throw new Error( "Already have mapping for " + m_obfName + "." + obfKey ); + public void addMethodMapping(MethodMapping methodMapping) { + String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); + if (m_methodsByObf.containsKey(obfKey)) { + throw new Error("Already have mapping for " + m_obfName + "." + obfKey); } - boolean wasAdded = m_methodsByObf.put( obfKey, methodMapping ) == null; - assert( wasAdded ); - if( methodMapping.getDeobfName() != null ) - { - String deobfKey = getMethodKey( methodMapping.getDeobfName(), methodMapping.getObfSignature() ); - if( m_methodsByDeobf.containsKey( deobfKey ) ) - { - throw new Error( "Already have mapping for " + m_deobfName + "." + deobfKey ); + boolean wasAdded = m_methodsByObf.put(obfKey, methodMapping) == null; + assert (wasAdded); + if (methodMapping.getDeobfName() != null) { + String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); + if (m_methodsByDeobf.containsKey(deobfKey)) { + throw new Error("Already have mapping for " + m_deobfName + "." + deobfKey); } - boolean deobfWasAdded = m_methodsByDeobf.put( deobfKey, methodMapping ) == null; - assert( deobfWasAdded ); + boolean deobfWasAdded = m_methodsByDeobf.put(deobfKey, methodMapping) == null; + assert (deobfWasAdded); } - assert( m_methodsByObf.size() >= m_methodsByDeobf.size() ); + assert (m_methodsByObf.size() >= m_methodsByDeobf.size()); } - public void removeMethodMapping( MethodMapping methodMapping ) - { - boolean obfWasRemoved = m_methodsByObf.remove( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ) ) != null; - assert( obfWasRemoved ); - if( methodMapping.getDeobfName() != null ) - { - boolean deobfWasRemoved = m_methodsByDeobf.remove( getMethodKey( methodMapping.getDeobfName(), methodMapping.getObfSignature() ) ) != null; - assert( deobfWasRemoved ); + public void removeMethodMapping(MethodMapping methodMapping) { + boolean obfWasRemoved = m_methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; + assert (obfWasRemoved); + if (methodMapping.getDeobfName() != null) { + boolean deobfWasRemoved = m_methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; + assert (deobfWasRemoved); } } - public MethodMapping getMethodByObf( String obfName, String signature ) - { - return m_methodsByObf.get( getMethodKey( obfName, signature ) ); + public MethodMapping getMethodByObf(String obfName, String signature) { + return m_methodsByObf.get(getMethodKey(obfName, signature)); } - public MethodMapping getMethodByDeobf( String deobfName, String signature ) - { - return m_methodsByDeobf.get( getMethodKey( deobfName, signature ) ); + public MethodMapping getMethodByDeobf(String deobfName, String signature) { + return m_methodsByDeobf.get(getMethodKey(deobfName, signature)); } - private String getMethodKey( String name, String signature ) - { - if( name == null ) - { - throw new IllegalArgumentException( "name cannot be null!" ); + private String getMethodKey(String name, String signature) { + if (name == null) { + throw new IllegalArgumentException("name cannot be null!"); } - if( signature == null ) - { - throw new IllegalArgumentException( "signature cannot be null!" ); + if (signature == null) { + throw new IllegalArgumentException("signature cannot be null!"); } return name + signature; } - public void setMethodName( String obfName, String obfSignature, String deobfName ) - { - MethodMapping methodMapping = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) ); - if( methodMapping == null ) - { - methodMapping = createMethodMapping( obfName, obfSignature ); - } - else if( methodMapping.getDeobfName() != null ) - { - boolean wasRemoved = m_methodsByDeobf.remove( getMethodKey( methodMapping.getDeobfName(), methodMapping.getObfSignature() ) ) != null; - assert( wasRemoved ); + public void setMethodName(String obfName, String obfSignature, String deobfName) { + MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfName, obfSignature)); + if (methodMapping == null) { + methodMapping = createMethodMapping(obfName, obfSignature); + } else if (methodMapping.getDeobfName() != null) { + boolean wasRemoved = m_methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; + assert (wasRemoved); } - methodMapping.setDeobfName( deobfName ); - if( deobfName != null ) - { - boolean wasAdded = m_methodsByDeobf.put( getMethodKey( deobfName, obfSignature ), methodMapping ) == null; - assert( wasAdded ); + methodMapping.setDeobfName(deobfName); + if (deobfName != null) { + boolean wasAdded = m_methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; + assert (wasAdded); } } //// ARGUMENTS //////// - public void setArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName ) - { - MethodMapping methodMapping = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ); - if( methodMapping == null ) - { - methodMapping = createMethodMapping( obfMethodName, obfMethodSignature ); + public void setArgumentName(String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName) { + MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); + if (methodMapping == null) { + methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); } - methodMapping.setArgumentName( argumentIndex, argumentName ); + methodMapping.setArgumentName(argumentIndex, argumentName); } - public void removeArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex ) - { - m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ).removeArgumentName( argumentIndex ); + public void removeArgumentName(String obfMethodName, String obfMethodSignature, int argumentIndex) { + m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); } - - private MethodMapping createMethodMapping( String obfName, String obfSignature ) - { - MethodMapping methodMapping = new MethodMapping( obfName, obfSignature ); - boolean wasAdded = m_methodsByObf.put( getMethodKey( obfName, obfSignature ), methodMapping ) == null; - assert( wasAdded ); + + private MethodMapping createMethodMapping(String obfName, String obfSignature) { + MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); + boolean wasAdded = m_methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; + assert (wasAdded); return methodMapping; } - + @Override - public String toString( ) - { + public String toString() { StringBuilder buf = new StringBuilder(); - buf.append( m_obfName ); - buf.append( " <-> " ); - buf.append( m_deobfName ); - buf.append( "\n" ); - buf.append( "Fields:\n" ); - for( FieldMapping fieldMapping : fields() ) - { - buf.append( "\t" ); - buf.append( fieldMapping.getObfName() ); - buf.append( " <-> " ); - buf.append( fieldMapping.getDeobfName() ); - buf.append( "\n" ); - } - buf.append( "Methods:\n" ); - for( MethodMapping methodMapping : m_methodsByObf.values() ) - { - buf.append( methodMapping.toString() ); - buf.append( "\n" ); - } - buf.append( "Inner Classes:\n" ); - for( ClassMapping classMapping : m_innerClassesByObf.values() ) - { - buf.append( "\t" ); - buf.append( classMapping.getObfName() ); - buf.append( " <-> " ); - buf.append( classMapping.getDeobfName() ); - buf.append( "\n" ); + buf.append(m_obfName); + buf.append(" <-> "); + buf.append(m_deobfName); + buf.append("\n"); + buf.append("Fields:\n"); + for (FieldMapping fieldMapping : fields()) { + buf.append("\t"); + buf.append(fieldMapping.getObfName()); + buf.append(" <-> "); + buf.append(fieldMapping.getDeobfName()); + buf.append("\n"); + } + buf.append("Methods:\n"); + for (MethodMapping methodMapping : m_methodsByObf.values()) { + buf.append(methodMapping.toString()); + buf.append("\n"); + } + buf.append("Inner Classes:\n"); + for (ClassMapping classMapping : m_innerClassesByObf.values()) { + buf.append("\t"); + buf.append(classMapping.getObfName()); + buf.append(" <-> "); + buf.append(classMapping.getDeobfName()); + buf.append("\n"); } return buf.toString(); } @Override - public int compareTo( ClassMapping other ) - { + public int compareTo(ClassMapping other) { // sort by a, b, c, ... aa, ab, etc - if( m_obfName.length() != other.m_obfName.length() ) - { + if (m_obfName.length() != other.m_obfName.length()) { return m_obfName.length() - other.m_obfName.length(); } - return m_obfName.compareTo( other.m_obfName ); + return m_obfName.compareTo(other.m_obfName); } - public boolean renameObfClass( String oldObfClassName, String newObfClassName ) - { + public boolean renameObfClass(String oldObfClassName, String newObfClassName) { + // rename inner classes - for( ClassMapping innerClassMapping : new ArrayList( m_innerClassesByObf.values() ) ) - { - if( innerClassMapping.renameObfClass( oldObfClassName, newObfClassName ) ) - { - boolean wasRemoved = m_innerClassesByObf.remove( oldObfClassName ) != null; - assert( wasRemoved ); - boolean wasAdded = m_innerClassesByObf.put( newObfClassName, innerClassMapping ) == null; - assert( wasAdded ); + for (ClassMapping innerClassMapping : new ArrayList(m_innerClassesByObf.values())) { + if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) { + boolean wasRemoved = m_innerClassesByObf.remove(oldObfClassName) != null; + assert (wasRemoved); + boolean wasAdded = m_innerClassesByObf.put(newObfClassName, innerClassMapping) == null; + assert (wasAdded); } } // rename method signatures - for( MethodMapping methodMapping : new ArrayList( m_methodsByObf.values() ) ) - { - String oldMethodKey = getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ); - if( methodMapping.renameObfClass( oldObfClassName, newObfClassName ) ) - { - boolean wasRemoved = m_methodsByObf.remove( oldMethodKey ) != null; - assert( wasRemoved ); - boolean wasAdded = m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping ) == null; - assert( wasAdded ); + for (MethodMapping methodMapping : new ArrayList(m_methodsByObf.values())) { + String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); + if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { + boolean wasRemoved = m_methodsByObf.remove(oldMethodKey) != null; + assert (wasRemoved); + boolean wasAdded = m_methodsByObf.put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; + assert (wasAdded); } } - if( m_obfName.equals( oldObfClassName ) ) - { + if (m_obfName.equals(oldObfClassName)) { // rename this class m_obfName = newObfClassName; return true; } return false; } - - public boolean containsArgument( BehaviorEntry obfBehaviorEntry, String name ) - { - MethodMapping methodMapping = m_methodsByObf.get( getMethodKey( obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature() ) ); - if( methodMapping != null ) - { - return methodMapping.containsArgument( name ); + + public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { + MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); + if (methodMapping != null) { + return methodMapping.containsArgument(name); } return false; } - public static boolean isSimpleClassName( String name ) - { - return name.indexOf( '/' ) < 0 && name.indexOf( '$' ) < 0; + public static boolean isSimpleClassName(String name) { + return name.indexOf('/') < 0 && name.indexOf('$') < 0; } } diff --git a/src/cuchaz/enigma/mapping/ConstructorEntry.java b/src/cuchaz/enigma/mapping/ConstructorEntry.java index d99d1c35..ea0535f8 100644 --- a/src/cuchaz/enigma/mapping/ConstructorEntry.java +++ b/src/cuchaz/enigma/mapping/ConstructorEntry.java @@ -14,129 +14,102 @@ import java.io.Serializable; import cuchaz.enigma.Util; -public class ConstructorEntry implements BehaviorEntry, Serializable -{ +public class ConstructorEntry implements BehaviorEntry, Serializable { + private static final long serialVersionUID = -868346075317366758L; private ClassEntry m_classEntry; private String m_signature; - public ConstructorEntry( ClassEntry classEntry ) - { - this( classEntry, null ); + public ConstructorEntry(ClassEntry classEntry) { + this(classEntry, null); } - public ConstructorEntry( ClassEntry classEntry, String signature ) - { - if( classEntry == null ) - { - throw new IllegalArgumentException( "Class cannot be null!" ); + public ConstructorEntry(ClassEntry classEntry, String signature) { + if (classEntry == null) { + throw new IllegalArgumentException("Class cannot be null!"); } m_classEntry = classEntry; m_signature = signature; } - public ConstructorEntry( ConstructorEntry other ) - { - m_classEntry = new ClassEntry( other.m_classEntry ); + public ConstructorEntry(ConstructorEntry other) { + m_classEntry = new ClassEntry(other.m_classEntry); m_signature = other.m_signature; } - public ConstructorEntry( ConstructorEntry other, String newClassName ) - { - m_classEntry = new ClassEntry( newClassName ); + public ConstructorEntry(ConstructorEntry other, String newClassName) { + m_classEntry = new ClassEntry(newClassName); m_signature = other.m_signature; } @Override - public ClassEntry getClassEntry( ) - { + public ClassEntry getClassEntry() { return m_classEntry; } @Override - public String getName( ) - { - if( isStatic() ) - { + public String getName() { + if (isStatic()) { return ""; } return ""; } - public boolean isStatic( ) - { + public boolean isStatic() { return m_signature == null; } @Override - public String getSignature( ) - { + public String getSignature() { return m_signature; } @Override - public String getClassName( ) - { + public String getClassName() { return m_classEntry.getName(); } @Override - public ConstructorEntry cloneToNewClass( ClassEntry classEntry ) - { - return new ConstructorEntry( this, classEntry.getName() ); + public ConstructorEntry cloneToNewClass(ClassEntry classEntry) { + return new ConstructorEntry(this, classEntry.getName()); } @Override - public int hashCode( ) - { - if( isStatic() ) - { - return Util.combineHashesOrdered( m_classEntry ); - } - else - { - return Util.combineHashesOrdered( m_classEntry, m_signature ); + public int hashCode() { + if (isStatic()) { + return Util.combineHashesOrdered(m_classEntry); + } else { + return Util.combineHashesOrdered(m_classEntry, m_signature); } } @Override - public boolean equals( Object other ) - { - if( other instanceof ConstructorEntry ) - { - return equals( (ConstructorEntry)other ); + public boolean equals(Object other) { + if (other instanceof ConstructorEntry) { + return equals((ConstructorEntry)other); } return false; } - public boolean equals( ConstructorEntry other ) - { - if( isStatic() != other.isStatic() ) - { + public boolean equals(ConstructorEntry other) { + if (isStatic() != other.isStatic()) { return false; } - if( isStatic() ) - { - return m_classEntry.equals( other.m_classEntry ); - } - else - { - return m_classEntry.equals( other.m_classEntry ) && m_signature.equals( other.m_signature ); + if (isStatic()) { + return m_classEntry.equals(other.m_classEntry); + } else { + return m_classEntry.equals(other.m_classEntry) && m_signature.equals(other.m_signature); } } @Override - public String toString( ) - { - if( isStatic() ) - { + public String toString() { + if (isStatic()) { return m_classEntry.getName() + "." + getName(); - } - else - { + } else { return m_classEntry.getName() + "." + getName() + m_signature; } } diff --git a/src/cuchaz/enigma/mapping/Entry.java b/src/cuchaz/enigma/mapping/Entry.java index 8524834c..39e1507d 100644 --- a/src/cuchaz/enigma/mapping/Entry.java +++ b/src/cuchaz/enigma/mapping/Entry.java @@ -10,10 +10,9 @@ ******************************************************************************/ package cuchaz.enigma.mapping; -public interface Entry -{ - String getName( ); - String getClassName( ); - ClassEntry getClassEntry( ); - Entry cloneToNewClass( ClassEntry classEntry ); +public interface Entry { + String getName(); + String getClassName(); + ClassEntry getClassEntry(); + Entry cloneToNewClass(ClassEntry classEntry); } diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java index f94d77ea..60411c40 100644 --- a/src/cuchaz/enigma/mapping/EntryPair.java +++ b/src/cuchaz/enigma/mapping/EntryPair.java @@ -10,15 +10,12 @@ ******************************************************************************/ package cuchaz.enigma.mapping; - - -public class EntryPair -{ +public class EntryPair { + public T obf; public T deobf; - public EntryPair( T obf, T deobf ) - { + public EntryPair(T obf, T deobf) { this.obf = obf; this.deobf = deobf; } diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java index 626af576..6cc9eb78 100644 --- a/src/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -14,90 +14,75 @@ import java.io.Serializable; import cuchaz.enigma.Util; -public class FieldEntry implements Entry, Serializable -{ +public class FieldEntry implements Entry, Serializable { + private static final long serialVersionUID = 3004663582802885451L; private ClassEntry m_classEntry; private String m_name; // NOTE: this argument order is important for the MethodReader/MethodWriter - public FieldEntry( ClassEntry classEntry, String name ) - { - if( classEntry == null ) - { - throw new IllegalArgumentException( "Class cannot be null!" ); + public FieldEntry(ClassEntry classEntry, String name) { + if (classEntry == null) { + throw new IllegalArgumentException("Class cannot be null!"); } - if( name == null ) - { - throw new IllegalArgumentException( "Field name cannot be null!" ); + if (name == null) { + throw new IllegalArgumentException("Field name cannot be null!"); } m_classEntry = classEntry; m_name = name; } - public FieldEntry( FieldEntry other ) - { - m_classEntry = new ClassEntry( other.m_classEntry ); + public FieldEntry(FieldEntry other) { + m_classEntry = new ClassEntry(other.m_classEntry); m_name = other.m_name; } - - public FieldEntry( FieldEntry other, String newClassName ) - { - m_classEntry = new ClassEntry( newClassName ); + + public FieldEntry(FieldEntry other, String newClassName) { + m_classEntry = new ClassEntry(newClassName); m_name = other.m_name; } @Override - public ClassEntry getClassEntry( ) - { + public ClassEntry getClassEntry() { return m_classEntry; } @Override - public String getName( ) - { + public String getName() { return m_name; } @Override - public String getClassName( ) - { + public String getClassName() { return m_classEntry.getName(); } @Override - public FieldEntry cloneToNewClass( ClassEntry classEntry ) - { - return new FieldEntry( this, classEntry.getName() ); + public FieldEntry cloneToNewClass(ClassEntry classEntry) { + return new FieldEntry(this, classEntry.getName()); } @Override - public int hashCode( ) - { - return Util.combineHashesOrdered( m_classEntry, m_name ); + public int hashCode() { + return Util.combineHashesOrdered(m_classEntry, m_name); } @Override - public boolean equals( Object other ) - { - if( other instanceof FieldEntry ) - { - return equals( (FieldEntry)other ); + public boolean equals(Object other) { + if (other instanceof FieldEntry) { + return equals((FieldEntry)other); } return false; } - public boolean equals( FieldEntry other ) - { - return m_classEntry.equals( other.m_classEntry ) - && m_name.equals( other.m_name ); + public boolean equals(FieldEntry other) { + return m_classEntry.equals(other.m_classEntry) && m_name.equals(other.m_name); } @Override - public String toString( ) - { + public String toString() { return m_classEntry.getName() + "." + m_name; } } diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java index ae0855a8..5f5c270d 100644 --- a/src/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -12,36 +12,32 @@ package cuchaz.enigma.mapping; import java.io.Serializable; -public class FieldMapping implements Serializable, Comparable -{ +public class FieldMapping implements Serializable, Comparable { + private static final long serialVersionUID = 8610742471440861315L; private String m_obfName; private String m_deobfName; - public FieldMapping( String obfName, String deobfName ) - { + public FieldMapping(String obfName, String deobfName) { m_obfName = obfName; - m_deobfName = NameValidator.validateFieldName( deobfName ); + m_deobfName = NameValidator.validateFieldName(deobfName); } - - public String getObfName( ) - { + + public String getObfName() { return m_obfName; } - - public String getDeobfName( ) - { + + public String getDeobfName() { return m_deobfName; } - public void setDeobfName( String val ) - { - m_deobfName = NameValidator.validateFieldName( val ); + + public void setDeobfName(String val) { + m_deobfName = NameValidator.validateFieldName(val); } @Override - public int compareTo( FieldMapping other ) - { - return m_obfName.compareTo( other.m_obfName ); + public int compareTo(FieldMapping other) { + return m_obfName.compareTo(other.m_obfName); } } diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java index 830f05c4..aacaf3b0 100644 --- a/src/cuchaz/enigma/mapping/IllegalNameException.java +++ b/src/cuchaz/enigma/mapping/IllegalNameException.java @@ -10,39 +10,34 @@ ******************************************************************************/ package cuchaz.enigma.mapping; -public class IllegalNameException extends RuntimeException -{ +public class IllegalNameException extends RuntimeException { + private static final long serialVersionUID = -2279910052561114323L; private String m_name; private String m_reason; - public IllegalNameException( String name ) - { - this( name, null ); + public IllegalNameException(String name) { + this(name, null); } - public IllegalNameException( String name, String reason ) - { + public IllegalNameException(String name, String reason) { m_name = name; m_reason = reason; } - public String getReason( ) - { + public String getReason() { return m_reason; } @Override - public String getMessage( ) - { + public String getMessage() { StringBuilder buf = new StringBuilder(); - buf.append( "Illegal name: " ); - buf.append( m_name ); - if( m_reason != null ) - { - buf.append( " because " ); - buf.append( m_reason ); + buf.append("Illegal name: "); + buf.append(m_name); + if (m_reason != null) { + buf.append(" because "); + buf.append(m_reason); } return buf.toString(); } diff --git a/src/cuchaz/enigma/mapping/MappingParseException.java b/src/cuchaz/enigma/mapping/MappingParseException.java index 4fcc1f18..1974c222 100644 --- a/src/cuchaz/enigma/mapping/MappingParseException.java +++ b/src/cuchaz/enigma/mapping/MappingParseException.java @@ -10,22 +10,20 @@ ******************************************************************************/ package cuchaz.enigma.mapping; -public class MappingParseException extends Exception -{ +public class MappingParseException extends Exception { + private static final long serialVersionUID = -5487280332892507236L; private int m_line; private String m_message; - public MappingParseException( int line, String message ) - { + public MappingParseException(int line, String message) { m_line = line; m_message = message; } @Override - public String getMessage( ) - { + public String getMessage() { return "Line " + m_line + ": " + m_message; } } diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 3a39d100..c5e38f4b 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -26,230 +26,182 @@ import com.google.common.collect.Sets; import cuchaz.enigma.Util; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; -public class Mappings implements Serializable -{ +public class Mappings implements Serializable { + private static final long serialVersionUID = 4649790259460259026L; protected Map m_classesByObf; protected Map m_classesByDeobf; - public Mappings( ) - { + public Mappings() { m_classesByObf = Maps.newHashMap(); m_classesByDeobf = Maps.newHashMap(); } - public Mappings( Iterable classes ) - { + public Mappings(Iterable classes) { this(); - for( ClassMapping classMapping : classes ) - { - m_classesByObf.put( classMapping.getObfName(), classMapping ); - if( classMapping.getDeobfName() != null ) - { - m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ); + for (ClassMapping classMapping : classes) { + m_classesByObf.put(classMapping.getObfName(), classMapping); + if (classMapping.getDeobfName() != null) { + m_classesByDeobf.put(classMapping.getDeobfName(), classMapping); } } } - public static Mappings newFromResource( String resource ) - throws IOException - { + public static Mappings newFromResource(String resource) throws IOException { InputStream in = null; - try - { - in = Mappings.class.getResourceAsStream( resource ); - return newFromStream( in ); - } - finally - { - Util.closeQuietly( in ); + try { + in = Mappings.class.getResourceAsStream(resource); + return newFromStream(in); + } finally { + Util.closeQuietly(in); } } - public Collection classes( ) - { - assert( m_classesByObf.size() >= m_classesByDeobf.size() ); + public Collection classes() { + assert (m_classesByObf.size() >= m_classesByDeobf.size()); return m_classesByObf.values(); } - public void addClassMapping( ClassMapping classMapping ) - { - if( m_classesByObf.containsKey( classMapping.getObfName() ) ) - { - throw new Error( "Already have mapping for " + classMapping.getObfName() ); + public void addClassMapping(ClassMapping classMapping) { + if (m_classesByObf.containsKey(classMapping.getObfName())) { + throw new Error("Already have mapping for " + classMapping.getObfName()); } - boolean obfWasAdded = m_classesByObf.put( classMapping.getObfName(), classMapping ) == null; - assert( obfWasAdded ); - if( classMapping.getDeobfName() != null ) - { - if( m_classesByDeobf.containsKey( classMapping.getDeobfName() ) ) - { - throw new Error( "Already have mapping for " + classMapping.getDeobfName() ); + boolean obfWasAdded = m_classesByObf.put(classMapping.getObfName(), classMapping) == null; + assert (obfWasAdded); + if (classMapping.getDeobfName() != null) { + if (m_classesByDeobf.containsKey(classMapping.getDeobfName())) { + throw new Error("Already have mapping for " + classMapping.getDeobfName()); } - boolean deobfWasAdded = m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ) == null; - assert( deobfWasAdded ); + boolean deobfWasAdded = m_classesByDeobf.put(classMapping.getDeobfName(), classMapping) == null; + assert (deobfWasAdded); } } - public void removeClassMapping( ClassMapping classMapping ) - { - boolean obfWasRemoved = m_classesByObf.remove( classMapping.getObfName() ) != null; - assert( obfWasRemoved ); - if( classMapping.getDeobfName() != null ) - { - boolean deobfWasRemoved = m_classesByDeobf.remove( classMapping.getDeobfName() ) != null; - assert( deobfWasRemoved ); + public void removeClassMapping(ClassMapping classMapping) { + boolean obfWasRemoved = m_classesByObf.remove(classMapping.getObfName()) != null; + assert (obfWasRemoved); + if (classMapping.getDeobfName() != null) { + boolean deobfWasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (deobfWasRemoved); } } - public ClassMapping getClassByObf( ClassEntry entry ) - { - return getClassByObf( entry.getName() ); + public ClassMapping getClassByObf(ClassEntry entry) { + return getClassByObf(entry.getName()); } - public ClassMapping getClassByObf( String obfName ) - { - return m_classesByObf.get( obfName ); + public ClassMapping getClassByObf(String obfName) { + return m_classesByObf.get(obfName); } - public ClassMapping getClassByDeobf( ClassEntry entry ) - { - return getClassByDeobf( entry.getName() ); + public ClassMapping getClassByDeobf(ClassEntry entry) { + return getClassByDeobf(entry.getName()); } - public ClassMapping getClassByDeobf( String deobfName ) - { - return m_classesByDeobf.get( deobfName ); + public ClassMapping getClassByDeobf(String deobfName) { + return m_classesByDeobf.get(deobfName); } - public Translator getTranslator( TranslationDirection direction ) - { - switch( direction ) - { + public Translator getTranslator(TranslationDirection direction) { + switch (direction) { case Deobfuscating: - return new Translator( direction, m_classesByObf ); + return new Translator(direction, m_classesByObf); case Obfuscating: // fill in the missing deobf class entries with obf entries Map classes = Maps.newHashMap(); - for( ClassMapping classMapping : classes() ) - { - if( classMapping.getDeobfName() != null ) - { - classes.put( classMapping.getDeobfName(), classMapping ); - } - else - { - classes.put( classMapping.getObfName(), classMapping ); + for (ClassMapping classMapping : classes()) { + if (classMapping.getDeobfName() != null) { + classes.put(classMapping.getDeobfName(), classMapping); + } else { + classes.put(classMapping.getObfName(), classMapping); } } - return new Translator( direction, classes ); + return new Translator(direction, classes); default: - throw new Error( "Invalid translation direction!" ); + throw new Error("Invalid translation direction!"); } } - public static Mappings newFromStream( InputStream in ) - throws IOException - { - try - { - return (Mappings)new ObjectInputStream( new GZIPInputStream( in ) ).readObject(); - } - catch( ClassNotFoundException ex ) - { - throw new Error( ex ); + public static Mappings newFromStream(InputStream in) throws IOException { + try { + return (Mappings)new ObjectInputStream(new GZIPInputStream(in)).readObject(); + } catch (ClassNotFoundException ex) { + throw new Error(ex); } } @Override - public String toString( ) - { + public String toString() { StringBuilder buf = new StringBuilder(); - for( ClassMapping classMapping : m_classesByObf.values() ) - { - buf.append( classMapping.toString() ); - buf.append( "\n" ); + for (ClassMapping classMapping : m_classesByObf.values()) { + buf.append(classMapping.toString()); + buf.append("\n"); } return buf.toString(); } - public void renameObfClass( String oldObfName, String newObfName ) - { - for( ClassMapping classMapping : new ArrayList( classes() ) ) - { - if( classMapping.renameObfClass( oldObfName, newObfName ) ) - { - boolean wasRemoved = m_classesByObf.remove( oldObfName ) != null; - assert( wasRemoved ); - boolean wasAdded = m_classesByObf.put( newObfName, classMapping ) == null; - assert( wasAdded ); + public void renameObfClass(String oldObfName, String newObfName) { + for (ClassMapping classMapping : new ArrayList(classes())) { + if (classMapping.renameObfClass(oldObfName, newObfName)) { + boolean wasRemoved = m_classesByObf.remove(oldObfName) != null; + assert (wasRemoved); + boolean wasAdded = m_classesByObf.put(newObfName, classMapping) == null; + assert (wasAdded); } } } - public Set getAllObfClassNames( ) - { + public Set getAllObfClassNames() { final Set classNames = Sets.newHashSet(); - for( ClassMapping classMapping : classes() ) - { + for (ClassMapping classMapping : classes()) { // add the class name - classNames.add( classMapping.getObfName() ); + classNames.add(classMapping.getObfName()); // add classes from method signatures - for( MethodMapping methodMapping : classMapping.methods() ) - { - SignatureUpdater.update( methodMapping.getObfSignature(), new ClassNameUpdater( ) - { + for (MethodMapping methodMapping : classMapping.methods()) { + SignatureUpdater.update(methodMapping.getObfSignature(), new ClassNameUpdater() { @Override - public String update( String className ) - { - classNames.add( className ); + public String update(String className) { + classNames.add(className); return className; } - } ); + }); } } return classNames; } - - public boolean containsDeobfClass( String deobfName ) - { - return m_classesByDeobf.containsKey( deobfName ); + + public boolean containsDeobfClass(String deobfName) { + return m_classesByDeobf.containsKey(deobfName); } - public boolean containsDeobfField( ClassEntry obfClassEntry, String deobfName ) - { - ClassMapping classMapping = m_classesByObf.get( obfClassEntry.getName() ); - if( classMapping != null ) - { - return classMapping.containsDeobfField( deobfName ); + public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { + ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName()); + if (classMapping != null) { + return classMapping.containsDeobfField(deobfName); } return false; } - - public boolean containsDeobfMethod( ClassEntry obfClassEntry, String deobfName, String deobfSignature ) - { - ClassMapping classMapping = m_classesByObf.get( obfClassEntry.getName() ); - if( classMapping != null ) - { - return classMapping.containsDeobfMethod( deobfName, deobfSignature ); + + public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, String deobfSignature) { + ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName()); + if (classMapping != null) { + return classMapping.containsDeobfMethod(deobfName, deobfSignature); } return false; } - - public boolean containsArgument( BehaviorEntry obfBehaviorEntry, String name ) - { - ClassMapping classMapping = m_classesByObf.get( obfBehaviorEntry.getClassName() ); - if( classMapping != null ) - { - return classMapping.containsArgument( obfBehaviorEntry, name ); + + public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { + ClassMapping classMapping = m_classesByObf.get(obfBehaviorEntry.getClassName()); + if (classMapping != null) { + return classMapping.containsArgument(obfBehaviorEntry, name); } return false; } diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index 4bd9f121..72e829d5 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -20,209 +20,157 @@ import com.google.common.collect.Queues; import cuchaz.enigma.Constants; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; -public class MappingsReader -{ - public Mappings read( Reader in ) - throws IOException, MappingParseException - { - return read( new BufferedReader( in ) ); +public class MappingsReader { + + public Mappings read(Reader in) throws IOException, MappingParseException { + return read(new BufferedReader(in)); } - public Mappings read( BufferedReader in ) - throws IOException, MappingParseException - { + public Mappings read(BufferedReader in) throws IOException, MappingParseException { Mappings mappings = new Mappings(); Deque mappingStack = Queues.newArrayDeque(); int lineNumber = 0; String line = null; - while( ( line = in.readLine() ) != null ) - { + while ( (line = in.readLine()) != null) { lineNumber++; // strip comments - int commentPos = line.indexOf( '#' ); - if( commentPos >= 0 ) - { - line = line.substring( 0, commentPos ); + int commentPos = line.indexOf('#'); + if (commentPos >= 0) { + line = line.substring(0, commentPos); } // skip blank lines - if( line.trim().length() <= 0 ) - { + if (line.trim().length() <= 0) { continue; } // get the indent of this line int indent = 0; - for( int i=0; i implementations = m_index.getRelatedMethodImplementations( obf ); + public void setMethodTreeName(MethodEntry obf, String deobfName) { + Set implementations = m_index.getRelatedMethodImplementations(obf); - deobfName = NameValidator.validateMethodName( deobfName ); - for( MethodEntry entry : implementations ) - { - String deobfSignature = m_mappings.getTranslator( TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); - MethodEntry targetEntry = new MethodEntry( entry.getClassEntry(), deobfName, deobfSignature ); - if( m_mappings.containsDeobfMethod( entry.getClassEntry(), deobfName, entry.getSignature() ) || m_index.containsObfBehavior( targetEntry ) ) - { - String deobfClassName = m_mappings.getTranslator( TranslationDirection.Deobfuscating ).translateClass( entry.getClassName() ); - throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + deobfClassName ); + deobfName = NameValidator.validateMethodName(deobfName); + for (MethodEntry entry : implementations) { + String deobfSignature = m_mappings.getTranslator(TranslationDirection.Deobfuscating).translateSignature(obf.getSignature()); + MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, deobfSignature); + if (m_mappings.containsDeobfMethod(entry.getClassEntry(), deobfName, entry.getSignature()) || m_index.containsObfBehavior(targetEntry)) { + String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating).translateClass(entry.getClassName()); + throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); } } - for( MethodEntry entry : implementations ) - { - setMethodName( entry, deobfName ); + for (MethodEntry entry : implementations) { + setMethodName(entry, deobfName); } } - - public void setMethodName( MethodEntry obf, String deobfName ) - { - deobfName = NameValidator.validateMethodName( deobfName ); - MethodEntry targetEntry = new MethodEntry( obf.getClassEntry(), deobfName, obf.getSignature() ); - if( m_mappings.containsDeobfMethod( obf.getClassEntry(), deobfName, obf.getSignature() ) || m_index.containsObfBehavior( targetEntry ) ) - { - String deobfClassName = m_mappings.getTranslator( TranslationDirection.Deobfuscating ).translateClass( obf.getClassName() ); - throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + deobfClassName ); + + public void setMethodName(MethodEntry obf, String deobfName) { + deobfName = NameValidator.validateMethodName(deobfName); + MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); + if (m_mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) || m_index.containsObfBehavior(targetEntry)) { + String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating).translateClass(obf.getClassName()); + throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); } - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); - classMapping.setMethodName( obf.getName(), obf.getSignature(), deobfName ); + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); } - public void removeMethodTreeMapping( MethodEntry obf ) - { - for( MethodEntry implementation : m_index.getRelatedMethodImplementations( obf ) ) - { - removeMethodMapping( implementation ); + public void removeMethodTreeMapping(MethodEntry obf) { + for (MethodEntry implementation : m_index.getRelatedMethodImplementations(obf)) { + removeMethodMapping(implementation); } } - public void removeMethodMapping( MethodEntry obf ) - { - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); - classMapping.setMethodName( obf.getName(), obf.getSignature(), null ); + public void removeMethodMapping(MethodEntry obf) { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getSignature(), null); } - public void markMethodTreeAsDeobfuscated( MethodEntry obf ) - { - for( MethodEntry implementation : m_index.getRelatedMethodImplementations( obf ) ) - { - markMethodAsDeobfuscated( implementation ); + public void markMethodTreeAsDeobfuscated(MethodEntry obf) { + for (MethodEntry implementation : m_index.getRelatedMethodImplementations(obf)) { + markMethodAsDeobfuscated(implementation); } } - public void markMethodAsDeobfuscated( MethodEntry obf ) - { - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); - classMapping.setMethodName( obf.getName(), obf.getSignature(), obf.getName() ); + public void markMethodAsDeobfuscated(MethodEntry obf) { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); } - public void setArgumentName( ArgumentEntry obf, String deobfName ) - { - deobfName = NameValidator.validateArgumentName( deobfName ); + public void setArgumentName(ArgumentEntry obf, String deobfName) { + deobfName = NameValidator.validateArgumentName(deobfName); // NOTE: don't need to check arguments for name collisions with names determined by Procyon - if( m_mappings.containsArgument( obf.getBehaviorEntry(), deobfName ) ) - { - throw new IllegalNameException( deobfName, "There is already an argument with that name" ); + if (m_mappings.containsArgument(obf.getBehaviorEntry(), deobfName)) { + throw new IllegalNameException(deobfName, "There is already an argument with that name"); } - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); - classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName ); + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); } - public void removeArgumentMapping( ArgumentEntry obf ) - { - ClassMapping classMapping = getClassMappingOrInnerClassMapping( obf.getClassEntry() ); - classMapping.removeArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex() ); + public void removeArgumentMapping(ArgumentEntry obf) { + ClassMapping classMapping = getClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); } - public void markArgumentAsDeobfuscated( ArgumentEntry obf ) - { - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); - classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName() ); + public void markArgumentAsDeobfuscated(ArgumentEntry obf) { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); } - public boolean moveFieldToObfClass( ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass ) - { - classMapping.removeFieldMapping( fieldMapping ); - ClassMapping targetClassMapping = getOrCreateClassMapping( obfClass ); - if( !targetClassMapping.containsObfField( fieldMapping.getObfName() ) ) - { - if( !targetClassMapping.containsDeobfField( fieldMapping.getDeobfName() ) ) - { - targetClassMapping.addFieldMapping( fieldMapping ); + public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { + classMapping.removeFieldMapping(fieldMapping); + ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); + if (!targetClassMapping.containsObfField(fieldMapping.getObfName())) { + if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName())) { + targetClassMapping.addFieldMapping(fieldMapping); return true; - } - else - { - System.err.println( "WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName() ); + } else { + System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName()); } } return false; } - public boolean moveMethodToObfClass( ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass ) - { - classMapping.removeMethodMapping( methodMapping ); - ClassMapping targetClassMapping = getOrCreateClassMapping( obfClass ); - if( !targetClassMapping.containsObfMethod( methodMapping.getObfName(), methodMapping.getObfSignature() ) ) - { - if( !targetClassMapping.containsDeobfMethod( methodMapping.getDeobfName(), methodMapping.getObfSignature() ) ) - { - targetClassMapping.addMethodMapping( methodMapping ); + public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { + classMapping.removeMethodMapping(methodMapping); + ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); + if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { + if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { + targetClassMapping.addMethodMapping(methodMapping); return true; - } - else - { - System.err.println( "WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature() ); + } else { + System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); } } return false; } - public void write( OutputStream out ) - throws IOException - { + public void write(OutputStream out) throws IOException { // TEMP: just use the object output for now. We can find a more efficient storage format later - GZIPOutputStream gzipout = new GZIPOutputStream( out ); - ObjectOutputStream oout = new ObjectOutputStream( gzipout ); - oout.writeObject( this ); + GZIPOutputStream gzipout = new GZIPOutputStream(out); + ObjectOutputStream oout = new ObjectOutputStream(gzipout); + oout.writeObject(this); gzipout.finish(); } - private ClassMapping getClassMapping( ClassEntry obfClassEntry ) - { - return m_mappings.m_classesByObf.get( obfClassEntry.getOuterClassName() ); + private ClassMapping getClassMapping(ClassEntry obfClassEntry) { + return m_mappings.m_classesByObf.get(obfClassEntry.getOuterClassName()); } - private ClassMapping getOrCreateClassMapping( ClassEntry obfClassEntry ) - { + private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { String obfClassName = obfClassEntry.getOuterClassName(); - ClassMapping classMapping = m_mappings.m_classesByObf.get( obfClassName ); - if( classMapping == null ) - { - classMapping = new ClassMapping( obfClassName ); - boolean obfWasAdded = m_mappings.m_classesByObf.put( classMapping.getObfName(), classMapping ) == null; - assert( obfWasAdded ); + ClassMapping classMapping = m_mappings.m_classesByObf.get(obfClassName); + if (classMapping == null) { + classMapping = new ClassMapping(obfClassName); + boolean obfWasAdded = m_mappings.m_classesByObf.put(classMapping.getObfName(), classMapping) == null; + assert (obfWasAdded); } return classMapping; } - private ClassMapping getClassMappingOrInnerClassMapping( ClassEntry obfClassEntry ) - { - ClassMapping classMapping = getClassMapping( obfClassEntry ); - if( obfClassEntry.isInDefaultPackage() ) - { - classMapping = classMapping.getInnerClassByObf( obfClassEntry.getInnerClassName() ); + private ClassMapping getClassMappingOrInnerClassMapping(ClassEntry obfClassEntry) { + ClassMapping classMapping = getClassMapping(obfClassEntry); + if (obfClassEntry.isInDefaultPackage()) { + classMapping = classMapping.getInnerClassByObf(obfClassEntry.getInnerClassName()); } return classMapping; } - private ClassMapping getOrCreateClassMappingOrInnerClassMapping( ClassEntry obfClassEntry ) - { - ClassMapping classMapping = getOrCreateClassMapping( obfClassEntry ); - if( obfClassEntry.isInnerClass() ) - { - classMapping = classMapping.getOrCreateInnerClass( obfClassEntry.getInnerClassName() ); + private ClassMapping getOrCreateClassMappingOrInnerClassMapping(ClassEntry obfClassEntry) { + ClassMapping classMapping = getOrCreateClassMapping(obfClassEntry); + if (obfClassEntry.isInnerClass()) { + classMapping = classMapping.getOrCreateInnerClass(obfClassEntry.getInnerClassName()); } return classMapping; } diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java index 3c86dfc0..5ac409fc 100644 --- a/src/cuchaz/enigma/mapping/MappingsWriter.java +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -17,105 +17,71 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class MappingsWriter -{ - public void write( Writer out, Mappings mappings ) - throws IOException - { - write( new PrintWriter( out ), mappings ); +public class MappingsWriter { + + public void write(Writer out, Mappings mappings) throws IOException { + write(new PrintWriter(out), mappings); } - public void write( PrintWriter out, Mappings mappings ) - throws IOException - { - for( ClassMapping classMapping : sorted( mappings.classes() ) ) - { - write( out, classMapping, 0 ); + public void write(PrintWriter out, Mappings mappings) throws IOException { + for (ClassMapping classMapping : sorted(mappings.classes())) { + write(out, classMapping, 0); } } - private void write( PrintWriter out, ClassMapping classMapping, int depth ) - throws IOException - { - if( classMapping.getDeobfName() == null ) - { - out.format( "%sCLASS %s\n", getIndent( depth ), classMapping.getObfName() ); - } - else - { - out.format( "%sCLASS %s %s\n", getIndent( depth ), classMapping.getObfName(), classMapping.getDeobfName() ); + private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { + if (classMapping.getDeobfName() == null) { + out.format("%sCLASS %s\n", getIndent(depth), classMapping.getObfName()); + } else { + out.format("%sCLASS %s %s\n", getIndent(depth), classMapping.getObfName(), classMapping.getDeobfName()); } - for( ClassMapping innerClassMapping : sorted( classMapping.innerClasses() ) ) - { - write( out, innerClassMapping, depth + 1 ); + for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { + write(out, innerClassMapping, depth + 1); } - for( FieldMapping fieldMapping : sorted( classMapping.fields() ) ) - { - write( out, fieldMapping, depth + 1 ); + for (FieldMapping fieldMapping : sorted(classMapping.fields())) { + write(out, fieldMapping, depth + 1); } - for( MethodMapping methodMapping : sorted( classMapping.methods() ) ) - { - write( out, methodMapping, depth + 1 ); + for (MethodMapping methodMapping : sorted(classMapping.methods())) { + write(out, methodMapping, depth + 1); } } - - private void write( PrintWriter out, FieldMapping fieldMapping, int depth ) - throws IOException - { - out.format( "%sFIELD %s %s\n", getIndent( depth ), fieldMapping.getObfName(), fieldMapping.getDeobfName() ); + + private void write(PrintWriter out, FieldMapping fieldMapping, int depth) throws IOException { + out.format("%sFIELD %s %s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName()); } - private void write( PrintWriter out, MethodMapping methodMapping, int depth ) - throws IOException - { - if( methodMapping.getDeobfName() == null ) - { - out.format( "%sMETHOD %s %s\n", - getIndent( depth ), - methodMapping.getObfName(), methodMapping.getObfSignature() - ); - } - else - { - out.format( "%sMETHOD %s %s %s\n", - getIndent( depth ), - methodMapping.getObfName(), methodMapping.getDeobfName(), - methodMapping.getObfSignature() - ); + private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { + if (methodMapping.getDeobfName() == null) { + out.format("%sMETHOD %s %s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature()); + } else { + out.format("%sMETHOD %s %s %s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature()); } - for( ArgumentMapping argumentMapping : sorted( methodMapping.arguments() ) ) - { - write( out, argumentMapping, depth + 1 ); + for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { + write(out, argumentMapping, depth + 1); } } - - private void write( PrintWriter out, ArgumentMapping argumentMapping, int depth ) - throws IOException - { - out.format( "%sARG %d %s\n", getIndent( depth ), argumentMapping.getIndex(), argumentMapping.getName() ); + + private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) throws IOException { + out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName()); } - private > List sorted( Iterable classes ) - { + private > List sorted(Iterable classes) { List out = new ArrayList(); - for( T t : classes ) - { - out.add( t ); + for (T t : classes) { + out.add(t); } - Collections.sort( out ); + Collections.sort(out); return out; } - private String getIndent( int depth ) - { + private String getIndent(int depth) { StringBuilder buf = new StringBuilder(); - for( int i=0; i -{ +public class MethodMapping implements Serializable, Comparable { + private static final long serialVersionUID = -4409570216084263978L; private String m_obfName; @@ -25,165 +25,135 @@ public class MethodMapping implements Serializable, Comparable private String m_obfSignature; private Map m_arguments; - public MethodMapping( String obfName, String obfSignature ) - { - this( obfName, obfSignature, null ); + public MethodMapping(String obfName, String obfSignature) { + this(obfName, obfSignature, null); } - public MethodMapping( String obfName, String obfSignature, String deobfName ) - { - if( obfName == null ) - { - throw new IllegalArgumentException( "obf name cannot be null!" ); + public MethodMapping(String obfName, String obfSignature, String deobfName) { + if (obfName == null) { + throw new IllegalArgumentException("obf name cannot be null!"); } - if( obfSignature == null ) - { - throw new IllegalArgumentException( "obf signature cannot be null!" ); + if (obfSignature == null) { + throw new IllegalArgumentException("obf signature cannot be null!"); } m_obfName = obfName; - m_deobfName = NameValidator.validateMethodName( deobfName ); + m_deobfName = NameValidator.validateMethodName(deobfName); m_obfSignature = obfSignature; m_arguments = new TreeMap(); } - - public String getObfName( ) - { + + public String getObfName() { return m_obfName; } - public String getDeobfName( ) - { + public String getDeobfName() { return m_deobfName; } - public void setDeobfName( String val ) - { - m_deobfName = NameValidator.validateMethodName( val ); + + public void setDeobfName(String val) { + m_deobfName = NameValidator.validateMethodName(val); } - public String getObfSignature( ) - { + public String getObfSignature() { return m_obfSignature; } - public Iterable arguments( ) - { + public Iterable arguments() { return m_arguments.values(); } - public boolean isConstructor( ) - { - return m_obfName.startsWith( "<" ); + public boolean isConstructor() { + return m_obfName.startsWith("<"); } - public void addArgumentMapping( ArgumentMapping argumentMapping ) - { - boolean wasAdded = m_arguments.put( argumentMapping.getIndex(), argumentMapping ) == null; - assert( wasAdded ); + public void addArgumentMapping(ArgumentMapping argumentMapping) { + boolean wasAdded = m_arguments.put(argumentMapping.getIndex(), argumentMapping) == null; + assert (wasAdded); } - public String getObfArgumentName( int index ) - { - ArgumentMapping argumentMapping = m_arguments.get( index ); - if( argumentMapping != null ) - { + public String getObfArgumentName(int index) { + ArgumentMapping argumentMapping = m_arguments.get(index); + if (argumentMapping != null) { return argumentMapping.getName(); } return null; } - public String getDeobfArgumentName( int index ) - { - ArgumentMapping argumentMapping = m_arguments.get( index ); - if( argumentMapping != null ) - { + public String getDeobfArgumentName(int index) { + ArgumentMapping argumentMapping = m_arguments.get(index); + if (argumentMapping != null) { return argumentMapping.getName(); } return null; } - public void setArgumentName( int index, String name ) - { - ArgumentMapping argumentMapping = m_arguments.get( index ); - if( argumentMapping == null ) - { - argumentMapping = new ArgumentMapping( index, name ); - boolean wasAdded = m_arguments.put( index, argumentMapping ) == null; - assert( wasAdded ); - } - else - { - argumentMapping.setName( name ); + public void setArgumentName(int index, String name) { + ArgumentMapping argumentMapping = m_arguments.get(index); + if (argumentMapping == null) { + argumentMapping = new ArgumentMapping(index, name); + boolean wasAdded = m_arguments.put(index, argumentMapping) == null; + assert (wasAdded); + } else { + argumentMapping.setName(name); } } - public void removeArgumentName( int index ) - { - boolean wasRemoved = m_arguments.remove( index ) != null; - assert( wasRemoved ); + public void removeArgumentName(int index) { + boolean wasRemoved = m_arguments.remove(index) != null; + assert (wasRemoved); } @Override - public String toString( ) - { + public String toString() { StringBuilder buf = new StringBuilder(); - buf.append( "\t" ); - buf.append( m_obfName ); - buf.append( " <-> " ); - buf.append( m_deobfName ); - buf.append( "\n" ); - buf.append( "\t" ); - buf.append( m_obfSignature ); - buf.append( "\n" ); - buf.append( "\tArguments:\n" ); - for( ArgumentMapping argumentMapping : m_arguments.values() ) - { - buf.append( "\t\t" ); - buf.append( argumentMapping.getIndex() ); - buf.append( " -> " ); - buf.append( argumentMapping.getName() ); - buf.append( "\n" ); + buf.append("\t"); + buf.append(m_obfName); + buf.append(" <-> "); + buf.append(m_deobfName); + buf.append("\n"); + buf.append("\t"); + buf.append(m_obfSignature); + buf.append("\n"); + buf.append("\tArguments:\n"); + for (ArgumentMapping argumentMapping : m_arguments.values()) { + buf.append("\t\t"); + buf.append(argumentMapping.getIndex()); + buf.append(" -> "); + buf.append(argumentMapping.getName()); + buf.append("\n"); } return buf.toString(); } @Override - public int compareTo( MethodMapping other ) - { - return ( m_obfName + m_obfSignature ).compareTo( other.m_obfName + other.m_obfSignature ); + public int compareTo(MethodMapping other) { + return (m_obfName + m_obfSignature).compareTo(other.m_obfName + other.m_obfSignature); } - - public boolean renameObfClass( final String oldObfClassName, final String newObfClassName ) - { + + public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { // rename obf classes in the signature - String newSignature = SignatureUpdater.update( m_obfSignature, new ClassNameUpdater( ) - { + String newSignature = SignatureUpdater.update(m_obfSignature, new ClassNameUpdater() { @Override - public String update( String className ) - { - if( className.equals( oldObfClassName ) ) - { + public String update(String className) { + if (className.equals(oldObfClassName)) { return newObfClassName; } return className; } - } ); + }); - if( newSignature != m_obfSignature ) - { + if (newSignature != m_obfSignature) { m_obfSignature = newSignature; return true; } return false; } - - public boolean containsArgument( String name ) - { - for( ArgumentMapping argumentMapping : m_arguments.values() ) - { - if( argumentMapping.getName().equals( name ) ) - { + + public boolean containsArgument(String name) { + for (ArgumentMapping argumentMapping : m_arguments.values()) { + if (argumentMapping.getName().equals(name)) { return true; } } diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java index c6ae5969..35a17f90 100644 --- a/src/cuchaz/enigma/mapping/NameValidator.java +++ b/src/cuchaz/enigma/mapping/NameValidator.java @@ -16,82 +16,65 @@ import java.util.regex.Pattern; import javassist.bytecode.Descriptor; -public class NameValidator -{ +public class NameValidator { + private static final Pattern IdentifierPattern; private static final Pattern ClassPattern; private static final List ReservedWords = Arrays.asList( - "abstract", "continue", "for", "new", "switch", - "assert", "default", "goto", "package", "synchronized", - "boolean", "do", "if", "private", "this", - "break", "double", "implements", "protected", "throw", - "byte", "else", "import", "public", "throws", - "case", "enum", "instanceof", "return", "transient", - "catch", "extends", "int", "short", "try", - "char", "final", "interface", "static", "void", - "class", "finally", "long", "strictfp", "volatile", - "const", "float", "native", "super", "while" + "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", + "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", + "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", + "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", + "long", "strictfp", "volatile", "const", "float", "native", "super", "while" ); - static - { + static { + // java allows all kinds of weird characters... StringBuilder startChars = new StringBuilder(); StringBuilder partChars = new StringBuilder(); - for( int i = Character.MIN_CODE_POINT; i <= Character.MAX_CODE_POINT; i++ ) - { - if( Character.isJavaIdentifierStart( i ) ) - { - startChars.appendCodePoint( i ); + for (int i = Character.MIN_CODE_POINT; i <= Character.MAX_CODE_POINT; i++) { + if (Character.isJavaIdentifierStart(i)) { + startChars.appendCodePoint(i); } - if( Character.isJavaIdentifierPart( i ) ) - { - partChars.appendCodePoint( i ); + if (Character.isJavaIdentifierPart(i)) { + partChars.appendCodePoint(i); } } String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; - IdentifierPattern = Pattern.compile( identifierRegex ); - ClassPattern = Pattern.compile( String.format( "^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex ) ); + IdentifierPattern = Pattern.compile(identifierRegex); + ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex)); } - public static String validateClassName( String name, boolean packageRequired ) - { - if( name == null ) - { + public static String validateClassName(String name, boolean packageRequired) { + if (name == null) { return null; } - if( !ClassPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) - { - throw new IllegalNameException( name, "This doesn't look like a legal class name" ); + if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { + throw new IllegalNameException(name, "This doesn't look like a legal class name"); } - if( packageRequired && new ClassEntry( name ).getPackageName() == null ) - { - throw new IllegalNameException( name, "Class must be in a package" ); + if (packageRequired && new ClassEntry(name).getPackageName() == null) { + throw new IllegalNameException(name, "Class must be in a package"); } - return Descriptor.toJvmName( name ); + return Descriptor.toJvmName(name); } - public static String validateFieldName( String name ) - { - if( name == null ) - { + public static String validateFieldName(String name) { + if (name == null) { return null; } - if( !IdentifierPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) - { - throw new IllegalNameException( name, "This doesn't look like a legal identifier" ); + if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) { + throw new IllegalNameException(name, "This doesn't look like a legal identifier"); } return name; } - public static String validateMethodName( String name ) - { - return validateFieldName( name ); + public static String validateMethodName(String name) { + return validateFieldName(name); } - public static String validateArgumentName( String name ) - { - return validateFieldName( name ); + public static String validateArgumentName(String name) { + return validateFieldName(name); } } diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java index 809473e5..3477cd56 100644 --- a/src/cuchaz/enigma/mapping/SignatureUpdater.java +++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java @@ -16,84 +16,63 @@ import java.util.List; import com.google.common.collect.Lists; -public class SignatureUpdater -{ - public interface ClassNameUpdater - { - String update( String className ); +public class SignatureUpdater { + + public interface ClassNameUpdater { + String update(String className); } - public static String update( String signature, ClassNameUpdater updater ) - { - try - { + public static String update(String signature, ClassNameUpdater updater) { + try { StringBuilder buf = new StringBuilder(); // read the signature character-by-character - StringReader reader = new StringReader( signature ); + StringReader reader = new StringReader(signature); int i = -1; - while( ( i = reader.read() ) != -1 ) - { + while ( (i = reader.read()) != -1) { char c = (char)i; // does this character start a class name? - if( c == 'L' ) - { + if (c == 'L') { // update the class name and add it to the buffer - buf.append( 'L' ); - String className = readClass( reader ); - if( className == null ) - { - throw new IllegalArgumentException( "Malformed signature: " + signature ); + buf.append('L'); + String className = readClass(reader); + if (className == null) { + throw new IllegalArgumentException("Malformed signature: " + signature); } - buf.append( updater.update( className ) ); - buf.append( ';' ); - } - else - { + buf.append(updater.update(className)); + buf.append(';'); + } else { // copy the character into the buffer - buf.append( c ); + buf.append(c); } } return buf.toString(); - } - catch( IOException ex ) - { + } catch (IOException ex) { // I'm pretty sure a StringReader will never throw one of these - throw new Error( ex ); + throw new Error(ex); } } - private static String readClass( StringReader reader ) - throws IOException - { + private static String readClass(StringReader reader) throws IOException { // read all the characters in the buffer until we hit a ';' // remember to treat generics correctly StringBuilder buf = new StringBuilder(); int depth = 0; int i = -1; - while( ( i = reader.read() ) != -1 ) - { + while ( (i = reader.read()) != -1) { char c = (char)i; - if( c == '<' ) - { + if (c == '<') { depth++; - } - else if( c == '>' ) - { + } else if (c == '>') { depth--; - } - else if( depth == 0 ) - { - if( c == ';' ) - { + } else if (depth == 0) { + if (c == ';') { return buf.toString(); - } - else - { - buf.append( c ); + } else { + buf.append(c); } } } @@ -101,18 +80,15 @@ public class SignatureUpdater return null; } - public static List getClasses( String signature ) - { + public static List getClasses(String signature) { final List classNames = Lists.newArrayList(); - update( signature, new ClassNameUpdater( ) - { + update(signature, new ClassNameUpdater() { @Override - public String update( String className ) - { - classNames.add( className ); + public String update(String className) { + classNames.add(className); return className; } - } ); + }); return classNames; } } diff --git a/src/cuchaz/enigma/mapping/TranslationDirection.java b/src/cuchaz/enigma/mapping/TranslationDirection.java index 79ae0d32..d1b14cd5 100644 --- a/src/cuchaz/enigma/mapping/TranslationDirection.java +++ b/src/cuchaz/enigma/mapping/TranslationDirection.java @@ -10,25 +10,20 @@ ******************************************************************************/ package cuchaz.enigma.mapping; - -public enum TranslationDirection -{ - Deobfuscating - { +public enum TranslationDirection { + + Deobfuscating { @Override - public T choose( T deobfChoice, T obfChoice ) - { + public T choose(T deobfChoice, T obfChoice) { return deobfChoice; } }, - Obfuscating - { + Obfuscating { @Override - public T choose( T deobfChoice, T obfChoice ) - { + public T choose(T deobfChoice, T obfChoice) { return obfChoice; } }; - - public abstract T choose( T deobfChoice, T obfChoice ); + + public abstract T choose(T deobfChoice, T obfChoice); } diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 6cb52402..d8d9f480 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -16,277 +16,203 @@ import com.google.common.collect.Maps; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; -public class Translator -{ +public class Translator { + private TranslationDirection m_direction; private Map m_classes; - public Translator( ) - { + public Translator() { m_direction = null; m_classes = Maps.newHashMap(); } - public Translator( TranslationDirection direction, Map classes ) - { + public Translator(TranslationDirection direction, Map classes) { m_direction = direction; m_classes = classes; } - @SuppressWarnings( "unchecked" ) - public T translateEntry( T entry ) - { - if( entry instanceof ClassEntry ) - { - return (T)translateEntry( (ClassEntry)entry ); - } - else if( entry instanceof FieldEntry ) - { - return (T)translateEntry( (FieldEntry)entry ); - } - else if( entry instanceof MethodEntry ) - { - return (T)translateEntry( (MethodEntry)entry ); - } - else if( entry instanceof ConstructorEntry ) - { - return (T)translateEntry( (ConstructorEntry)entry ); - } - else if( entry instanceof ArgumentEntry ) - { - return (T)translateEntry( (ArgumentEntry)entry ); - } - else - { - throw new Error( "Unknown entry type: " + entry.getClass().getName() ); + @SuppressWarnings("unchecked") + public T translateEntry(T entry) { + if (entry instanceof ClassEntry) { + return (T)translateEntry((ClassEntry)entry); + } else if (entry instanceof FieldEntry) { + return (T)translateEntry((FieldEntry)entry); + } else if (entry instanceof MethodEntry) { + return (T)translateEntry((MethodEntry)entry); + } else if (entry instanceof ConstructorEntry) { + return (T)translateEntry((ConstructorEntry)entry); + } else if (entry instanceof ArgumentEntry) { + return (T)translateEntry((ArgumentEntry)entry); + } else { + throw new Error("Unknown entry type: " + entry.getClass().getName()); } } - public String translateClass( String className ) - { - return translate( new ClassEntry( className ) ); + public String translateClass(String className) { + return translate(new ClassEntry(className)); } - public String translate( ClassEntry in ) - { - ClassMapping classMapping = m_classes.get( in.getOuterClassName() ); - if( classMapping != null ) - { - if( in.isInnerClass() ) - { + public String translate(ClassEntry in) { + ClassMapping classMapping = m_classes.get(in.getOuterClassName()); + if (classMapping != null) { + if (in.isInnerClass()) { // translate the inner class String translatedInnerClassName = m_direction.choose( - classMapping.getDeobfInnerClassName( in.getInnerClassName() ), - classMapping.getObfInnerClassName( in.getInnerClassName() ) + classMapping.getDeobfInnerClassName(in.getInnerClassName()), + classMapping.getObfInnerClassName(in.getInnerClassName()) ); - if( translatedInnerClassName != null ) - { + if (translatedInnerClassName != null) { // try to translate the outer name - String translatedOuterClassName = m_direction.choose( - classMapping.getDeobfName(), - classMapping.getObfName() - ); - if( translatedOuterClassName != null ) - { + String translatedOuterClassName = m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName()); + if (translatedOuterClassName != null) { return translatedOuterClassName + "$" + translatedInnerClassName; - } - else - { + } else { return in.getOuterClassName() + "$" + translatedInnerClassName; } } - } - else - { + } else { // just return outer - return m_direction.choose( - classMapping.getDeobfName(), - classMapping.getObfName() - ); + return m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName()); } } return null; } - public ClassEntry translateEntry( ClassEntry in ) - { + public ClassEntry translateEntry(ClassEntry in) { + // can we translate the inner class? - String name = translate( in ); - if( name != null ) - { - return new ClassEntry( name ); + String name = translate(in); + if (name != null) { + return new ClassEntry(name); } - if( in.isInnerClass() ) - { + if (in.isInnerClass()) { + // guess not. just translate the outer class name then - String outerClassName = translate( in.getOuterClassEntry() ); - if( outerClassName != null ) - { - return new ClassEntry( outerClassName + "$" + in.getInnerClassName() ); + String outerClassName = translate(in.getOuterClassEntry()); + if (outerClassName != null) { + return new ClassEntry(outerClassName + "$" + in.getInnerClassName()); } } return in; } - public String translate( FieldEntry in ) - { + public String translate(FieldEntry in) { + // look for the class - ClassMapping classMapping = findClassMapping( in.getClassEntry() ); - if( classMapping != null ) - { + ClassMapping classMapping = findClassMapping(in.getClassEntry()); + if (classMapping != null) { + // look for the field String translatedName = m_direction.choose( - classMapping.getDeobfFieldName( in.getName() ), - classMapping.getObfFieldName( in.getName() ) + classMapping.getDeobfFieldName(in.getName()), + classMapping.getObfFieldName(in.getName()) ); - if( translatedName != null ) - { + if (translatedName != null) { return translatedName; } } return null; } - public FieldEntry translateEntry( FieldEntry in ) - { - String name = translate( in ); - if( name == null ) - { + public FieldEntry translateEntry(FieldEntry in) { + String name = translate(in); + if (name == null) { name = in.getName(); } - return new FieldEntry( - translateEntry( in.getClassEntry() ), - name - ); + return new FieldEntry(translateEntry(in.getClassEntry()), name); } - public String translate( MethodEntry in ) - { + public String translate(MethodEntry in) { + // look for class - ClassMapping classMapping = findClassMapping( in.getClassEntry() ); - if( classMapping != null ) - { + ClassMapping classMapping = findClassMapping(in.getClassEntry()); + if (classMapping != null) { + // look for the method - MethodMapping methodMapping = m_direction.choose( - classMapping.getMethodByObf( in.getName(), in.getSignature() ), - classMapping.getMethodByDeobf( in.getName(), translateSignature( in.getSignature() ) ) - ); - if( methodMapping != null ) - { - return m_direction.choose( - methodMapping.getDeobfName(), - methodMapping.getObfName() - ); + MethodMapping methodMapping = m_direction.choose(classMapping.getMethodByObf(in.getName(), in.getSignature()), + classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature()))); + if (methodMapping != null) { + return m_direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); } } return null; } - public MethodEntry translateEntry( MethodEntry in ) - { - String name = translate( in ); - if( name == null ) - { + public MethodEntry translateEntry(MethodEntry in) { + String name = translate(in); + if (name == null) { name = in.getName(); } - return new MethodEntry( - translateEntry( in.getClassEntry() ), - name, - translateSignature( in.getSignature() ) - ); + return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature())); } - public ConstructorEntry translateEntry( ConstructorEntry in ) - { - if( in.isStatic() ) - { - return new ConstructorEntry( translateEntry( in.getClassEntry() ) ); - } - else - { - return new ConstructorEntry( - translateEntry( in.getClassEntry() ), - translateSignature( in.getSignature() ) - ); + public ConstructorEntry translateEntry(ConstructorEntry in) { + if (in.isStatic()) { + return new ConstructorEntry(translateEntry(in.getClassEntry())); + } else { + return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature())); } } - public BehaviorEntry translateEntry( BehaviorEntry in ) - { - if( in instanceof MethodEntry ) - { - return translateEntry( (MethodEntry)in ); + public BehaviorEntry translateEntry(BehaviorEntry in) { + if (in instanceof MethodEntry) { + return translateEntry((MethodEntry)in); + } else if (in instanceof ConstructorEntry) { + return translateEntry((ConstructorEntry)in); } - else if( in instanceof ConstructorEntry ) - { - return translateEntry( (ConstructorEntry)in ); - } - throw new Error( "Wrong entry type!" ); + throw new Error("Wrong entry type!"); } - public String translate( ArgumentEntry in ) - { + public String translate(ArgumentEntry in) { + // look for the class - ClassMapping classMapping = findClassMapping( in.getClassEntry() ); - if( classMapping != null ) - { + ClassMapping classMapping = findClassMapping(in.getClassEntry()); + if (classMapping != null) { + // look for the method MethodMapping methodMapping = m_direction.choose( - classMapping.getMethodByObf( in.getMethodName(), in.getMethodSignature() ), - classMapping.getMethodByDeobf( in.getMethodName(), translateSignature( in.getMethodSignature() ) ) + classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()), + classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature())) ); - if( methodMapping != null ) - { + if (methodMapping != null) { return m_direction.choose( - methodMapping.getDeobfArgumentName( in.getIndex() ), - methodMapping.getObfArgumentName( in.getIndex() ) + methodMapping.getDeobfArgumentName(in.getIndex()), + methodMapping.getObfArgumentName(in.getIndex()) ); } } return null; } - public ArgumentEntry translateEntry( ArgumentEntry in ) - { - String name = translate( in ); - if( name == null ) - { + public ArgumentEntry translateEntry(ArgumentEntry in) { + String name = translate(in); + if (name == null) { name = in.getName(); } - return new ArgumentEntry( - translateEntry( in.getBehaviorEntry() ), - in.getIndex(), - name - ); + return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name); } - public String translateSignature( String signature ) - { - return SignatureUpdater.update( signature, new ClassNameUpdater( ) - { + public String translateSignature(String signature) { + return SignatureUpdater.update(signature, new ClassNameUpdater() { @Override - public String update( String className ) - { - String translatedName = translateClass( className ); - if( translatedName != null ) - { + public String update(String className) { + String translatedName = translateClass(className); + if (translatedName != null) { return translatedName; } return className; } - } ); + }); } - private ClassMapping findClassMapping( ClassEntry classEntry ) - { - ClassMapping classMapping = m_classes.get( classEntry.getOuterClassName() ); - if( classMapping != null && classEntry.isInnerClass() ) - { + private ClassMapping findClassMapping(ClassEntry classEntry) { + ClassMapping classMapping = m_classes.get(classEntry.getOuterClassName()); + if (classMapping != null && classEntry.isInnerClass()) { classMapping = m_direction.choose( - classMapping.getInnerClassByObf( classEntry.getInnerClassName() ), - classMapping.getInnerClassByDeobfThenObf( classEntry.getInnerClassName() ) + classMapping.getInnerClassByObf(classEntry.getInnerClassName()), + classMapping.getInnerClassByDeobfThenObf(classEntry.getInnerClassName()) ); } return classMapping; diff --git a/test/cuchaz/enigma/EntryFactory.java b/test/cuchaz/enigma/EntryFactory.java index 5a8a4270..d9317ef3 100644 --- a/test/cuchaz/enigma/EntryFactory.java +++ b/test/cuchaz/enigma/EntryFactory.java @@ -18,45 +18,37 @@ import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; -public class EntryFactory -{ - public static ClassEntry newClass( String name ) - { - return new ClassEntry( name ); +public class EntryFactory { + + public static ClassEntry newClass(String name) { + return new ClassEntry(name); } - public static FieldEntry newField( String className, String fieldName ) - { - return new FieldEntry( newClass( className ), fieldName ); + public static FieldEntry newField(String className, String fieldName) { + return new FieldEntry(newClass(className), fieldName); } - public static MethodEntry newMethod( String className, String methodName, String methodSignature ) - { - return new MethodEntry( newClass( className ), methodName, methodSignature ); + public static MethodEntry newMethod(String className, String methodName, String methodSignature) { + return new MethodEntry(newClass(className), methodName, methodSignature); } - public static ConstructorEntry newConstructor( String className, String signature ) - { - return new ConstructorEntry( newClass( className ), signature ); + public static ConstructorEntry newConstructor(String className, String signature) { + return new ConstructorEntry(newClass(className), signature); } - public static EntryReference newFieldReferenceByMethod( FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature ) - { - return new EntryReference( fieldEntry, "", newMethod( callerClassName, callerName, callerSignature ) ); + public static EntryReference newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) { + return new EntryReference(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature)); } - public static EntryReference newFieldReferenceByConstructor( FieldEntry fieldEntry, String callerClassName, String callerSignature ) - { - return new EntryReference( fieldEntry, "", newConstructor( callerClassName, callerSignature ) ); + public static EntryReference newFieldReferenceByConstructor(FieldEntry fieldEntry, String callerClassName, String callerSignature) { + return new EntryReference(fieldEntry, "", newConstructor(callerClassName, callerSignature)); } - public static EntryReference newBehaviorReferenceByMethod( BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature ) - { - return new EntryReference( behaviorEntry, "", newMethod( callerClassName, callerName, callerSignature ) ); + public static EntryReference newBehaviorReferenceByMethod(BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature) { + return new EntryReference(behaviorEntry, "", newMethod(callerClassName, callerName, callerSignature)); } - public static EntryReference newBehaviorReferenceByConstructor( BehaviorEntry behaviorEntry, String callerClassName, String callerSignature ) - { - return new EntryReference( behaviorEntry, "", newConstructor( callerClassName, callerSignature ) ); + public static EntryReference newBehaviorReferenceByConstructor(BehaviorEntry behaviorEntry, String callerClassName, String callerSignature) { + return new EntryReference(behaviorEntry, "", newConstructor(callerClassName, callerSignature)); } } diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java index 71de24ae..45d27c40 100644 --- a/test/cuchaz/enigma/TestDeobfuscator.java +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -23,40 +23,32 @@ import com.google.common.collect.Lists; import cuchaz.enigma.mapping.ClassEntry; -public class TestDeobfuscator -{ - private Deobfuscator getDeobfuscator( ) - throws IOException - { - return new Deobfuscator( new File( "build/libs/testLoneClass.obf.jar" ) ); +public class TestDeobfuscator { + + private Deobfuscator getDeobfuscator() throws IOException { + return new Deobfuscator(new File("build/libs/testLoneClass.obf.jar")); } @Test - public void loadJar( ) - throws Exception - { + public void loadJar() throws Exception { getDeobfuscator(); } @Test - public void getClasses( ) - throws Exception - { + public void getClasses() throws Exception { Deobfuscator deobfuscator = getDeobfuscator(); List obfClasses = Lists.newArrayList(); List deobfClasses = Lists.newArrayList(); - deobfuscator.getSeparatedClasses( obfClasses, deobfClasses ); - assertEquals( 1, obfClasses.size() ); - assertEquals( "none/a", obfClasses.get( 0 ).getName() ); - assertEquals( 1, deobfClasses.size() ); - assertEquals( "cuchaz/enigma/inputs/Keep", deobfClasses.get( 0 ).getName() ); + deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); + assertEquals(1, obfClasses.size()); + assertEquals("none/a", obfClasses.get(0).getName()); + assertEquals(1, deobfClasses.size()); + assertEquals("cuchaz/enigma/inputs/Keep", deobfClasses.get(0).getName()); } @Test - public void decompileClass( ) - throws Exception - { + public void decompileClass() throws Exception { Deobfuscator deobfuscator = getDeobfuscator(); - deobfuscator.getSource( deobfuscator.getSourceTree( "none/a" ) ); + deobfuscator.getSource(deobfuscator.getSourceTree("none/a")); } } diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java index a7ee0b6d..c84d755e 100644 --- a/test/cuchaz/enigma/TestInnerClasses.java +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -20,8 +20,8 @@ import org.junit.Test; import cuchaz.enigma.analysis.JarIndex; -public class TestInnerClasses -{ +public class TestInnerClasses { + private JarIndex m_index; private static final String AnonymousOuter = "none/a"; @@ -33,42 +33,36 @@ public class TestInnerClasses private static final String AnonymousWithScopeArgsOuter = "none/c"; private static final String AnonymousWithScopeArgsInner = "d"; - public TestInnerClasses( ) - throws Exception - { + public TestInnerClasses() throws Exception { m_index = new JarIndex(); - m_index.indexJar( new JarFile( "build/libs/testInnerClasses.obf.jar" ), true ); + m_index.indexJar(new JarFile("build/libs/testInnerClasses.obf.jar"), true); } @Test - public void simple( ) - { - assertThat( m_index.getOuterClass( SimpleInner ), is( SimpleOuter ) ); - assertThat( m_index.getInnerClasses( SimpleOuter ), containsInAnyOrder( SimpleInner ) ); - assertThat( m_index.isAnonymousClass( SimpleInner ), is( false ) ); + public void simple() { + assertThat(m_index.getOuterClass(SimpleInner), is(SimpleOuter)); + assertThat(m_index.getInnerClasses(SimpleOuter), containsInAnyOrder(SimpleInner)); + assertThat(m_index.isAnonymousClass(SimpleInner), is(false)); } @Test - public void anonymous( ) - { - assertThat( m_index.getOuterClass( AnonymousInner ), is( AnonymousOuter ) ); - assertThat( m_index.getInnerClasses( AnonymousOuter ), containsInAnyOrder( AnonymousInner ) ); - assertThat( m_index.isAnonymousClass( AnonymousInner ), is( true ) ); + public void anonymous() { + assertThat(m_index.getOuterClass(AnonymousInner), is(AnonymousOuter)); + assertThat(m_index.getInnerClasses(AnonymousOuter), containsInAnyOrder(AnonymousInner)); + assertThat(m_index.isAnonymousClass(AnonymousInner), is(true)); } - + @Test - public void constructorArgs( ) - { - assertThat( m_index.getOuterClass( ConstructorArgsInner ), is( ConstructorArgsOuter ) ); - assertThat( m_index.getInnerClasses( ConstructorArgsOuter ), containsInAnyOrder( ConstructorArgsInner ) ); - assertThat( m_index.isAnonymousClass( ConstructorArgsInner ), is( false ) ); + public void constructorArgs() { + assertThat(m_index.getOuterClass(ConstructorArgsInner), is(ConstructorArgsOuter)); + assertThat(m_index.getInnerClasses(ConstructorArgsOuter), containsInAnyOrder(ConstructorArgsInner)); + assertThat(m_index.isAnonymousClass(ConstructorArgsInner), is(false)); } @Test - public void anonymousWithScopeArgs( ) - { - assertThat( m_index.getOuterClass( AnonymousWithScopeArgsInner ), is( AnonymousWithScopeArgsOuter ) ); - assertThat( m_index.getInnerClasses( AnonymousWithScopeArgsOuter ), containsInAnyOrder( AnonymousWithScopeArgsInner ) ); - assertThat( m_index.isAnonymousClass( AnonymousWithScopeArgsInner ), is( true ) ); + public void anonymousWithScopeArgs() { + assertThat(m_index.getOuterClass(AnonymousWithScopeArgsInner), is(AnonymousWithScopeArgsOuter)); + assertThat(m_index.getInnerClasses(AnonymousWithScopeArgsOuter), containsInAnyOrder(AnonymousWithScopeArgsInner)); + assertThat(m_index.isAnonymousClass(AnonymousWithScopeArgsInner), is(true)); } } diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java index 02381718..b5f4c7f3 100644 --- a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -26,117 +26,99 @@ import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; -public class TestJarIndexConstructorReferences -{ +public class TestJarIndexConstructorReferences { + private JarIndex m_index; - - private ClassEntry m_baseClass = new ClassEntry( "none/a" ); - private ClassEntry m_subClass = new ClassEntry( "none/d" ); - private ClassEntry m_subsubClass = new ClassEntry( "none/e" ); - private ClassEntry m_defaultClass = new ClassEntry( "none/c" ); - private ClassEntry m_callerClass = new ClassEntry( "none/b" ); - - public TestJarIndexConstructorReferences( ) - throws Exception - { - File jarFile = new File( "build/libs/testConstructors.obf.jar" ); + + private ClassEntry m_baseClass = new ClassEntry("none/a"); + private ClassEntry m_subClass = new ClassEntry("none/d"); + private ClassEntry m_subsubClass = new ClassEntry("none/e"); + private ClassEntry m_defaultClass = new ClassEntry("none/c"); + private ClassEntry m_callerClass = new ClassEntry("none/b"); + + public TestJarIndexConstructorReferences() throws Exception { + File jarFile = new File("build/libs/testConstructors.obf.jar"); m_index = new JarIndex(); - m_index.indexJar( new JarFile( jarFile ), false ); + m_index.indexJar(new JarFile(jarFile), false); } @Test - public void obfEntries( ) - { - assertThat( m_index.getObfClassEntries(), containsInAnyOrder( - newClass( "cuchaz/enigma/inputs/Keep" ), - m_baseClass, - m_subClass, - m_subsubClass, - m_defaultClass, - m_callerClass - ) ); + public void obfEntries() { + assertThat(m_index.getObfClassEntries(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), m_baseClass, m_subClass, m_subsubClass, m_defaultClass, m_callerClass)); } @Test - @SuppressWarnings( "unchecked" ) - public void baseDefault( ) - { - BehaviorEntry source = new ConstructorEntry( m_baseClass, "()V" ); - Collection> references = m_index.getBehaviorReferences( source ); - assertThat( references, containsInAnyOrder( - newBehaviorReferenceByMethod( source, m_callerClass.getName(), "a", "()V" ), - newBehaviorReferenceByConstructor( source, m_subClass.getName(), "()V" ), - newBehaviorReferenceByConstructor( source, m_subClass.getName(), "(III)V" ) - ) ); + @SuppressWarnings("unchecked") + public void baseDefault() { + BehaviorEntry source = new ConstructorEntry(m_baseClass, "()V"); + Collection> references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "a", "()V"), + newBehaviorReferenceByConstructor(source, m_subClass.getName(), "()V"), + newBehaviorReferenceByConstructor(source, m_subClass.getName(), "(III)V") + )); } @Test - @SuppressWarnings( "unchecked" ) - public void baseInt( ) - { - BehaviorEntry source = new ConstructorEntry( m_baseClass, "(I)V" ); - assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( - newBehaviorReferenceByMethod( source, m_callerClass.getName(), "b", "()V" ) - ) ); + @SuppressWarnings("unchecked") + public void baseInt() { + BehaviorEntry source = new ConstructorEntry(m_baseClass, "(I)V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "b", "()V") + )); } - + @Test - @SuppressWarnings( "unchecked" ) - public void subDefault( ) - { - BehaviorEntry source = new ConstructorEntry( m_subClass, "()V" ); - assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( - newBehaviorReferenceByMethod( source, m_callerClass.getName(), "c", "()V" ), - newBehaviorReferenceByConstructor( source, m_subClass.getName(), "(I)V" ) - ) ); + @SuppressWarnings("unchecked") + public void subDefault() { + BehaviorEntry source = new ConstructorEntry(m_subClass, "()V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "c", "()V"), + newBehaviorReferenceByConstructor(source, m_subClass.getName(), "(I)V") + )); } - + @Test - @SuppressWarnings( "unchecked" ) - public void subInt( ) - { - BehaviorEntry source = new ConstructorEntry( m_subClass, "(I)V" ); - assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( - newBehaviorReferenceByMethod( source, m_callerClass.getName(), "d", "()V" ), - newBehaviorReferenceByConstructor( source, m_subClass.getName(), "(II)V" ), - newBehaviorReferenceByConstructor( source, m_subsubClass.getName(), "(I)V" ) - ) ); + @SuppressWarnings("unchecked") + public void subInt() { + BehaviorEntry source = new ConstructorEntry(m_subClass, "(I)V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "d", "()V"), + newBehaviorReferenceByConstructor(source, m_subClass.getName(), "(II)V"), + newBehaviorReferenceByConstructor(source, m_subsubClass.getName(), "(I)V") + )); } - + @Test - @SuppressWarnings( "unchecked" ) - public void subIntInt( ) - { - BehaviorEntry source = new ConstructorEntry( m_subClass, "(II)V" ); - assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( - newBehaviorReferenceByMethod( source, m_callerClass.getName(), "e", "()V" ) - ) ); + @SuppressWarnings("unchecked") + public void subIntInt() { + BehaviorEntry source = new ConstructorEntry(m_subClass, "(II)V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "e", "()V") + )); } - + @Test - public void subIntIntInt( ) - { - BehaviorEntry source = new ConstructorEntry( m_subClass, "(III)V" ); - assertThat( m_index.getBehaviorReferences( source ), is( empty() ) ); + public void subIntIntInt() { + BehaviorEntry source = new ConstructorEntry(m_subClass, "(III)V"); + assertThat(m_index.getBehaviorReferences(source), is(empty())); } - + @Test - @SuppressWarnings( "unchecked" ) - public void subsubInt( ) - { - BehaviorEntry source = new ConstructorEntry( m_subsubClass, "(I)V" ); - assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( - newBehaviorReferenceByMethod( source, m_callerClass.getName(), "f", "()V" ) - ) ); + @SuppressWarnings("unchecked") + public void subsubInt() { + BehaviorEntry source = new ConstructorEntry(m_subsubClass, "(I)V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "f", "()V") + )); } - + @Test - @SuppressWarnings( "unchecked" ) - public void defaultConstructable( ) - { - BehaviorEntry source = new ConstructorEntry( m_defaultClass, "()V" ); - assertThat( m_index.getBehaviorReferences( source ), containsInAnyOrder( - newBehaviorReferenceByMethod( source, m_callerClass.getName(), "g", "()V" ) - ) ); + @SuppressWarnings("unchecked") + public void defaultConstructable() { + BehaviorEntry source = new ConstructorEntry(m_defaultClass, "()V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "g", "()V") + )); } } diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 50c22825..caf6578d 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -30,209 +30,199 @@ import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; -public class TestJarIndexInheritanceTree -{ +public class TestJarIndexInheritanceTree { + private JarIndex m_index; - private ClassEntry m_baseClass = new ClassEntry( "none/a" ); - private ClassEntry m_subClassA = new ClassEntry( "none/b" ); - private ClassEntry m_subClassAA = new ClassEntry( "none/d" ); - private ClassEntry m_subClassB = new ClassEntry( "none/c" ); - private FieldEntry m_nameField = new FieldEntry( m_baseClass, "a" ); - private FieldEntry m_numThingsField = new FieldEntry( m_subClassB, "a" ); + private ClassEntry m_baseClass = new ClassEntry("none/a"); + private ClassEntry m_subClassA = new ClassEntry("none/b"); + private ClassEntry m_subClassAA = new ClassEntry("none/d"); + private ClassEntry m_subClassB = new ClassEntry("none/c"); + private FieldEntry m_nameField = new FieldEntry(m_baseClass, "a"); + private FieldEntry m_numThingsField = new FieldEntry(m_subClassB, "a"); - public TestJarIndexInheritanceTree( ) - throws Exception - { + public TestJarIndexInheritanceTree() throws Exception { m_index = new JarIndex(); - m_index.indexJar( new JarFile( "build/libs/testInheritanceTree.obf.jar" ), false ); + m_index.indexJar(new JarFile("build/libs/testInheritanceTree.obf.jar"), false); } @Test - public void obfEntries( ) - { - assertThat( m_index.getObfClassEntries(), containsInAnyOrder( - newClass( "cuchaz/enigma/inputs/Keep" ), + public void obfEntries() { + assertThat(m_index.getObfClassEntries(), containsInAnyOrder( + newClass("cuchaz/enigma/inputs/Keep"), m_baseClass, m_subClassA, m_subClassAA, m_subClassB - ) ); + )); } @Test - public void translationIndex( ) - { + public void translationIndex() { + TranslationIndex index = m_index.getTranslationIndex(); // base class - assertThat( index.getSuperclassName( m_baseClass.getName() ), is( nullValue() ) ); - assertThat( index.getAncestry( m_baseClass.getName() ), is( empty() ) ); - assertThat( index.getSubclassNames( m_baseClass.getName() ), containsInAnyOrder( + assertThat(index.getSuperclassName(m_baseClass.getName()), is(nullValue())); + assertThat(index.getAncestry(m_baseClass.getName()), is(empty())); + assertThat(index.getSubclassNames(m_baseClass.getName()), containsInAnyOrder( m_subClassA.getName(), m_subClassB.getName() - ) ); + )); // subclass a - assertThat( index.getSuperclassName( m_subClassA.getName() ), is( m_baseClass.getName() ) ); - assertThat( index.getAncestry( m_subClassA.getName() ), contains( m_baseClass.getName() ) ); - assertThat( index.getSubclassNames( m_subClassA.getName() ), contains( m_subClassAA.getName() ) ); + assertThat(index.getSuperclassName(m_subClassA.getName()), is(m_baseClass.getName())); + assertThat(index.getAncestry(m_subClassA.getName()), contains(m_baseClass.getName())); + assertThat(index.getSubclassNames(m_subClassA.getName()), contains(m_subClassAA.getName())); // subclass aa - assertThat( index.getSuperclassName( m_subClassAA.getName() ), is( m_subClassA.getName() ) ); - assertThat( index.getAncestry( m_subClassAA.getName() ), contains( - m_subClassA.getName(), - m_baseClass.getName() - ) ); - assertThat( index.getSubclassNames( m_subClassAA.getName() ), is( empty() ) ); + assertThat(index.getSuperclassName(m_subClassAA.getName()), is(m_subClassA.getName())); + assertThat(index.getAncestry(m_subClassAA.getName()), contains(m_subClassA.getName(), m_baseClass.getName())); + assertThat(index.getSubclassNames(m_subClassAA.getName()), is(empty())); // subclass b - assertThat( index.getSuperclassName( m_subClassB.getName() ), is( m_baseClass.getName() ) ); - assertThat( index.getAncestry( m_subClassB.getName() ), contains( m_baseClass.getName() ) ); - assertThat( index.getSubclassNames( m_subClassB.getName() ), is( empty() ) ); + assertThat(index.getSuperclassName(m_subClassB.getName()), is(m_baseClass.getName())); + assertThat(index.getAncestry(m_subClassB.getName()), contains(m_baseClass.getName())); + assertThat(index.getSubclassNames(m_subClassB.getName()), is(empty())); } @Test - public void access( ) - { - assertThat( m_index.getAccess( m_nameField ), is( Access.Private ) ); - assertThat( m_index.getAccess( m_numThingsField ), is( Access.Private ) ); + public void access() { + assertThat(m_index.getAccess(m_nameField), is(Access.Private)); + assertThat(m_index.getAccess(m_numThingsField), is(Access.Private)); } @Test - public void relatedMethodImplementations( ) - { + public void relatedMethodImplementations() { + Set entries; // getName() - entries = m_index.getRelatedMethodImplementations( new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ) ); - assertThat( entries, containsInAnyOrder( - new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ), - new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ) - ) ); - entries = m_index.getRelatedMethodImplementations( new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ) ); - assertThat( entries, containsInAnyOrder( - new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ), - new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ) - ) ); + entries = m_index.getRelatedMethodImplementations(new MethodEntry(m_baseClass, "a", "()Ljava/lang/String;")); + assertThat(entries, containsInAnyOrder( + new MethodEntry(m_baseClass, "a", "()Ljava/lang/String;"), + new MethodEntry(m_subClassAA, "a", "()Ljava/lang/String;") + )); + entries = m_index.getRelatedMethodImplementations(new MethodEntry(m_subClassAA, "a", "()Ljava/lang/String;")); + assertThat(entries, containsInAnyOrder( + new MethodEntry(m_baseClass, "a", "()Ljava/lang/String;"), + new MethodEntry(m_subClassAA, "a", "()Ljava/lang/String;") + )); // doBaseThings() - entries = m_index.getRelatedMethodImplementations( new MethodEntry( m_baseClass, "a", "()V" ) ); - assertThat( entries, containsInAnyOrder( - new MethodEntry( m_baseClass, "a", "()V" ), - new MethodEntry( m_subClassAA, "a", "()V" ), - new MethodEntry( m_subClassB, "a", "()V" ) - ) ); - entries = m_index.getRelatedMethodImplementations( new MethodEntry( m_subClassAA, "a", "()V" ) ); - assertThat( entries, containsInAnyOrder( - new MethodEntry( m_baseClass, "a", "()V" ), - new MethodEntry( m_subClassAA, "a", "()V" ), - new MethodEntry( m_subClassB, "a", "()V" ) - ) ); - entries = m_index.getRelatedMethodImplementations( new MethodEntry( m_subClassB, "a", "()V" ) ); - assertThat( entries, containsInAnyOrder( - new MethodEntry( m_baseClass, "a", "()V" ), - new MethodEntry( m_subClassAA, "a", "()V" ), - new MethodEntry( m_subClassB, "a", "()V" ) - ) ); + entries = m_index.getRelatedMethodImplementations(new MethodEntry(m_baseClass, "a", "()V")); + assertThat(entries, containsInAnyOrder( + new MethodEntry(m_baseClass, "a", "()V"), + new MethodEntry(m_subClassAA, "a", "()V"), + new MethodEntry(m_subClassB, "a", "()V") + )); + entries = m_index.getRelatedMethodImplementations(new MethodEntry(m_subClassAA, "a", "()V")); + assertThat(entries, containsInAnyOrder( + new MethodEntry(m_baseClass, "a", "()V"), + new MethodEntry(m_subClassAA, "a", "()V"), + new MethodEntry(m_subClassB, "a", "()V") + )); + entries = m_index.getRelatedMethodImplementations(new MethodEntry(m_subClassB, "a", "()V")); + assertThat(entries, containsInAnyOrder( + new MethodEntry(m_baseClass, "a", "()V"), + new MethodEntry(m_subClassAA, "a", "()V"), + new MethodEntry(m_subClassB, "a", "()V") + )); // doBThings - entries = m_index.getRelatedMethodImplementations( new MethodEntry( m_subClassB, "b", "()V" ) ); - assertThat( entries, containsInAnyOrder( - new MethodEntry( m_subClassB, "b", "()V" ) - ) ); + entries = m_index.getRelatedMethodImplementations(new MethodEntry(m_subClassB, "b", "()V")); + assertThat(entries, containsInAnyOrder(new MethodEntry(m_subClassB, "b", "()V"))); } @Test - @SuppressWarnings( "unchecked" ) - public void fieldReferences( ) - { + @SuppressWarnings("unchecked") + public void fieldReferences() { Collection> references; // name - references = m_index.getFieldReferences( m_nameField ); - assertThat( references, containsInAnyOrder( - newFieldReferenceByConstructor( m_nameField, m_baseClass.getName(), "(Ljava/lang/String;)V" ), - newFieldReferenceByMethod( m_nameField, m_baseClass.getName(), "a", "()Ljava/lang/String;" ) - ) ); + references = m_index.getFieldReferences(m_nameField); + assertThat(references, containsInAnyOrder( + newFieldReferenceByConstructor(m_nameField, m_baseClass.getName(), "(Ljava/lang/String;)V"), + newFieldReferenceByMethod(m_nameField, m_baseClass.getName(), "a", "()Ljava/lang/String;") + )); // numThings - references = m_index.getFieldReferences( m_numThingsField ); - assertThat( references, containsInAnyOrder( - newFieldReferenceByConstructor( m_numThingsField, m_subClassB.getName(), "()V" ), - newFieldReferenceByMethod( m_numThingsField, m_subClassB.getName(), "b", "()V" ) - ) ); + references = m_index.getFieldReferences(m_numThingsField); + assertThat(references, containsInAnyOrder( + newFieldReferenceByConstructor(m_numThingsField, m_subClassB.getName(), "()V"), + newFieldReferenceByMethod(m_numThingsField, m_subClassB.getName(), "b", "()V") + )); } @Test - @SuppressWarnings( "unchecked" ) - public void behaviorReferences( ) - { + @SuppressWarnings("unchecked") + public void behaviorReferences() { + BehaviorEntry source; Collection> references; // baseClass constructor - source = new ConstructorEntry( m_baseClass, "(Ljava/lang/String;)V" ); - references = m_index.getBehaviorReferences( source ); - assertThat( references, containsInAnyOrder( - newBehaviorReferenceByConstructor( source, m_subClassA.getName(), "(Ljava/lang/String;)V" ), - newBehaviorReferenceByConstructor( source, m_subClassB.getName(), "()V" ) - ) ); + source = new ConstructorEntry(m_baseClass, "(Ljava/lang/String;)V"); + references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByConstructor(source, m_subClassA.getName(), "(Ljava/lang/String;)V"), + newBehaviorReferenceByConstructor(source, m_subClassB.getName(), "()V") + )); // subClassA constructor - source = new ConstructorEntry( m_subClassA, "(Ljava/lang/String;)V" ); - references = m_index.getBehaviorReferences( source ); - assertThat( references, containsInAnyOrder( - newBehaviorReferenceByConstructor( source, m_subClassAA.getName(), "()V" ) - ) ); + source = new ConstructorEntry(m_subClassA, "(Ljava/lang/String;)V"); + references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByConstructor(source, m_subClassAA.getName(), "()V") + )); // baseClass.getName() - source = new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ); - references = m_index.getBehaviorReferences( source ); - assertThat( references, containsInAnyOrder( - newBehaviorReferenceByMethod( source, m_subClassAA.getName(), "a", "()Ljava/lang/String;" ), - newBehaviorReferenceByMethod( source, m_subClassB.getName(), "a", "()V" ) - ) ); + source = new MethodEntry(m_baseClass, "a", "()Ljava/lang/String;"); + references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_subClassAA.getName(), "a", "()Ljava/lang/String;"), + newBehaviorReferenceByMethod(source, m_subClassB.getName(), "a", "()V") + )); // subclassAA.getName() - source = new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ); - references = m_index.getBehaviorReferences( source ); - assertThat( references, containsInAnyOrder( - newBehaviorReferenceByMethod( source, m_subClassAA.getName(), "a", "()V" ) - ) ); + source = new MethodEntry(m_subClassAA, "a", "()Ljava/lang/String;"); + references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_subClassAA.getName(), "a", "()V") + )); } @Test - public void containsEntries( ) - { + public void containsEntries() { + // classes - assertThat( m_index.containsObfClass( m_baseClass ), is( true ) ); - assertThat( m_index.containsObfClass( m_subClassA ), is( true ) ); - assertThat( m_index.containsObfClass( m_subClassAA ), is( true ) ); - assertThat( m_index.containsObfClass( m_subClassB ), is( true ) ); + assertThat(m_index.containsObfClass(m_baseClass), is(true)); + assertThat(m_index.containsObfClass(m_subClassA), is(true)); + assertThat(m_index.containsObfClass(m_subClassAA), is(true)); + assertThat(m_index.containsObfClass(m_subClassB), is(true)); // fields - assertThat( m_index.containsObfField( m_nameField ), is( true ) ); - assertThat( m_index.containsObfField( m_numThingsField ), is( true ) ); + assertThat(m_index.containsObfField(m_nameField), is(true)); + assertThat(m_index.containsObfField(m_numThingsField), is(true)); // methods // getName() - assertThat( m_index.containsObfBehavior( new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ) ), is( true ) ); - assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassA, "a", "()Ljava/lang/String;" ) ), is( false ) ); - assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassAA, "a", "()Ljava/lang/String;" ) ), is( true ) ); - assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassB, "a", "()Ljava/lang/String;" ) ), is( false ) ); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_baseClass, "a", "()Ljava/lang/String;")), is(true)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassA, "a", "()Ljava/lang/String;")), is(false)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassAA, "a", "()Ljava/lang/String;")), is(true)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassB, "a", "()Ljava/lang/String;")), is(false)); // doBaseThings() - assertThat( m_index.containsObfBehavior( new MethodEntry( m_baseClass, "a", "()V" ) ), is( true ) ); - assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassA, "a", "()V" ) ), is( false ) ); - assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassAA, "a", "()V" ) ), is( true ) ); - assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassB, "a", "()V" ) ), is( true ) ); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_baseClass, "a", "()V")), is(true)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassA, "a", "()V")), is(false)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassAA, "a", "()V")), is(true)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassB, "a", "()V")), is(true)); // doBThings() - assertThat( m_index.containsObfBehavior( new MethodEntry( m_baseClass, "b", "()V" ) ), is( false ) ); - assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassA, "b", "()V" ) ), is( false ) ); - assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassAA, "b", "()V" ) ), is( false ) ); - assertThat( m_index.containsObfBehavior( new MethodEntry( m_subClassB, "b", "()V" ) ), is( true ) ); - + assertThat(m_index.containsObfBehavior(new MethodEntry(m_baseClass, "b", "()V")), is(false)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassA, "b", "()V")), is(false)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassAA, "b", "()V")), is(false)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassB, "b", "()V")), is(true)); + } } diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index e2a87d09..0575eec3 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -33,154 +33,136 @@ import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; -public class TestJarIndexLoneClass -{ +public class TestJarIndexLoneClass { + private JarIndex m_index; - public TestJarIndexLoneClass( ) - throws Exception - { + public TestJarIndexLoneClass() throws Exception { m_index = new JarIndex(); - m_index.indexJar( new JarFile( "build/libs/testLoneClass.obf.jar" ), false ); + m_index.indexJar(new JarFile("build/libs/testLoneClass.obf.jar"), false); } @Test - public void obfEntries( ) - { - assertThat( m_index.getObfClassEntries(), containsInAnyOrder( - newClass( "cuchaz/enigma/inputs/Keep" ), - newClass( "none/a" ) - ) ); + public void obfEntries() { + assertThat(m_index.getObfClassEntries(), containsInAnyOrder( + newClass("cuchaz/enigma/inputs/Keep"), + newClass("none/a") + )); } @Test - public void translationIndex( ) - { - assertThat( m_index.getTranslationIndex().getSuperclassName( "none/a" ), is( nullValue() ) ); - assertThat( m_index.getTranslationIndex().getSuperclassName( "cuchaz/enigma/inputs/Keep" ), is( nullValue() ) ); - assertThat( m_index.getTranslationIndex().getAncestry( "none/a" ), is( empty() ) ); - assertThat( m_index.getTranslationIndex().getAncestry( "cuchaz/enigma/inputs/Keep" ), is( empty() ) ); - assertThat( m_index.getTranslationIndex().getSubclassNames( "none/a" ), is( empty() ) ); - assertThat( m_index.getTranslationIndex().getSubclassNames( "cuchaz/enigma/inputs/Keep" ), is( empty() ) ); + public void translationIndex() { + assertThat(m_index.getTranslationIndex().getSuperclassName("none/a"), is(nullValue())); + assertThat(m_index.getTranslationIndex().getSuperclassName("cuchaz/enigma/inputs/Keep"), is(nullValue())); + assertThat(m_index.getTranslationIndex().getAncestry("none/a"), is(empty())); + assertThat(m_index.getTranslationIndex().getAncestry("cuchaz/enigma/inputs/Keep"), is(empty())); + assertThat(m_index.getTranslationIndex().getSubclassNames("none/a"), is(empty())); + assertThat(m_index.getTranslationIndex().getSubclassNames("cuchaz/enigma/inputs/Keep"), is(empty())); } @Test - public void access( ) - { - assertThat( m_index.getAccess( newField( "none/a", "a" ) ), is( Access.Private ) ); - assertThat( m_index.getAccess( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( Access.Public ) ); - assertThat( m_index.getAccess( newField( "none/a", "b" ) ), is( nullValue() ) ); + public void access() { + assertThat(m_index.getAccess(newField("none/a", "a")), is(Access.Private)); + assertThat(m_index.getAccess(newMethod("none/a", "a", "()Ljava/lang/String;")), is(Access.Public)); + assertThat(m_index.getAccess(newField("none/a", "b")), is(nullValue())); } @Test - public void classInheritance( ) - { - ClassInheritanceTreeNode node = m_index.getClassInheritance( new Translator(), newClass( "none/a" ) ); - assertThat( node, is( not( nullValue() ) ) ); - assertThat( node.getObfClassName(), is( "none/a" ) ); - assertThat( node.getChildCount(), is( 0 ) ); + public void classInheritance() { + ClassInheritanceTreeNode node = m_index.getClassInheritance(new Translator(), newClass("none/a")); + assertThat(node, is(not(nullValue()))); + assertThat(node.getObfClassName(), is("none/a")); + assertThat(node.getChildCount(), is(0)); } - + @Test - public void methodInheritance( ) - { - MethodEntry source = newMethod( "none/a", "a", "()Ljava/lang/String;" ); - MethodInheritanceTreeNode node = m_index.getMethodInheritance( new Translator(), source ); - assertThat( node, is( not( nullValue() ) ) ); - assertThat( node.getMethodEntry(), is( source ) ); - assertThat( node.getChildCount(), is( 0 ) ); + public void methodInheritance() { + MethodEntry source = newMethod("none/a", "a", "()Ljava/lang/String;"); + MethodInheritanceTreeNode node = m_index.getMethodInheritance(new Translator(), source); + assertThat(node, is(not(nullValue()))); + assertThat(node.getMethodEntry(), is(source)); + assertThat(node.getChildCount(), is(0)); } @Test - public void classImplementations( ) - { - ClassImplementationsTreeNode node = m_index.getClassImplementations( new Translator(), newClass( "none/a" ) ); - assertThat( node, is( nullValue() ) ); + public void classImplementations() { + ClassImplementationsTreeNode node = m_index.getClassImplementations(new Translator(), newClass("none/a")); + assertThat(node, is(nullValue())); } @Test - public void methodImplementations( ) - { - MethodEntry source = newMethod( "none/a", "a", "()Ljava/lang/String;" ); - MethodImplementationsTreeNode node = m_index.getMethodImplementations( new Translator(), source ); - assertThat( node, is( nullValue() ) ); + public void methodImplementations() { + MethodEntry source = newMethod("none/a", "a", "()Ljava/lang/String;"); + MethodImplementationsTreeNode node = m_index.getMethodImplementations(new Translator(), source); + assertThat(node, is(nullValue())); } @Test - public void relatedMethodImplementations( ) - { - Set entries = m_index.getRelatedMethodImplementations( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ); - assertThat( entries, containsInAnyOrder( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ) ); + public void relatedMethodImplementations() { + Set entries = m_index.getRelatedMethodImplementations(newMethod("none/a", "a", "()Ljava/lang/String;")); + assertThat(entries, containsInAnyOrder( + newMethod("none/a", "a", "()Ljava/lang/String;") + )); } @Test - @SuppressWarnings( "unchecked" ) - public void fieldReferences( ) - { - FieldEntry source = newField( "none/a", "a" ); - Collection> references = m_index.getFieldReferences( source ); - assertThat( references, containsInAnyOrder( - newFieldReferenceByConstructor( source, "none/a", "(Ljava/lang/String;)V" ), - newFieldReferenceByMethod( source, "none/a", "a", "()Ljava/lang/String;" ) - ) ); + @SuppressWarnings("unchecked") + public void fieldReferences() { + FieldEntry source = newField("none/a", "a"); + Collection> references = m_index.getFieldReferences(source); + assertThat(references, containsInAnyOrder( + newFieldReferenceByConstructor(source, "none/a", "(Ljava/lang/String;)V"), + newFieldReferenceByMethod(source, "none/a", "a", "()Ljava/lang/String;") + )); } @Test - public void behaviorReferences( ) - { - assertThat( m_index.getBehaviorReferences( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( empty() ) ); + public void behaviorReferences() { + assertThat(m_index.getBehaviorReferences(newMethod("none/a", "a", "()Ljava/lang/String;")), is(empty())); } @Test - public void innerClasses( ) - { - assertThat( m_index.getInnerClasses( "none/a" ), is( empty() ) ); + public void innerClasses() { + assertThat(m_index.getInnerClasses("none/a"), is(empty())); } @Test - public void outerClass( ) - { - assertThat( m_index.getOuterClass( "a" ), is( nullValue() ) ); + public void outerClass() { + assertThat(m_index.getOuterClass("a"), is(nullValue())); } @Test - public void isAnonymousClass( ) - { - assertThat( m_index.isAnonymousClass( "none/a" ), is( false ) ); + public void isAnonymousClass() { + assertThat(m_index.isAnonymousClass("none/a"), is(false)); } @Test - public void interfaces( ) - { - assertThat( m_index.getInterfaces( "none/a" ), is( empty() ) ); + public void interfaces() { + assertThat(m_index.getInterfaces("none/a"), is(empty())); } @Test - public void implementingClasses( ) - { - assertThat( m_index.getImplementingClasses( "none/a" ), is( empty() ) ); + public void implementingClasses() { + assertThat(m_index.getImplementingClasses("none/a"), is(empty())); } @Test - public void isInterface( ) - { - assertThat( m_index.isInterface( "none/a" ), is( false ) ); + public void isInterface() { + assertThat(m_index.isInterface("none/a"), is(false)); } @Test - public void bridgeMethods( ) - { - assertThat( m_index.getBridgeMethod( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( nullValue() ) ); + public void bridgeMethods() { + assertThat(m_index.getBridgeMethod(newMethod("none/a", "a", "()Ljava/lang/String;")), is(nullValue())); } @Test - public void contains( ) - { - assertThat( m_index.containsObfClass( newClass( "none/a" ) ), is( true ) ); - assertThat( m_index.containsObfClass( newClass( "none/b" ) ), is( false ) ); - assertThat( m_index.containsObfField( newField( "none/a", "a" ) ), is( true ) ); - assertThat( m_index.containsObfField( newField( "none/a", "b" ) ), is( false ) ); - assertThat( m_index.containsObfBehavior( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( true ) ); - assertThat( m_index.containsObfBehavior( newMethod( "none/a", "b", "()Ljava/lang/String;" ) ), is( false ) ); + public void contains() { + assertThat(m_index.containsObfClass(newClass("none/a")), is(true)); + assertThat(m_index.containsObfClass(newClass("none/b")), is(false)); + assertThat(m_index.containsObfField(newField("none/a", "a")), is(true)); + assertThat(m_index.containsObfField(newField("none/a", "b")), is(false)); + assertThat(m_index.containsObfBehavior(newMethod("none/a", "a", "()Ljava/lang/String;")), is(true)); + assertThat(m_index.containsObfBehavior(newMethod("none/a", "b", "()Ljava/lang/String;")), is(false)); } } diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java index dc6ca7e2..fb385e06 100644 --- a/test/cuchaz/enigma/TestSourceIndex.java +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -21,35 +21,27 @@ import com.strobel.decompiler.languages.java.ast.CompilationUnit; import cuchaz.enigma.mapping.ClassEntry; -public class TestSourceIndex -{ +public class TestSourceIndex { + @Test - public void indexEverything( ) - throws Exception - { - Deobfuscator deobfuscator = new Deobfuscator( new File( "input/1.8.jar" ) ); + public void indexEverything() throws Exception { + Deobfuscator deobfuscator = new Deobfuscator(new File("input/1.8.jar")); // get all classes that aren't inner classes Set classEntries = Sets.newHashSet(); - for( ClassEntry obfClassEntry : deobfuscator.getJarIndex().getObfClassEntries() ) - { - if( !obfClassEntry.isInnerClass() ) - { - classEntries.add( obfClassEntry ); + for (ClassEntry obfClassEntry : deobfuscator.getJarIndex().getObfClassEntries()) { + if (!obfClassEntry.isInnerClass()) { + classEntries.add(obfClassEntry); } } - for( ClassEntry obfClassEntry : classEntries ) - { - try - { - CompilationUnit tree = deobfuscator.getSourceTree( obfClassEntry.getName() ); - String source = deobfuscator.getSource( tree ); - deobfuscator.getSourceIndex( tree, source ); - } - catch( Throwable t ) - { - throw new Error( "Unable to index " + obfClassEntry, t ); + for (ClassEntry obfClassEntry : classEntries) { + try { + CompilationUnit tree = deobfuscator.getSourceTree(obfClassEntry.getName()); + String source = deobfuscator.getSource(tree); + deobfuscator.getSourceIndex(tree, source); + } catch (Throwable t) { + throw new Error("Unable to index " + obfClassEntry, t); } } } diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java index 24091532..f805a655 100644 --- a/test/cuchaz/enigma/TestTokensConstructors.java +++ b/test/cuchaz/enigma/TestTokensConstructors.java @@ -24,129 +24,116 @@ import org.junit.Test; import cuchaz.enigma.mapping.BehaviorEntry; -public class TestTokensConstructors extends TokenChecker -{ - public TestTokensConstructors( ) - throws Exception - { - super( new File( "build/libs/testConstructors.obf.jar" ) ); +public class TestTokensConstructors extends TokenChecker { + + public TestTokensConstructors() throws Exception { + super(new File("build/libs/testConstructors.obf.jar")); } @Test - public void baseDeclarations( ) - { - assertThat( getDeclarationToken( newConstructor( "none/a", "()V" ) ), is( "a" ) ); - assertThat( getDeclarationToken( newConstructor( "none/a", "(I)V" ) ), is( "a" ) ); + public void baseDeclarations() { + assertThat(getDeclarationToken(newConstructor("none/a", "()V")), is("a")); + assertThat(getDeclarationToken(newConstructor("none/a", "(I)V")), is("a")); } @Test - public void subDeclarations( ) - { - assertThat( getDeclarationToken( newConstructor( "none/d", "()V" ) ), is( "d" ) ); - assertThat( getDeclarationToken( newConstructor( "none/d", "(I)V" ) ), is( "d" ) ); - assertThat( getDeclarationToken( newConstructor( "none/d", "(II)V" ) ), is( "d" ) ); - assertThat( getDeclarationToken( newConstructor( "none/d", "(III)V" ) ), is( "d" ) ); + public void subDeclarations() { + assertThat(getDeclarationToken(newConstructor("none/d", "()V")), is("d")); + assertThat(getDeclarationToken(newConstructor("none/d", "(I)V")), is("d")); + assertThat(getDeclarationToken(newConstructor("none/d", "(II)V")), is("d")); + assertThat(getDeclarationToken(newConstructor("none/d", "(III)V")), is("d")); } @Test - public void subsubDeclarations( ) - { - assertThat( getDeclarationToken( newConstructor( "none/e", "(I)V" ) ), is( "e" ) ); + public void subsubDeclarations() { + assertThat(getDeclarationToken(newConstructor("none/e", "(I)V")), is("e")); } @Test - public void defaultDeclarations( ) - { - assertThat( getDeclarationToken( newConstructor( "none/c", "()V" ) ), nullValue() ); + public void defaultDeclarations() { + assertThat(getDeclarationToken(newConstructor("none/c", "()V")), nullValue()); } @Test - public void baseDefaultReferences( ) - { - BehaviorEntry source = newConstructor( "none/a", "()V" ); + public void baseDefaultReferences() { + BehaviorEntry source = newConstructor("none/a", "()V"); assertThat( - getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "a", "()V" ) ), - containsInAnyOrder( "a" ) + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "a", "()V")), + containsInAnyOrder("a") ); assertThat( - getReferenceTokens( newBehaviorReferenceByConstructor( source, "none/d", "()V" ) ), - containsInAnyOrder( "super" ) // implicit call, decompiled to "super" + getReferenceTokens(newBehaviorReferenceByConstructor(source, "none/d", "()V")), + containsInAnyOrder("super") // implicit call, decompiled to "super" ); assertThat( - getReferenceTokens( newBehaviorReferenceByConstructor( source, "none/d", "(III)V" ) ), - containsInAnyOrder( "super" ) // implicit call, decompiled to "super" + getReferenceTokens(newBehaviorReferenceByConstructor(source, "none/d", "(III)V")), + containsInAnyOrder("super") // implicit call, decompiled to "super" ); } @Test - public void baseIntReferences( ) - { - BehaviorEntry source = newConstructor( "none/a", "(I)V" ); + public void baseIntReferences() { + BehaviorEntry source = newConstructor("none/a", "(I)V"); assertThat( - getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "b", "()V" ) ), - containsInAnyOrder( "a" ) + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "b", "()V")), + containsInAnyOrder("a") ); } - + @Test - public void subDefaultReferences( ) - { - BehaviorEntry source = newConstructor( "none/d", "()V" ); + public void subDefaultReferences() { + BehaviorEntry source = newConstructor("none/d", "()V"); assertThat( - getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "c", "()V" ) ), - containsInAnyOrder( "d" ) + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "c", "()V")), + containsInAnyOrder("d") ); assertThat( - getReferenceTokens( newBehaviorReferenceByConstructor( source, "none/d", "(I)V" ) ), - containsInAnyOrder( "this" ) + getReferenceTokens(newBehaviorReferenceByConstructor(source, "none/d", "(I)V")), + containsInAnyOrder("this") ); } - + @Test - public void subIntReferences( ) - { - BehaviorEntry source = newConstructor( "none/d", "(I)V" ); - assertThat( - getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "d", "()V" ) ), - containsInAnyOrder( "d" ) + public void subIntReferences() { + BehaviorEntry source = newConstructor("none/d", "(I)V"); + assertThat(getReferenceTokens( + newBehaviorReferenceByMethod(source, "none/b", "d", "()V")), + containsInAnyOrder("d") ); - assertThat( - getReferenceTokens( newBehaviorReferenceByConstructor( source, "none/d", "(II)V" ) ), - containsInAnyOrder( "this" ) + assertThat(getReferenceTokens( + newBehaviorReferenceByConstructor(source, "none/d", "(II)V")), + containsInAnyOrder("this") ); - assertThat( - getReferenceTokens( newBehaviorReferenceByConstructor( source, "none/e", "(I)V" ) ), - containsInAnyOrder( "super" ) + assertThat(getReferenceTokens( + newBehaviorReferenceByConstructor(source, "none/e", "(I)V")), + containsInAnyOrder("super") ); } - + @Test - public void subIntIntReferences( ) - { - BehaviorEntry source = newConstructor( "none/d", "(II)V" ); + public void subIntIntReferences() { + BehaviorEntry source = newConstructor("none/d", "(II)V"); assertThat( - getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "e", "()V" ) ), - containsInAnyOrder( "d" ) + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "e", "()V")), + containsInAnyOrder("d") ); } - + @Test - public void subsubIntReferences( ) - { - BehaviorEntry source = newConstructor( "none/e", "(I)V" ); + public void subsubIntReferences() { + BehaviorEntry source = newConstructor("none/e", "(I)V"); assertThat( - getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "f", "()V" ) ), - containsInAnyOrder( "e" ) + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "f", "()V")), + containsInAnyOrder("e") ); } - + @Test - public void defaultConstructableReferences( ) - { - BehaviorEntry source = newConstructor( "none/c", "()V" ); + public void defaultConstructableReferences() { + BehaviorEntry source = newConstructor("none/c", "()V"); assertThat( - getReferenceTokens( newBehaviorReferenceByMethod( source, "none/b", "g", "()V" ) ), - containsInAnyOrder( "c" ) + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "g", "()V")), + containsInAnyOrder("c") ); } } diff --git a/test/cuchaz/enigma/TokenChecker.java b/test/cuchaz/enigma/TokenChecker.java index c0852f32..524c5ec5 100644 --- a/test/cuchaz/enigma/TokenChecker.java +++ b/test/cuchaz/enigma/TokenChecker.java @@ -23,47 +23,41 @@ import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.Entry; -public class TokenChecker -{ +public class TokenChecker { + private Deobfuscator m_deobfuscator; - - protected TokenChecker( File jarFile ) - throws IOException - { - m_deobfuscator = new Deobfuscator( jarFile ); + + protected TokenChecker(File jarFile) throws IOException { + m_deobfuscator = new Deobfuscator(jarFile); } - protected String getDeclarationToken( Entry entry ) - { + protected String getDeclarationToken(Entry entry) { // decompile the class - CompilationUnit tree = m_deobfuscator.getSourceTree( entry.getClassName() ); + CompilationUnit tree = m_deobfuscator.getSourceTree(entry.getClassName()); // DEBUG - //tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null ); - String source = m_deobfuscator.getSource( tree ); - SourceIndex index = m_deobfuscator.getSourceIndex( tree, source ); + // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null ); + String source = m_deobfuscator.getSource(tree); + SourceIndex index = m_deobfuscator.getSourceIndex(tree, source); // get the token value - Token token = index.getDeclarationToken( entry ); - if( token == null ) - { + Token token = index.getDeclarationToken(entry); + if (token == null) { return null; } - return source.substring( token.start, token.end ); + return source.substring(token.start, token.end); } - @SuppressWarnings( "unchecked" ) - protected Collection getReferenceTokens( EntryReference reference ) - { + @SuppressWarnings("unchecked") + protected Collection getReferenceTokens(EntryReference reference) { // decompile the class - CompilationUnit tree = m_deobfuscator.getSourceTree( reference.context.getClassName() ); - String source = m_deobfuscator.getSource( tree ); - SourceIndex index = m_deobfuscator.getSourceIndex( tree, source ); + CompilationUnit tree = m_deobfuscator.getSourceTree(reference.context.getClassName()); + String source = m_deobfuscator.getSource(tree); + SourceIndex index = m_deobfuscator.getSourceIndex(tree, source); // get the token values List values = Lists.newArrayList(); - for( Token token : index.getReferenceTokens( (EntryReference)reference ) ) - { - values.add( source.substring( token.start, token.end ) ); + for (Token token : index.getReferenceTokens((EntryReference)reference)) { + values.add(source.substring(token.start, token.end)); } return values; } diff --git a/test/cuchaz/enigma/inputs/Keep.java b/test/cuchaz/enigma/inputs/Keep.java index 3c12baea..390e82fb 100644 --- a/test/cuchaz/enigma/inputs/Keep.java +++ b/test/cuchaz/enigma/inputs/Keep.java @@ -1,9 +1,7 @@ package cuchaz.enigma.inputs; -public class Keep -{ - public static void main( String[] args ) - { - System.out.println( "Keep me!" ); +public class Keep { + public static void main(String[] args) { + System.out.println("Keep me!"); } } diff --git a/test/cuchaz/enigma/inputs/constructors/BaseClass.java b/test/cuchaz/enigma/inputs/constructors/BaseClass.java index e6d87681..93453086 100644 --- a/test/cuchaz/enigma/inputs/constructors/BaseClass.java +++ b/test/cuchaz/enigma/inputs/constructors/BaseClass.java @@ -1,17 +1,15 @@ package cuchaz.enigma.inputs.constructors; // none/a -public class BaseClass -{ +public class BaseClass { + // ()V - public BaseClass( ) - { - System.out.println( "Default constructor" ); + public BaseClass() { + System.out.println("Default constructor"); } - + // (I)V - public BaseClass( int i ) - { - System.out.println( "Int constructor " + i ); + public BaseClass(int i) { + System.out.println("Int constructor " + i); } } diff --git a/test/cuchaz/enigma/inputs/constructors/Caller.java b/test/cuchaz/enigma/inputs/constructors/Caller.java index b2186198..57278751 100644 --- a/test/cuchaz/enigma/inputs/constructors/Caller.java +++ b/test/cuchaz/enigma/inputs/constructors/Caller.java @@ -1,54 +1,47 @@ package cuchaz.enigma.inputs.constructors; // none/b -public class Caller -{ +public class Caller { + // a()V - public void callBaseDefault( ) - { + public void callBaseDefault() { // none/a.()V - System.out.println( new BaseClass() ); + System.out.println(new BaseClass()); } - + // b()V - public void callBaseInt( ) - { + public void callBaseInt() { // none/a.(I)V - System.out.println( new BaseClass( 5 ) ); + System.out.println(new BaseClass(5)); } - + // c()V - public void callSubDefault( ) - { + public void callSubDefault() { // none/d.()V - System.out.println( new SubClass() ); + System.out.println(new SubClass()); } - + // d()V - public void callSubInt( ) - { + public void callSubInt() { // none/d.(I)V - System.out.println( new SubClass( 6 ) ); + System.out.println(new SubClass(6)); } - + // e()V - public void callSubIntInt( ) - { + public void callSubIntInt() { // none/d.(II)V - System.out.println( new SubClass( 4, 2 ) ); + System.out.println(new SubClass(4, 2)); } - + // f()V - public void callSubSubInt( ) - { + public void callSubSubInt() { // none/e.(I)V - System.out.println( new SubSubClass( 3 ) ); + System.out.println(new SubSubClass(3)); } // g()V - public void callDefaultConstructable() - { + public void callDefaultConstructable() { // none/c.()V - System.out.println( new DefaultConstructable() ); + System.out.println(new DefaultConstructable()); } } diff --git a/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java index 6cfd35e6..26a3ddbb 100644 --- a/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java +++ b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java @@ -1,6 +1,5 @@ package cuchaz.enigma.inputs.constructors; -public class DefaultConstructable -{ +public class DefaultConstructable { // only default constructor } diff --git a/test/cuchaz/enigma/inputs/constructors/SubClass.java b/test/cuchaz/enigma/inputs/constructors/SubClass.java index 6ef77323..fecfa2b5 100644 --- a/test/cuchaz/enigma/inputs/constructors/SubClass.java +++ b/test/cuchaz/enigma/inputs/constructors/SubClass.java @@ -1,32 +1,28 @@ package cuchaz.enigma.inputs.constructors; // none/d extends none/a -public class SubClass extends BaseClass -{ +public class SubClass extends BaseClass { + // ()V - public SubClass( ) - { + public SubClass() { // none/a.()V } // (I)V - public SubClass( int num ) - { + public SubClass(int num) { // ()V this(); - System.out.println( "SubClass " + num ); + System.out.println("SubClass " + num); } // (II)V - public SubClass( int a, int b ) - { + public SubClass(int a, int b) { // (I)V - this( a + b ); + this(a + b); } // (III)V - public SubClass( int a, int b, int c ) - { + public SubClass(int a, int b, int c) { // none/a.()V } } diff --git a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java index 76a0f1f3..ab84161b 100644 --- a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java +++ b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java @@ -1,12 +1,11 @@ package cuchaz.enigma.inputs.constructors; // none/e extends none/d -public class SubSubClass extends SubClass -{ +public class SubSubClass extends SubClass { + // (I)V - public SubSubClass( int i ) - { + public SubSubClass(int i) { // none/c.(I)V - super( i ); + super(i); } } diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java index 8402dde3..5b416c41 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java @@ -1,23 +1,21 @@ package cuchaz.enigma.inputs.inheritanceTree; // none/a -public abstract class BaseClass -{ +public abstract class BaseClass { + // a private String m_name; // (Ljava/lang/String;)V - protected BaseClass( String name ) - { + protected BaseClass(String name) { m_name = name; } // a()Ljava/lang/String; - public String getName( ) - { + public String getName() { return m_name; } // a()V - public abstract void doBaseThings( ); + public abstract void doBaseThings(); } diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java index ed507093..7a99d516 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java @@ -1,12 +1,11 @@ package cuchaz.enigma.inputs.inheritanceTree; // none/b extends none/a -public abstract class SubclassA extends BaseClass -{ +public abstract class SubclassA extends BaseClass { + // (Ljava/lang/String;)V - protected SubclassA( String name ) - { + protected SubclassA(String name) { // call to none/a.(Ljava/lang/String)V - super( name ); + super(name); } } diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java index fc4c8eed..c9485d31 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java @@ -1,16 +1,15 @@ package cuchaz.enigma.inputs.inheritanceTree; // none/c extends none/a -public class SubclassB extends BaseClass -{ +public class SubclassB extends BaseClass { + // a private int m_numThings; // ()V - protected SubclassB( ) - { + protected SubclassB() { // none/a.(Ljava/lang/String;)V - super( "B" ); + super("B"); // access to a m_numThings = 4; @@ -18,16 +17,14 @@ public class SubclassB extends BaseClass @Override // a()V - public void doBaseThings( ) - { + public void doBaseThings() { // call to none/a.a()Ljava/lang/String; - System.out.println( "Base things by B! " + getName() ); + System.out.println("Base things by B! " + getName()); } // b()V - public void doBThings( ) - { + public void doBThings() { // access to a - System.out.println( "" + m_numThings + " B things!" ); + System.out.println("" + m_numThings + " B things!"); } } diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java index b3b83429..afd03ac4 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java @@ -1,27 +1,24 @@ package cuchaz.enigma.inputs.inheritanceTree; // none/d extends none/b -public class SubsubclassAA extends SubclassA -{ - protected SubsubclassAA( ) - { +public class SubsubclassAA extends SubclassA { + + protected SubsubclassAA() { // call to none/b.(Ljava/lang/String;)V - super( "AA" ); + super("AA"); } @Override // a()Ljava/lang/String; - public String getName( ) - { + public String getName() { // call to none/b.a()Ljava/lang/String; return "subsub" + super.getName(); } @Override // a()V - public void doBaseThings( ) - { + public void doBaseThings() { // call to none/d.a()Ljava/lang/String; - System.out.println( "Base things by " + getName() ); + System.out.println("Base things by " + getName()); } } diff --git a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java index d36a514c..f5d9d1cf 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java +++ b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java @@ -1,14 +1,11 @@ package cuchaz.enigma.inputs.innerClasses; -public class Anonymous -{ - public void foo( ) - { - Runnable runnable = new Runnable( ) - { +public class Anonymous { + + public void foo() { + Runnable runnable = new Runnable() { @Override - public void run( ) - { + public void run() { // don't care } }; diff --git a/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java b/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java index e0a65e25..b3ba1af8 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java +++ b/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java @@ -1,16 +1,13 @@ package cuchaz.enigma.inputs.innerClasses; -public class AnonymousWithScopeArgs -{ - public static void foo( final Simple arg ) - { - System.out.println( new Object( ) - { +public class AnonymousWithScopeArgs { + + public static void foo(final Simple arg) { + System.out.println(new Object() { @Override - public String toString( ) - { + public String toString() { return arg.toString(); } - } ); + }); } } diff --git a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java index e24395c7..08135fe5 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java +++ b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java @@ -1,22 +1,20 @@ package cuchaz.enigma.inputs.innerClasses; -@SuppressWarnings( "unused" ) -public class ConstructorArgs -{ - class Inner - { +@SuppressWarnings("unused") +public class ConstructorArgs { + + class Inner { + private int a; - public Inner( int a ) - { + public Inner(int a) { this.a = a; } } Inner i; - public void foo( ) - { - i = new Inner( 5 ); + public void foo() { + i = new Inner(5); } } diff --git a/test/cuchaz/enigma/inputs/innerClasses/Simple.java b/test/cuchaz/enigma/inputs/innerClasses/Simple.java index 405c6399..cb536fa6 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/Simple.java +++ b/test/cuchaz/enigma/inputs/innerClasses/Simple.java @@ -1,9 +1,8 @@ package cuchaz.enigma.inputs.innerClasses; -public class Simple -{ - class Inner - { +public class Simple { + + class Inner { // nothing to do } } diff --git a/test/cuchaz/enigma/inputs/loneClass/LoneClass.java b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java index 961b012e..18c716e9 100644 --- a/test/cuchaz/enigma/inputs/loneClass/LoneClass.java +++ b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java @@ -1,16 +1,14 @@ package cuchaz.enigma.inputs.loneClass; -public class LoneClass -{ +public class LoneClass { + private String m_name; - public LoneClass( String name ) - { + public LoneClass(String name) { m_name = name; } - public String getName( ) - { + public String getName() { return m_name; } } -- cgit v1.2.3 From 56c036d7d18e2e18682bf14d71e8dbecf266072a Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 13 Jan 2015 23:40:23 -0500 Subject: started on command line interface for build system --- src/cuchaz/enigma/CommandMain.java | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/cuchaz/enigma/CommandMain.java b/src/cuchaz/enigma/CommandMain.java index 7f881749..6a01661b 100644 --- a/src/cuchaz/enigma/CommandMain.java +++ b/src/cuchaz/enigma/CommandMain.java @@ -3,5 +3,41 @@ package cuchaz.enigma; public class CommandMain { public static void main(String[] args) { + + // parse the args + if (args.length < 1) { + printHelp(); + return; + } + + // process the command + String command = args[0]; + if (command.equalsIgnoreCase("deobfuscate")) { + deobfuscate(args); + } else if(command.equalsIgnoreCase("decompile")) { + decompile(args); + } else { + System.out.println("Command not recognized: " + args[0]); + printHelp(); + } + } + + private static void printHelp() { + System.out.println(String.format("%s - %s", Constants.Name, Constants.Version)); + System.out.println("Usage:"); + System.out.println("\tjava -jar enigma.jar cuchaz.enigma.CommandMain "); + System.out.println("\twhere is one of:"); + System.out.println("\t\tdeobfuscate "); + System.out.println("\t\tdecompile "); + } + + private static void decompile(String[] args) { + // TODO + throw new Error("Not implemented yet"); + } + + private static void deobfuscate(String[] args) { + // TODO + throw new Error("Not implemented yet"); } } -- cgit v1.2.3 From ba67f6c0231157c0b07b37fe0a09fca381bb37d9 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 17 Jan 2015 16:20:15 -0500 Subject: added command-line interface for scriptable awesome --- build.py | 13 ++-- readme.gui.txt | 19 ------ readme.txt | 28 ++++++++ src/cuchaz/enigma/CommandMain.java | 134 +++++++++++++++++++++++++++++++------ 4 files changed, 149 insertions(+), 45 deletions(-) delete mode 100644 readme.gui.txt create mode 100644 readme.txt diff --git a/build.py b/build.py index 69182514..62b96d11 100644 --- a/build.py +++ b/build.py @@ -12,8 +12,11 @@ dirBuild = "build" dirTemp = os.path.join(dirBuild, "tmp") -def getJarFullName(name) : - return "%s-%s-%s.jar" % (projectName, name, version) +def getJarFullName(name=None) : + if name is not None: + return "%s-%s-%s.jar" % (projectName, name, version) + else: + return "%s-%s.jar" % (projectName, version) def buildGuiJar(): jarName = "gui" @@ -25,9 +28,9 @@ def buildGuiJar(): ssjb.delete(os.path.join(dirTemp, "LICENSE.txt")) ssjb.copyFile(dirTemp, "license.APL2.txt") ssjb.copyFile(dirTemp, "license.GPL3.txt") - ssjb.copyFile(dirTemp, "readme.gui.txt", renameTo="readme.txt") - manifest = ssjb.buildManifest("%s-%s" % (projectName, jarName), version, author, "cuchaz.enigma.Main") - ssjb.jar(os.path.join(dirBuild, getJarFullName(jarName)), dirTemp, manifest=manifest) + ssjb.copyFile(dirTemp, "readme.txt") + manifest = ssjb.buildManifest(projectName, version, author, "cuchaz.enigma.Main") + ssjb.jar(os.path.join(dirBuild, getJarFullName()), dirTemp, manifest=manifest) ssjb.delete(dirTemp) def buildTranslateJar(): diff --git a/readme.gui.txt b/readme.gui.txt deleted file mode 100644 index 81f985a0..00000000 --- a/readme.gui.txt +++ /dev/null @@ -1,19 +0,0 @@ - -Enigma v0.6 beta -A tool for deobfuscation of Java bytecode - -Copyright Jeff Martin, 2014 - - -LICENSE - -Enigma is distributed under the GNU General Public license version 3 - -Enigma includes a modified version of Procyon which is distributed under the Apache license version 2. Procyon is copyrighted by Mike Strobel, 2013 - -Enigma includes unmodified versions of the following libraries which are also released under the Apache license version 2. - Guava - Javassist - JSyntaxPane - -Copies of the GNU General Public license verion 3 and the Apache license v2 have been included in this distribution. diff --git a/readme.txt b/readme.txt new file mode 100644 index 00000000..3844f54e --- /dev/null +++ b/readme.txt @@ -0,0 +1,28 @@ + +Enigma v0.6 beta +A tool for deobfuscation of Java bytecode + +Copyright Jeff Martin, 2014 + + +LICENSE + +Enigma is distributed under the GNU General Public license version 3 + +Enigma includes a modified version of Procyon which is distributed under the Apache license version 2. Procyon is copyrighted by Mike Strobel, 2013 + +Enigma includes unmodified versions of the following libraries which are also released under the Apache license version 2. + Guava + Javassist + JSyntaxPane + +Copies of the GNU General Public license verion 3 and the Apache license v2 have been included in this distribution. + + +USING ENIGMA + +Launch the GUI: + java -jar enigma.jar + +Use Enigma on the command line: + java -cp enigma.jar cuchaz.enigma.CommandMain diff --git a/src/cuchaz/enigma/CommandMain.java b/src/cuchaz/enigma/CommandMain.java index 6a01661b..74bd4991 100644 --- a/src/cuchaz/enigma/CommandMain.java +++ b/src/cuchaz/enigma/CommandMain.java @@ -1,23 +1,65 @@ package cuchaz.enigma; +import java.io.File; +import java.io.FileReader; + +import cuchaz.enigma.Deobfuscator.ProgressListener; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsReader; + public class CommandMain { - public static void main(String[] args) { + public static class ConsoleProgressListener implements ProgressListener { - // parse the args - if (args.length < 1) { - printHelp(); - return; + private static final int ReportTime = 5000; // 5s + + private int m_totalWork; + private long m_startTime; + private long m_lastReportTime; + + @Override + public void init(int totalWork, String title) { + m_totalWork = totalWork; + m_startTime = System.currentTimeMillis(); + m_lastReportTime = m_startTime; + System.out.println(title); + } + + @Override + public void onProgress(int numDone, String message) { + + long now = System.currentTimeMillis(); + boolean isLastUpdate = numDone == m_totalWork; + boolean shouldReport = isLastUpdate || now - m_lastReportTime > ReportTime; + + if (shouldReport) { + int percent = numDone*100/m_totalWork; + System.out.println(String.format("\tProgress: %3d%%", percent)); + m_lastReportTime = now; + } + if (isLastUpdate) { + double elapsedSeconds = (now - m_startTime)/1000; + System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds)); + } } + } + + public static void main(String[] args) + throws Exception { - // process the command - String command = args[0]; - if (command.equalsIgnoreCase("deobfuscate")) { - deobfuscate(args); - } else if(command.equalsIgnoreCase("decompile")) { - decompile(args); - } else { - System.out.println("Command not recognized: " + args[0]); + try { + + // process the command + String command = getArg(args, 0, "command"); + if (command.equalsIgnoreCase("deobfuscate")) { + deobfuscate(args); + } else if(command.equalsIgnoreCase("decompile")) { + decompile(args); + } else { + throw new IllegalArgumentException("Command not recognized: " + command); + } + } catch (IllegalArgumentException ex) { + System.out.println(ex.getMessage()); printHelp(); } } @@ -25,19 +67,69 @@ public class CommandMain { private static void printHelp() { System.out.println(String.format("%s - %s", Constants.Name, Constants.Version)); System.out.println("Usage:"); - System.out.println("\tjava -jar enigma.jar cuchaz.enigma.CommandMain "); + System.out.println("\tjava -cp enigma.jar cuchaz.enigma.CommandMain "); System.out.println("\twhere is one of:"); System.out.println("\t\tdeobfuscate "); - System.out.println("\t\tdecompile "); + System.out.println("\t\tdecompile "); } - private static void decompile(String[] args) { - // TODO - throw new Error("Not implemented yet"); + private static void decompile(String[] args) + throws Exception { + File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); + File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); + File fileJarOut = getWritableFolder(getArg(args, 3, "out folder")); + Deobfuscator deobfuscator = getDeobfuscator(fileMappings, fileJarIn); + deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); } - private static void deobfuscate(String[] args) { - // TODO - throw new Error("Not implemented yet"); + private static void deobfuscate(String[] args) + throws Exception { + File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); + File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); + File fileJarOut = getWritableFile(getArg(args, 3, "out jar")); + Deobfuscator deobfuscator = getDeobfuscator(fileMappings, fileJarIn); + deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); + } + + private static Deobfuscator getDeobfuscator(File fileMappings, File fileJar) + throws Exception { + System.out.println("Reading mappings..."); + Mappings mappings = new MappingsReader().read(new FileReader(fileMappings)); + System.out.println("Reading jar..."); + Deobfuscator deobfuscator = new Deobfuscator(fileJar); + deobfuscator.setMappings(mappings); + return deobfuscator; + } + + private static String getArg(String[] args, int i, String name) { + if (i >= args.length) { + throw new IllegalArgumentException(name + " is required"); + } + return args[i]; + } + + private static File getWritableFile(String path) { + File file = new File(path).getAbsoluteFile(); + File dir = file.getParentFile(); + if (dir == null || !dir.exists()) { + throw new IllegalArgumentException("Cannot write to folder: " + file); + } + return file; + } + + private static File getWritableFolder(String path) { + File dir = new File(path).getAbsoluteFile(); + if (!dir.exists()) { + throw new IllegalArgumentException("Cannot write to folder: " + dir); + } + return dir; + } + + private static File getReadableFile(String path) { + File file = new File(path).getAbsoluteFile(); + if (!file.exists()) { + throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); + } + return file; } } -- cgit v1.2.3 From 5390e6a1a2e2750b6e065cab4d4fbcdab477e9a3 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 17 Jan 2015 20:11:44 -0500 Subject: move BytecodeTools to m3l --- .../enigma/bytecode/BytecodeIndexIterator.java | 151 ----------- src/cuchaz/enigma/bytecode/BytecodeTools.java | 287 --------------------- 2 files changed, 438 deletions(-) delete mode 100644 src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java delete mode 100644 src/cuchaz/enigma/bytecode/BytecodeTools.java diff --git a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java deleted file mode 100644 index fc2bac3d..00000000 --- a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java +++ /dev/null @@ -1,151 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.bytecode; - -import java.util.Iterator; - -import javassist.bytecode.BadBytecode; -import javassist.bytecode.Bytecode; -import javassist.bytecode.CodeAttribute; -import javassist.bytecode.CodeIterator; -import javassist.bytecode.Opcode; - -public class BytecodeIndexIterator implements Iterator { - - public static class Index { - - private CodeIterator m_iter; - private int m_pos; - private boolean m_isWide; - - protected Index(CodeIterator iter, int pos, boolean isWide) { - m_iter = iter; - m_pos = pos; - m_isWide = isWide; - } - - public int getIndex() { - if (m_isWide) { - return m_iter.s16bitAt(m_pos); - } else { - return m_iter.byteAt(m_pos); - } - } - - public void setIndex(int val) throws BadBytecode { - if (m_isWide) { - m_iter.write16bit(val, m_pos); - } else { - if (val < 256) { - // we can write the byte - m_iter.writeByte(val, m_pos); - } else { - // we need to upgrade this instruction to LDC_W - assert (m_iter.byteAt(m_pos - 1) == Opcode.LDC); - m_iter.insertGap(m_pos - 1, 1); - m_iter.writeByte(Opcode.LDC_W, m_pos - 1); - m_iter.write16bit(val, m_pos); - m_isWide = true; - - // move the iterator to the next opcode - m_iter.move(m_pos + 2); - } - } - - // sanity check - assert (val == getIndex()); - } - - public boolean isValid(Bytecode bytecode) { - return getIndex() >= 0 && getIndex() < bytecode.getConstPool().getSize(); - } - } - - private Bytecode m_bytecode; - private CodeAttribute m_attribute; - private CodeIterator m_iter; - private Index m_next; - - public BytecodeIndexIterator(Bytecode bytecode) throws BadBytecode { - m_bytecode = bytecode; - m_attribute = bytecode.toCodeAttribute(); - m_iter = m_attribute.iterator(); - - m_next = getNext(); - } - - @Override - public boolean hasNext() { - return m_next != null; - } - - @Override - public Index next() { - Index out = m_next; - try { - m_next = getNext(); - } catch (BadBytecode ex) { - throw new Error(ex); - } - return out; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - private Index getNext() throws BadBytecode { - while (m_iter.hasNext()) { - int pos = m_iter.next(); - int opcode = m_iter.byteAt(pos); - switch (opcode) { - - // for only these opcodes, the next two bytes are a const pool reference - case Opcode.ANEWARRAY: - case Opcode.CHECKCAST: - case Opcode.INSTANCEOF: - case Opcode.INVOKEDYNAMIC: - case Opcode.INVOKEINTERFACE: - case Opcode.INVOKESPECIAL: - case Opcode.INVOKESTATIC: - case Opcode.INVOKEVIRTUAL: - case Opcode.LDC_W: - case Opcode.LDC2_W: - case Opcode.MULTIANEWARRAY: - case Opcode.NEW: - case Opcode.PUTFIELD: - case Opcode.PUTSTATIC: - case Opcode.GETFIELD: - case Opcode.GETSTATIC: - return new Index(m_iter, pos + 1, true); - - case Opcode.LDC: - return new Index(m_iter, pos + 1, false); - } - } - - return null; - } - - public Iterable indices() { - return new Iterable() { - @Override - public Iterator iterator() { - return BytecodeIndexIterator.this; - } - }; - } - - public void saveChangesToBytecode() { - BytecodeTools.setBytecode(m_bytecode, m_attribute.getCode()); - } -} diff --git a/src/cuchaz/enigma/bytecode/BytecodeTools.java b/src/cuchaz/enigma/bytecode/BytecodeTools.java deleted file mode 100644 index 2e456f45..00000000 --- a/src/cuchaz/enigma/bytecode/BytecodeTools.java +++ /dev/null @@ -1,287 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.bytecode; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javassist.CtBehavior; -import javassist.bytecode.BadBytecode; -import javassist.bytecode.Bytecode; -import javassist.bytecode.CodeAttribute; -import javassist.bytecode.ConstPool; -import javassist.bytecode.ExceptionTable; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import cuchaz.enigma.Util; -import cuchaz.enigma.bytecode.BytecodeIndexIterator.Index; -import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; - -public class BytecodeTools { - - public static byte[] writeBytecode(Bytecode bytecode) throws IOException { - - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(buf); - - try { - // write the constant pool - new ConstPoolEditor(bytecode.getConstPool()).writePool(out); - - // write metadata - out.writeShort(bytecode.getMaxStack()); - out.writeShort(bytecode.getMaxLocals()); - out.writeShort(bytecode.getStackDepth()); - - // write the code - out.writeShort(bytecode.getSize()); - out.write(bytecode.get()); - - // write the exception table - int numEntries = bytecode.getExceptionTable().size(); - out.writeShort(numEntries); - for (int i = 0; i < numEntries; i++) { - out.writeShort(bytecode.getExceptionTable().startPc(i)); - out.writeShort(bytecode.getExceptionTable().endPc(i)); - out.writeShort(bytecode.getExceptionTable().handlerPc(i)); - out.writeShort(bytecode.getExceptionTable().catchType(i)); - } - - out.close(); - return buf.toByteArray(); - } catch (Exception ex) { - Util.closeQuietly(out); - throw new Error(ex); - } - } - - public static Bytecode readBytecode(byte[] bytes) throws IOException { - - ByteArrayInputStream buf = new ByteArrayInputStream(bytes); - DataInputStream in = new DataInputStream(buf); - - try { - // read the constant pool entries and update the class - ConstPool pool = ConstPoolEditor.readPool(in); - - // read metadata - int maxStack = in.readShort(); - int maxLocals = in.readShort(); - int stackDepth = in.readShort(); - - Bytecode bytecode = new Bytecode(pool, maxStack, maxLocals); - bytecode.setStackDepth(stackDepth); - - // read the code - int size = in.readShort(); - byte[] code = new byte[size]; - in.read(code); - setBytecode(bytecode, code); - - // read the exception table - int numEntries = in.readShort(); - for (int i = 0; i < numEntries; i++) { - bytecode.getExceptionTable().add(in.readShort(), in.readShort(), in.readShort(), in.readShort()); - } - - in.close(); - return bytecode; - } catch (Exception ex) { - Util.closeQuietly(in); - throw new Error(ex); - } - } - - public static Bytecode prepareMethodForBytecode(CtBehavior behavior, Bytecode bytecode) throws BadBytecode { - - // update the destination class const pool - bytecode = copyBytecodeToConstPool(behavior.getMethodInfo().getConstPool(), bytecode); - - // update method locals and stack - CodeAttribute attribute = behavior.getMethodInfo().getCodeAttribute(); - if (bytecode.getMaxLocals() > attribute.getMaxLocals()) { - attribute.setMaxLocals(bytecode.getMaxLocals()); - } - if (bytecode.getMaxStack() > attribute.getMaxStack()) { - attribute.setMaxStack(bytecode.getMaxStack()); - } - - return bytecode; - } - - public static Bytecode copyBytecodeToConstPool(ConstPool dest, Bytecode bytecode) throws BadBytecode { - - // get the entries this bytecode needs from the const pool - Set indices = Sets.newTreeSet(); - ConstPoolEditor editor = new ConstPoolEditor(bytecode.getConstPool()); - BytecodeIndexIterator iterator = new BytecodeIndexIterator(bytecode); - for (Index index : iterator.indices()) { - assert (index.isValid(bytecode)); - InfoType.gatherIndexTree(indices, editor, index.getIndex()); - } - - Map indexMap = Maps.newTreeMap(); - - ConstPool src = bytecode.getConstPool(); - ConstPoolEditor editorSrc = new ConstPoolEditor(src); - ConstPoolEditor editorDest = new ConstPoolEditor(dest); - - // copy entries over in order of level so the index mapping is easier - for (InfoType type : InfoType.getSortedByLevel()) { - for (int index : indices) { - ConstInfoAccessor entry = editorSrc.getItem(index); - - // skip entries that aren't this type - if (entry.getType() != type) { - continue; - } - - // make sure the source entry is valid before we copy it - assert (type.subIndicesAreValid(entry, editorSrc)); - assert (type.selfIndexIsValid(entry, editorSrc)); - - // make a copy of the entry so we can modify it safely - ConstInfoAccessor entryCopy = editorSrc.getItem(index).copy(); - assert (type.subIndicesAreValid(entryCopy, editorSrc)); - assert (type.selfIndexIsValid(entryCopy, editorSrc)); - - // remap the indices - type.remapIndices(indexMap, entryCopy); - assert (type.subIndicesAreValid(entryCopy, editorDest)); - - // put the copy in the destination pool - int newIndex = editorDest.addItem(entryCopy.getItem()); - entryCopy.setIndex(newIndex); - assert (type.selfIndexIsValid(entryCopy, editorDest)) : type + ", self: " + entryCopy + " dest: " + editorDest.getItem(entryCopy.getIndex()); - - // make sure the source entry is unchanged - assert (type.subIndicesAreValid(entry, editorSrc)); - assert (type.selfIndexIsValid(entry, editorSrc)); - - // add the index mapping so we can update the bytecode later - if (indexMap.containsKey(index)) { - throw new Error("Entry at index " + index + " already copied!"); - } - indexMap.put(index, newIndex); - } - } - - // make a new bytecode - Bytecode newBytecode = new Bytecode(dest, bytecode.getMaxStack(), bytecode.getMaxLocals()); - bytecode.setStackDepth(bytecode.getStackDepth()); - setBytecode(newBytecode, bytecode.get()); - setExceptionTable(newBytecode, bytecode.getExceptionTable()); - - // apply the mappings to the bytecode - BytecodeIndexIterator iter = new BytecodeIndexIterator(newBytecode); - for (Index index : iter.indices()) { - int oldIndex = index.getIndex(); - Integer newIndex = indexMap.get(oldIndex); - if (newIndex != null) { - // make sure this mapping makes sense - InfoType typeSrc = editorSrc.getItem(oldIndex).getType(); - InfoType typeDest = editorDest.getItem(newIndex).getType(); - assert (typeSrc == typeDest); - - // apply the mapping - index.setIndex(newIndex); - } - } - iter.saveChangesToBytecode(); - - // make sure all the indices are valid - iter = new BytecodeIndexIterator(newBytecode); - for (Index index : iter.indices()) { - assert (index.isValid(newBytecode)); - } - - return newBytecode; - } - - public static void setBytecode(Bytecode dest, byte[] src) { - if (src.length > dest.getSize()) { - dest.addGap(src.length - dest.getSize()); - } - assert (dest.getSize() == src.length); - for (int i = 0; i < src.length; i++) { - dest.write(i, src[i]); - } - } - - public static void setExceptionTable(Bytecode dest, ExceptionTable src) { - - // clear the dest exception table - int size = dest.getExceptionTable().size(); - for (int i = size - 1; i >= 0; i--) { - dest.getExceptionTable().remove(i); - } - - // copy the exception table - for (int i = 0; i < src.size(); i++) { - dest.getExceptionTable().add(src.startPc(i), src.endPc(i), src.handlerPc(i), src.catchType(i)); - } - } - - public static List getParameterTypes(String signature) { - List types = Lists.newArrayList(); - for (int i = 0; i < signature.length();) { - char c = signature.charAt(i); - - // handle parens - if (c == '(') { - i++; - c = signature.charAt(i); - } - if (c == ')') { - break; - } - - // find a type - String type = null; - - int arrayDim = 0; - while (c == '[') { - // advance to array type - arrayDim++; - i++; - c = signature.charAt(i); - } - - if (c == 'L') { - // read class type - int pos = signature.indexOf(';', i + 1); - String className = signature.substring(i + 1, pos); - type = "L" + className + ";"; - i = pos + 1; - } else { - // read primitive type - type = signature.substring(i, i + 1); - i++; - } - - // was it an array? - while (arrayDim-- > 0) { - type = "[" + type; - } - types.add(type); - } - return types; - } -} -- cgit v1.2.3 From 91460a865cc5d4f8d1e0c38fd08c1f071af147bb Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 18 Jan 2015 17:17:27 -0500 Subject: also translate source file attributes so it's easy to browse decompiled sources --- src/cuchaz/enigma/bytecode/ClassTranslator.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index bc12405c..735a8fa3 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -18,6 +18,7 @@ import javassist.CtField; import javassist.CtMethod; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; +import javassist.bytecode.SourceFileAttribute; import com.google.common.collect.Maps; @@ -125,5 +126,12 @@ public class ClassTranslator { } } ClassRenamer.renameClasses(c, map); + + // translate the source file attribute too + ClassEntry deobfClassEntry = map.get(classEntry); + if (deobfClassEntry != null) { + String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOuterClassName()) + ".java"; + c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); + } } } -- cgit v1.2.3 From ec86ae2cc015baeba1d86db52311905051765c33 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 18 Jan 2015 18:06:25 -0500 Subject: add the system classpath to the javassist class pool --- src/cuchaz/enigma/analysis/JarClassIterator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java index 8d9947c1..72a99122 100644 --- a/src/cuchaz/enigma/analysis/JarClassIterator.java +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -126,6 +126,7 @@ public class JarClassIterator implements Iterator { // get a javassist handle for the class String className = Descriptor.toJavaName(getClassEntry(entry).getName()); ClassPool classPool = new ClassPool(); + classPool.appendSystemPath(); classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray())); return classPool.get(className); } -- cgit v1.2.3 From 5f88fcdc4fa9e8147ad7072da09c2bb49e007946 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 18 Jan 2015 22:57:15 -0500 Subject: added inverse operation for moving classes out of the default package --- src/cuchaz/enigma/bytecode/ClassRenamer.java | 34 ++++++---------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index f8e63d16..a5fea926 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -14,7 +14,6 @@ import java.util.Map; import java.util.Set; import javassist.ClassMap; -import javassist.CtBehavior; import javassist.CtClass; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; @@ -24,8 +23,6 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.SignatureUpdater; -import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class ClassRenamer { @@ -92,8 +89,6 @@ public class ClassRenamer { } public static void moveAllClassesOutOfDefaultPackage(CtClass c, String newPackageName) { - - // rename all classes Map map = Maps.newHashMap(); for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { if (classEntry.isInDefaultPackage()) { @@ -101,30 +96,15 @@ public class ClassRenamer { } } ClassRenamer.renameClasses(c, map); - - // TEMP + } + + public static void moveAllClassesIntoDefaultPackage(CtClass c, String oldPackageName) { + Map map = Maps.newHashMap(); for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { - if (classEntry.isInDefaultPackage()) { - throw new Error("!!! " + classEntry); + if (classEntry.getPackageName().equals(oldPackageName)) { + map.put(classEntry, new ClassEntry(classEntry.getSimpleName())); } } - - // TEMP - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - if (behavior.getSignature() == null) { - continue; - } - - SignatureUpdater.update(behavior.getSignature(), new ClassNameUpdater() { - @Override - public String update(String className) { - ClassEntry classEntry = new ClassEntry(className); - if (classEntry.isInDefaultPackage()) { - throw new Error("!!! " + className); - } - return className; - } - }); - } + ClassRenamer.renameClasses(c, map); } } -- cgit v1.2.3 From 2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 19 Jan 2015 22:22:57 -0500 Subject: solved tricky issue with incorrect translation of fields/methods referenced by a subclass instead of the declaring class --- build.py | 79 +++++--- libs/proguard.jar | Bin 0 -> 853203 bytes proguard.conf | 7 + src/cuchaz/enigma/Deobfuscator.java | 8 +- src/cuchaz/enigma/Main.java | 2 +- .../enigma/analysis/ClassInheritanceTreeNode.java | 4 +- src/cuchaz/enigma/analysis/JarIndex.java | 65 ++---- .../enigma/analysis/MethodInheritanceTreeNode.java | 4 +- src/cuchaz/enigma/analysis/TranslationIndex.java | 172 ++++++++++++---- src/cuchaz/enigma/bytecode/ClassTranslator.java | 9 + src/cuchaz/enigma/mapping/JavassistUtil.java | 55 +++++ src/cuchaz/enigma/mapping/Mappings.java | 11 +- src/cuchaz/enigma/mapping/MappingsRenamer.java | 6 +- src/cuchaz/enigma/mapping/Translator.java | 57 ++++-- ssjb.py | 223 +++++++++++---------- test/cuchaz/enigma/TestDeobfuscator.java | 2 +- test/cuchaz/enigma/TestInnerClasses.java | 2 +- .../enigma/TestJarIndexConstructorReferences.java | 2 +- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 30 +-- test/cuchaz/enigma/TestJarIndexLoneClass.java | 15 +- test/cuchaz/enigma/TestSourceIndex.java | 1 + test/cuchaz/enigma/TestTokensConstructors.java | 2 +- 22 files changed, 472 insertions(+), 284 deletions(-) create mode 100644 libs/proguard.jar create mode 100644 proguard.conf create mode 100644 src/cuchaz/enigma/mapping/JavassistUtil.java diff --git a/build.py b/build.py index 62b96d11..b9d1121f 100644 --- a/build.py +++ b/build.py @@ -19,42 +19,65 @@ def getJarFullName(name=None) : return "%s-%s.jar" % (projectName, version) def buildGuiJar(): - jarName = "gui" - os.makedirs(dirTemp) - ssjb.copyFiles(dirTemp, dirBin, ssjb.findFiles(dirBin)) - ssjb.unpackJars(dirTemp, "ivy/bundles", recursive=True) - ssjb.unpackJars(dirTemp, "ivy/jars", recursive=True) - ssjb.unpackJars(dirTemp, "libs", recursive=True) - ssjb.delete(os.path.join(dirTemp, "LICENSE.txt")) - ssjb.copyFile(dirTemp, "license.APL2.txt") - ssjb.copyFile(dirTemp, "license.GPL3.txt") - ssjb.copyFile(dirTemp, "readme.txt") - manifest = ssjb.buildManifest(projectName, version, author, "cuchaz.enigma.Main") - ssjb.jar(os.path.join(dirBuild, getJarFullName()), dirTemp, manifest=manifest) - ssjb.delete(dirTemp) + jarName = "gui" + os.makedirs(dirTemp) + ssjb.copyFiles(dirTemp, dirBin, ssjb.findFiles(dirBin)) + ssjb.unpackJars(dirTemp, "ivy/bundles", recursive=True) + ssjb.unpackJars(dirTemp, "ivy/jars", recursive=True) + ssjb.unpackJars(dirTemp, "libs", recursive=True) + ssjb.delete(os.path.join(dirTemp, "LICENSE.txt")) + ssjb.copyFile(dirTemp, "license.APL2.txt") + ssjb.copyFile(dirTemp, "license.GPL3.txt") + ssjb.copyFile(dirTemp, "readme.txt") + manifest = ssjb.buildManifest(projectName, version, author, "cuchaz.enigma.Main") + ssjb.jar(os.path.join(dirBuild, getJarFullName()), dirTemp, manifest=manifest) + ssjb.delete(dirTemp) def buildTranslateJar(): - jarName = "translate" - os.makedirs(dirTemp) - files = ssjb.findFiles(dirBin, "cuchaz/enigma/mapping/*") - files += ssjb.findFiles(dirBin, "cuchaz/enigma/bytecode/*") - ssjb.copyFiles(dirTemp, dirBin, files) - ssjb.copyFile(dirTemp, "license.GPL3.txt", renameTo="license.txt") - ssjb.copyFile(dirTemp, "readme.translate.txt", renameTo="readme.txt") - manifest = ssjb.buildManifest("%s-%s" % (projectName, jarName), version, author) - ssjb.jar(os.path.join(dirBuild, getJarFullName(jarName)), dirTemp, manifest=manifest) - ssjb.delete(dirTemp) + jarName = "translate" + os.makedirs(dirTemp) + files = ssjb.findFiles(dirBin, "cuchaz/enigma/mapping/*") + files += ssjb.findFiles(dirBin, "cuchaz/enigma/bytecode/*") + ssjb.copyFiles(dirTemp, dirBin, files) + ssjb.copyFile(dirTemp, "license.GPL3.txt", renameTo="license.txt") + ssjb.copyFile(dirTemp, "readme.translate.txt", renameTo="readme.txt") + manifest = ssjb.buildManifest("%s-%s" % (projectName, jarName), version, author) + ssjb.jar(os.path.join(dirBuild, getJarFullName(jarName)), dirTemp, manifest=manifest) + ssjb.delete(dirTemp) def taskMain(): - ssjb.delete(dirBuild) - os.makedirs(dirBuild) - buildGuiJar() - buildTranslateJar() + ssjb.delete(dirBuild) + os.makedirs(dirBuild) + buildGuiJar() + buildTranslateJar() +def makeTestJar(name, glob): + + pathJar = os.path.join(dirBuild, "%s.jar" % name) + pathObfJar = os.path.join(dirBuild, "%s.obf.jar" % name) + + # build the deobf jar + ssjb.delete(dirTemp) + os.makedirs(dirTemp) + ssjb.copyFiles(dirTemp, dirBin, ssjb.findFiles(dirBin, "cuchaz/enigma/inputs/Keep.class")) + ssjb.copyFiles(dirTemp, dirBin, ssjb.findFiles(dirBin, glob)) + ssjb.jar(pathJar, dirTemp) + ssjb.delete(dirTemp) + + # build the obf jar + ssjb.callJavaJar("libs/proguard.jar", ["@proguard.conf", "-injars", pathJar, "-outjars", pathObfJar]) + +def taskBuildTestJars(): + makeTestJar("testLoneClass", "cuchaz/enigma/inputs/loneClass/*.class") + makeTestJar("testConstructors", "cuchaz/enigma/inputs/constructors/*.class") + makeTestJar("testInheritanceTree", "cuchaz/enigma/inputs/inheritanceTree/*.class") + makeTestJar("testInnerClasses", "cuchaz/enigma/inputs/innerClasses/*.class") + ssjb.registerTask("main", taskMain) +ssjb.registerTask("buildTestJars", taskBuildTestJars) if __name__ == "__main__": - ssjb.run() + ssjb.run() diff --git a/libs/proguard.jar b/libs/proguard.jar new file mode 100644 index 00000000..a948c89b Binary files /dev/null and b/libs/proguard.jar differ diff --git a/proguard.conf b/proguard.conf new file mode 100644 index 00000000..e1f04aee --- /dev/null +++ b/proguard.conf @@ -0,0 +1,7 @@ +-libraryjars /lib/rt.jar +-overloadaggressively +-repackageclasses +-allowaccessmodification +-dontoptimize +-dontshrink +-keep class cuchaz.enigma.inputs.Keep \ No newline at end of file diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 679518a4..23057223 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -144,7 +144,7 @@ public class Deobfuscator { // fields for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { FieldEntry fieldEntry = new FieldEntry(obfClassEntry, fieldMapping.getObfName()); - ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass(fieldEntry); + ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(fieldEntry); if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(fieldEntry.getClassEntry())) { boolean wasMoved = renamer.moveFieldToObfClass(classMapping, fieldMapping, resolvedObfClassEntry); if (wasMoved) { @@ -167,7 +167,7 @@ public class Deobfuscator { methodMapping.getObfName(), methodMapping.getObfSignature() ); - ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass(methodEntry); + ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(methodEntry); if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(methodEntry.getClassEntry())) { boolean wasMoved = renamer.moveMethodToObfClass(classMapping, methodMapping, resolvedObfClassEntry); if (wasMoved) { @@ -233,7 +233,7 @@ public class Deobfuscator { public Translator getTranslator(TranslationDirection direction) { Translator translator = m_translatorCache.get(direction); if (translator == null) { - translator = m_mappings.getTranslator(direction); + translator = m_mappings.getTranslator(direction, m_jarIndex.getTranslationIndex()); m_translatorCache.put(direction, translator); } return translator; @@ -311,7 +311,7 @@ public class Deobfuscator { Entry obfEntry = obfuscateEntry(deobfReference.entry); // try to resolve the class - ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass(obfEntry); + ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(obfEntry); if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { // change the class of the entry obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index f8d3afe2..1891ded8 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -28,7 +28,7 @@ public class Main { } // DEBUG - // gui.getController().openDeclaration( new ClassEntry( "none/ces" ) ); + //gui.getController().openDeclaration(new ClassEntry("none/bxq")); } private static File getFile(String path) { diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java index b132305c..3eaa3912 100644 --- a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -51,8 +51,8 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { public void load(TranslationIndex ancestries, boolean recurse) { // get all the child nodes List nodes = Lists.newArrayList(); - for (String subclassName : ancestries.getSubclassNames(m_obfClassName)) { - nodes.add(new ClassInheritanceTreeNode(m_deobfuscatingTranslator, subclassName)); + for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(m_obfClassName))) { + nodes.add(new ClassInheritanceTreeNode(m_deobfuscatingTranslator, subclassEntry.getName())); } // add them to this node diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 4b03a332..c96d3bc4 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -114,8 +114,8 @@ public class JarIndex { // step 3: index extends, implements, fields, and methods for (CtClass c : JarClassIterator.classes(jar)) { ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + m_translationIndex.indexClass(c); String className = Descriptor.toJvmName(c.getName()); - m_translationIndex.addSuperclass(className, Descriptor.toJvmName(c.getClassFile().getSuperclass())); for (String interfaceName : c.getClassFile().getInterfaces()) { className = Descriptor.toJvmName(className); interfaceName = Descriptor.toJvmName(interfaceName); @@ -191,8 +191,6 @@ public class JarIndex { String className = Descriptor.toJvmName(field.getDeclaringClass().getName()); FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName()); - m_translationIndex.addField(className, field.getName()); - // is the field a class type? if (field.getSignature().startsWith("L")) { ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1)); @@ -230,13 +228,12 @@ public class JarIndex { behavior.instrument(new ExprEditor() { @Override public void edit(MethodCall call) { - String className = Descriptor.toJvmName(call.getClassName()); MethodEntry calledMethodEntry = new MethodEntry( - new ClassEntry(className), + new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getMethodName(), call.getSignature() ); - ClassEntry resolvedClassEntry = resolveEntryClass(calledMethodEntry); + ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledMethodEntry); if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { calledMethodEntry = new MethodEntry( resolvedClassEntry, @@ -254,12 +251,11 @@ public class JarIndex { @Override public void edit(FieldAccess call) { - String className = Descriptor.toJvmName(call.getClassName()); FieldEntry calledFieldEntry = new FieldEntry( - new ClassEntry(className), + new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getFieldName() ); - ClassEntry resolvedClassEntry = resolveEntryClass(calledFieldEntry); + ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry); if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName()); } @@ -273,9 +269,8 @@ public class JarIndex { @Override public void edit(ConstructorCall call) { - String className = Descriptor.toJvmName(call.getClassName()); ConstructorEntry calledConstructorEntry = new ConstructorEntry( - new ClassEntry(className), + new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getSignature() ); EntryReference reference = new EntryReference( @@ -288,9 +283,8 @@ public class JarIndex { @Override public void edit(NewExpr call) { - String className = Descriptor.toJvmName(call.getClassName()); ConstructorEntry calledConstructorEntry = new ConstructorEntry( - new ClassEntry(className), + new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getSignature() ); EntryReference reference = new EntryReference( @@ -306,25 +300,6 @@ public class JarIndex { } } - public ClassEntry resolveEntryClass(Entry obfEntry) { - - // this entry could refer to a method on a class where the method is not actually implemented - // travel up the inheritance tree to find the closest implementation - while (!containsObfEntry(obfEntry)) { - // is there a parent class? - String superclassName = m_translationIndex.getSuperclassName(obfEntry.getClassName()); - if (superclassName == null) { - // this is probably a method from a class in a library - // we can't trace the implementation up any higher unless we index the library - return null; - } - - // move up to the parent class - obfEntry = obfEntry.cloneToNewClass(new ClassEntry(superclassName)); - } - return obfEntry.getClassEntry(); - } - private CtMethod getBridgedMethod(CtMethod method) { // bridge methods just call another method, cast it to the return type, and return the result @@ -402,9 +377,9 @@ public class JarIndex { if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { // is the entry a superclass of the context? - String calledClassName = reference.entry.getClassName(); - String callerSuperclassName = m_translationIndex.getSuperclassName(reference.context.getClassName()); - if (callerSuperclassName != null && callerSuperclassName.equals(calledClassName)) { + ClassEntry calledClassEntry = reference.entry.getClassEntry(); + ClassEntry superclassEntry = m_translationIndex.getSuperclass(reference.context.getClassEntry()); + if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) { // it's a super call, skip continue; } @@ -599,7 +574,9 @@ public class JarIndex { // get the root node List ancestry = Lists.newArrayList(); ancestry.add(obfClassEntry.getName()); - ancestry.addAll(m_translationIndex.getAncestry(obfClassEntry.getName())); + for (ClassEntry classEntry : m_translationIndex.getAncestry(obfClassEntry)) { + ancestry.add(classEntry.getName()); + } ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, ancestry.get(ancestry.size() - 1) @@ -625,21 +602,21 @@ public class JarIndex { public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { // travel to the ancestor implementation - String baseImplementationClassName = obfMethodEntry.getClassName(); - for (String ancestorClassName : m_translationIndex.getAncestry(obfMethodEntry.getClassName())) { + ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); + for (ClassEntry ancestorClassEntry : m_translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { MethodEntry ancestorMethodEntry = new MethodEntry( - new ClassEntry(ancestorClassName), + new ClassEntry(ancestorClassEntry), obfMethodEntry.getName(), obfMethodEntry.getSignature() ); if (containsObfBehavior(ancestorMethodEntry)) { - baseImplementationClassName = ancestorClassName; + baseImplementationClassEntry = ancestorClassEntry; } } // make a root node at the base MethodEntry methodEntry = new MethodEntry( - new ClassEntry(baseImplementationClassName), + baseImplementationClassEntry, obfMethodEntry.getName(), obfMethodEntry.getSignature() ); @@ -781,8 +758,8 @@ public class JarIndex { public Set getInterfaces(String className) { Set interfaceNames = new HashSet(); interfaceNames.addAll(m_interfaces.get(className)); - for (String ancestor : m_translationIndex.getAncestry(className)) { - interfaceNames.addAll(m_interfaces.get(ancestor)); + for (ClassEntry ancestor : m_translationIndex.getAncestry(new ClassEntry(className))) { + interfaceNames.addAll(m_interfaces.get(ancestor.getName())); } return interfaceNames; } @@ -795,7 +772,7 @@ public class JarIndex { String interfaceName = entry.getValue(); if (interfaceName.equals(targetInterfaceName)) { classNames.add(className); - m_translationIndex.getSubclassNamesRecursively(classNames, className); + m_translationIndex.getSubclassNamesRecursively(classNames, new ClassEntry(className)); } } return classNames; diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index eba8d874..87182204 100644 --- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -71,9 +71,9 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { public void load(JarIndex index, boolean recurse) { // get all the child nodes List nodes = Lists.newArrayList(); - for (String subclassName : index.getTranslationIndex().getSubclassNames(m_entry.getClassName())) { + for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(m_entry.getClassEntry())) { MethodEntry methodEntry = new MethodEntry( - new ClassEntry(subclassName), + subclassEntry, m_entry.getName(), m_entry.getSignature() ); diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index c14fd593..4a356eb6 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -11,97 +11,185 @@ package cuchaz.enigma.analysis; import java.io.Serializable; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; -import javassist.bytecode.Descriptor; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtField; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.Translator; + public class TranslationIndex implements Serializable { private static final long serialVersionUID = 738687982126844179L; - private Map m_superclasses; - private Multimap m_fields; + private Map m_superclasses; + private Multimap m_fieldEntries; + private Multimap m_behaviorEntries; public TranslationIndex() { m_superclasses = Maps.newHashMap(); - m_fields = HashMultimap.create(); - } - - public TranslationIndex(TranslationIndex other) { - m_superclasses = Maps.newHashMap(other.m_superclasses); - m_fields = HashMultimap.create(other.m_fields); + m_fieldEntries = HashMultimap.create(); + m_behaviorEntries = HashMultimap.create(); } - public void addSuperclass(String className, String superclassName) { - className = Descriptor.toJvmName(className); - superclassName = Descriptor.toJvmName(superclassName); + public TranslationIndex(TranslationIndex other, Translator translator) { - if (className.equals(superclassName)) { - throw new IllegalArgumentException("Class cannot be its own superclass! " + className); + // translate the superclasses + m_superclasses = Maps.newHashMap(); + for (Map.Entry mapEntry : other.m_superclasses.entrySet()) { + m_superclasses.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); } - if (!isJre(className) && !isJre(superclassName)) { - m_superclasses.put(className, superclassName); + // translate the fields + m_fieldEntries = HashMultimap.create(); + for (Map.Entry mapEntry : other.m_fieldEntries.entries()) { + m_fieldEntries.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + + m_behaviorEntries = HashMultimap.create(); + for (Map.Entry mapEntry : other.m_behaviorEntries.entries()) { + m_behaviorEntries.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); } } - public void addField(String className, String fieldName) { - m_fields.put(className, fieldName); + public void indexClass(CtClass c) { + + ClassEntry classEntry = JavassistUtil.getClassEntry(c); + + // add the superclass + ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c); + if (!isJre(classEntry) && !isJre(superclassEntry)) { + m_superclasses.put(classEntry, superclassEntry); + } + + // add fields + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); + } + + // add behaviors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior); + m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); + } } public void renameClasses(Map renames) { EntryRenamer.renameClassesInMap(renames, m_superclasses); - EntryRenamer.renameClassesInMultimap(renames, m_fields); + EntryRenamer.renameClassesInMultimap(renames, m_fieldEntries); + EntryRenamer.renameClassesInMultimap(renames, m_behaviorEntries); } - public String getSuperclassName(String className) { - return m_superclasses.get(className); + public ClassEntry getSuperclass(ClassEntry classEntry) { + return m_superclasses.get(classEntry); } - public List getAncestry(String className) { - List ancestors = new ArrayList(); - while (className != null) { - className = getSuperclassName(className); - if (className != null) { - ancestors.add(className); + public List getAncestry(ClassEntry classEntry) { + List ancestors = Lists.newArrayList(); + while (classEntry != null) { + classEntry = getSuperclass(classEntry); + if (classEntry != null) { + ancestors.add(classEntry); } } return ancestors; } - public List getSubclassNames(String className) { + public List getSubclass(ClassEntry classEntry) { // linear search is fast enough for now - List subclasses = Lists.newArrayList(); - for (Map.Entry entry : m_superclasses.entrySet()) { - String subclass = entry.getKey(); - String superclass = entry.getValue(); - if (className.equals(superclass)) { + List subclasses = Lists.newArrayList(); + for (Map.Entry entry : m_superclasses.entrySet()) { + ClassEntry subclass = entry.getKey(); + ClassEntry superclass = entry.getValue(); + if (classEntry.equals(superclass)) { subclasses.add(subclass); } } return subclasses; } - public void getSubclassNamesRecursively(Set out, String className) { - for (String subclassName : getSubclassNames(className)) { - out.add(subclassName); - getSubclassNamesRecursively(out, subclassName); + public void getSubclassesRecursively(Set out, ClassEntry classEntry) { + for (ClassEntry subclassEntry : getSubclass(classEntry)) { + out.add(subclassEntry); + getSubclassesRecursively(out, subclassEntry); + } + } + + public void getSubclassNamesRecursively(Set out, ClassEntry classEntry) { + for (ClassEntry subclassEntry : getSubclass(classEntry)) { + out.add(subclassEntry.getName()); + getSubclassNamesRecursively(out, subclassEntry); } } - public boolean containsField(String className, String fieldName) { - return m_fields.containsEntry(className, fieldName); + public boolean entryExists(Entry entry) { + if (entry instanceof FieldEntry) { + return fieldExists((FieldEntry)entry); + } else if (entry instanceof BehaviorEntry) { + return behaviorExists((BehaviorEntry)entry); + } else if (entry instanceof ArgumentEntry) { + return behaviorExists(((ArgumentEntry)entry).getBehaviorEntry()); + } + throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); + } + + public boolean fieldExists(FieldEntry fieldEntry) { + return m_fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); + } + + public boolean behaviorExists(BehaviorEntry behaviorEntry) { + return m_behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); + } + + public ClassEntry resolveEntryClass(Entry entry) { + + if (entry instanceof ClassEntry) { + return (ClassEntry)entry; + } + + // this entry could refer to a method on a class where the method is not actually implemented + // travel up the inheritance tree to find the closest implementation + while (!entryExists(entry)) { + + // is there a parent class? + ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); + if (superclassEntry == null) { + // this is probably a method from a class in a library + // we can't trace the implementation up any higher unless we index the library + return null; + } + + // move up to the parent class + entry = entry.cloneToNewClass(superclassEntry); + } + return entry.getClassEntry(); } - private boolean isJre(String className) { - return className.startsWith("java/") || className.startsWith("javax/"); + private boolean isJre(ClassEntry classEntry) { + return classEntry.getPackageName().startsWith("java") || classEntry.getPackageName().startsWith("javax"); } } diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 735a8fa3..64418307 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -38,6 +38,7 @@ public class ClassTranslator { } public void translate(CtClass c) { + // NOTE: the order of these translations is very important // translate all the field and method references in the code by editing the constant pool @@ -45,7 +46,9 @@ public class ClassTranslator { ConstPoolEditor editor = new ConstPoolEditor(constants); for (int i = 1; i < constants.getSize(); i++) { switch (constants.getTag(i)) { + case ConstPool.CONST_Fieldref: { + // translate the name FieldEntry entry = new FieldEntry( new ClassEntry(Descriptor.toJvmName(constants.getFieldrefClassName(i))), @@ -53,6 +56,11 @@ public class ClassTranslator { ); FieldEntry translatedEntry = m_translator.translateEntry(entry); + // TEMP + if (entry.toString().equals("none/bxq.m")) { + System.out.println("FIELD: " + entry + " -> " + translatedEntry); + } + // translate the type String type = constants.getFieldrefType(i); String translatedType = m_translator.translateSignature(type); @@ -65,6 +73,7 @@ public class ClassTranslator { case ConstPool.CONST_Methodref: case ConstPool.CONST_InterfaceMethodref: { + // translate the name and type BehaviorEntry entry = BehaviorEntryFactory.create( Descriptor.toJvmName(editor.getMemberrefClassname(i)), diff --git a/src/cuchaz/enigma/mapping/JavassistUtil.java b/src/cuchaz/enigma/mapping/JavassistUtil.java new file mode 100644 index 00000000..b011e0be --- /dev/null +++ b/src/cuchaz/enigma/mapping/JavassistUtil.java @@ -0,0 +1,55 @@ +package cuchaz.enigma.mapping; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.CtMethod; +import javassist.bytecode.Descriptor; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class JavassistUtil { + + public static ClassEntry getClassEntry(CtClass c) { + return new ClassEntry(Descriptor.toJvmName(c.getName())); + } + + public static ClassEntry getSuperclassEntry(CtClass c) { + return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); + } + + public static MethodEntry getMethodEntry(CtMethod method) { + return new MethodEntry( + getClassEntry(method.getDeclaringClass()), + method.getName(), + method.getMethodInfo().getDescriptor() + ); + } + + public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { + return new ConstructorEntry( + getClassEntry(constructor.getDeclaringClass()), + constructor.getMethodInfo().getDescriptor() + ); + } + + public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { + if (behavior instanceof CtMethod) { + return getMethodEntry((CtMethod)behavior); + } else if (behavior instanceof CtConstructor) { + return getConstructorEntry((CtConstructor)behavior); + } + throw new Error("behavior is neither Method nor Constructor!"); + } + + public static FieldEntry getFieldEntry(CtField field) { + return new FieldEntry( + getClassEntry(field.getDeclaringClass()), + field.getName() + ); + } +} diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index c5e38f4b..cc560a87 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -24,6 +24,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import cuchaz.enigma.Util; +import cuchaz.enigma.analysis.TranslationIndex; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class Mappings implements Serializable { @@ -104,11 +105,11 @@ public class Mappings implements Serializable { return m_classesByDeobf.get(deobfName); } - public Translator getTranslator(TranslationDirection direction) { + public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { switch (direction) { case Deobfuscating: - return new Translator(direction, m_classesByObf); + return new Translator(direction, m_classesByObf, index); case Obfuscating: @@ -122,7 +123,11 @@ public class Mappings implements Serializable { } } - return new Translator(direction, classes); + // translate the translation index + // NOTE: this isn't actually recursive + TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); + + return new Translator(direction, classes, deobfIndex); default: throw new Error("Invalid translation direction!"); diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index cb95f42b..3aac65a1 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -100,10 +100,10 @@ public class MappingsRenamer { deobfName = NameValidator.validateMethodName(deobfName); for (MethodEntry entry : implementations) { - String deobfSignature = m_mappings.getTranslator(TranslationDirection.Deobfuscating).translateSignature(obf.getSignature()); + String deobfSignature = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateSignature(obf.getSignature()); MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, deobfSignature); if (m_mappings.containsDeobfMethod(entry.getClassEntry(), deobfName, entry.getSignature()) || m_index.containsObfBehavior(targetEntry)) { - String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating).translateClass(entry.getClassName()); + String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(entry.getClassName()); throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); } } @@ -117,7 +117,7 @@ public class MappingsRenamer { deobfName = NameValidator.validateMethodName(deobfName); MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); if (m_mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) || m_index.containsObfBehavior(targetEntry)) { - String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating).translateClass(obf.getClassName()); + String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(obf.getClassName()); throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); } diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index d8d9f480..a5a3e2f0 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -14,21 +14,24 @@ import java.util.Map; import com.google.common.collect.Maps; +import cuchaz.enigma.analysis.TranslationIndex; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class Translator { private TranslationDirection m_direction; private Map m_classes; + private TranslationIndex m_index; public Translator() { m_direction = null; m_classes = Maps.newHashMap(); } - public Translator(TranslationDirection direction, Map classes) { + public Translator(TranslationDirection direction, Map classes, TranslationIndex index) { m_direction = direction; m_classes = classes; + m_index = index; } @SuppressWarnings("unchecked") @@ -100,17 +103,22 @@ public class Translator { public String translate(FieldEntry in) { - // look for the class - ClassMapping classMapping = findClassMapping(in.getClassEntry()); - if (classMapping != null) { - - // look for the field - String translatedName = m_direction.choose( - classMapping.getDeobfFieldName(in.getName()), - classMapping.getObfFieldName(in.getName()) - ); - if (translatedName != null) { - return translatedName; + // resolve the class entry + ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in); + if (resolvedClassEntry != null) { + + // look for the class + ClassMapping classMapping = findClassMapping(resolvedClassEntry); + if (classMapping != null) { + + // look for the field + String translatedName = m_direction.choose( + classMapping.getDeobfFieldName(in.getName()), + classMapping.getObfFieldName(in.getName()) + ); + if (translatedName != null) { + return translatedName; + } } } return null; @@ -126,15 +134,22 @@ public class Translator { public String translate(MethodEntry in) { - // look for class - ClassMapping classMapping = findClassMapping(in.getClassEntry()); - if (classMapping != null) { - - // look for the method - MethodMapping methodMapping = m_direction.choose(classMapping.getMethodByObf(in.getName(), in.getSignature()), - classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature()))); - if (methodMapping != null) { - return m_direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); + // resolve the class entry + ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in); + if (resolvedClassEntry != null) { + + // look for class + ClassMapping classMapping = findClassMapping(resolvedClassEntry); + if (classMapping != null) { + + // look for the method + MethodMapping methodMapping = m_direction.choose( + classMapping.getMethodByObf(in.getName(), in.getSignature()), + classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature())) + ); + if (methodMapping != null) { + return m_direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); + } } } return null; diff --git a/ssjb.py b/ssjb.py index d3e2bb52..7f051cc5 100644 --- a/ssjb.py +++ b/ssjb.py @@ -17,29 +17,30 @@ import fnmatch tasks = {} def registerTask(name, func): - tasks[name] = func + tasks[name] = func def run(): - # get the task name - taskName = "main" - if len(sys.argv) > 1: - taskName = sys.argv[1] - - # find that task - try: - task = tasks[taskName] - except: - print "Couldn't find task: %s" % taskName - return + # get the task name + taskName = "main" + if len(sys.argv) > 1: + taskName = sys.argv[1] + + # find that task + try: + task = tasks[taskName] + except: + print "Couldn't find task: %s" % taskName + return - # run it! - task() + # run it! + print "Running task: %s" % taskName + task() # set up the default main task def mainTask(): - print "The main task doesn't do anything by default" + print "The main task doesn't do anything by default" registerTask("main", mainTask) @@ -47,117 +48,123 @@ registerTask("main", mainTask) # library of useful functions def findFiles(dirSrc, pattern=None): - out = [] - for root, dirs, files in os.walk(dirSrc): - for file in files: - path = os.path.join(root, file)[len(dirSrc) + 1:] - if pattern is None or fnmatch.fnmatch(path, pattern): - out.append(path) - return out + out = [] + for root, dirs, files in os.walk(dirSrc): + for file in files: + path = os.path.join(root, file)[len(dirSrc) + 1:] + if pattern is None or fnmatch.fnmatch(path, pattern): + out.append(path) + return out def copyFile(dirDest, pathSrc, renameTo=None): - (dirParent, filename) = os.path.split(pathSrc) - if renameTo is None: - renameTo = filename - pathDest = os.path.join(dirDest, renameTo) - shutil.copy2(pathSrc, pathDest) + (dirParent, filename) = os.path.split(pathSrc) + if renameTo is None: + renameTo = filename + pathDest = os.path.join(dirDest, renameTo) + shutil.copy2(pathSrc, pathDest) def copyFiles(dirDest, dirSrc, paths): - for path in paths: - pathSrc = os.path.join(dirSrc, path) - pathDest = os.path.join(dirDest, path) - dirParent = os.path.dirname(pathDest) - if not os.path.isdir(dirParent): - os.makedirs(dirParent) - shutil.copy2(pathSrc, pathDest) + for path in paths: + pathSrc = os.path.join(dirSrc, path) + pathDest = os.path.join(dirDest, path) + dirParent = os.path.dirname(pathDest) + if not os.path.isdir(dirParent): + os.makedirs(dirParent) + shutil.copy2(pathSrc, pathDest) def patternStringToRegex(patternString): - # escape special chars - patternString = re.escape(patternString) - - # process ** and * wildcards - replacements = { - re.escape("**"): ".*", - re.escape("*"): "[^" + os.sep + "]+" - } - def getReplacement(match): - print "matched", match - return "a" - patternString = re.compile("(\\\\\*)+").sub(lambda m: replacements[m.group(0)], patternString) - - return re.compile("^" + patternString + "$") + # escape special chars + patternString = re.escape(patternString) + + # process ** and * wildcards + replacements = { + re.escape("**"): ".*", + re.escape("*"): "[^" + os.sep + "]+" + } + def getReplacement(match): + print "matched", match + return "a" + patternString = re.compile("(\\\\\*)+").sub(lambda m: replacements[m.group(0)], patternString) + + return re.compile("^" + patternString + "$") def matchesAnyPath(path, patternStrings): - for patternString in patternStrings: - pattern = patternStringToRegex(patternString) - # TEMP - print path, pattern.match(path) is not None - if pattern.match(path) is not None: - return True - return False + for patternString in patternStrings: + pattern = patternStringToRegex(patternString) + # TEMP + print path, pattern.match(path) is not None + if pattern.match(path) is not None: + return True + return False def delete(path): - try: - if os.path.isdir(path): - shutil.rmtree(path) - elif os.path.isfile(path): - os.remove(path) - except: - # don't care if it failed - pass + try: + if os.path.isdir(path): + shutil.rmtree(path) + elif os.path.isfile(path): + os.remove(path) + except: + # don't care if it failed + pass def buildManifest(title, version, author, mainClass=None): - manifest = { - "Title": title, - "Version": version, - "Created-by": author - } - if mainClass is not None: - manifest["Main-Class"] = mainClass - return manifest + manifest = { + "Title": title, + "Version": version, + "Created-by": author + } + if mainClass is not None: + manifest["Main-Class"] = mainClass + return manifest def jar(pathOut, dirIn, dirRoot=None, manifest=None): - # build the base args - if dirRoot is None: - dirRoot = dirIn - dirIn = "." - invokeArgs = ["jar"] - filesArgs = ["-C", dirRoot, dirIn] - - if manifest is not None: - # make a temp file for the manifest - tempFile, tempFilename = tempfile.mkstemp(text=True) - try: - # write the manifest - for (key, value) in manifest.iteritems(): - os.write(tempFile, "%s: %s\n" % (key, value)) - os.close(tempFile) - - # build the jar with a manifest - subprocess.call(invokeArgs + ["cmf", tempFilename, pathOut] + filesArgs) - - finally: - os.remove(tempFilename) - else: - # just build the jar without a manifest - subprocess.call(invokeArgs + ["cf", pathOut] + filesArgs) - - print "Wrote jar: %s" % pathOut + # build the base args + if dirRoot is None: + dirRoot = dirIn + dirIn = "." + invokeArgs = ["jar"] + filesArgs = ["-C", dirRoot, dirIn] + + if manifest is not None: + # make a temp file for the manifest + tempFile, tempFilename = tempfile.mkstemp(text=True) + try: + # write the manifest + for (key, value) in manifest.iteritems(): + os.write(tempFile, "%s: %s\n" % (key, value)) + os.close(tempFile) + + # build the jar with a manifest + subprocess.call(invokeArgs + ["cmf", tempFilename, pathOut] + filesArgs) + + finally: + os.remove(tempFilename) + else: + # just build the jar without a manifest + subprocess.call(invokeArgs + ["cf", pathOut] + filesArgs) + + print "Wrote jar: %s" % pathOut def unpackJar(dirOut, pathJar): - with zipfile.ZipFile(pathJar) as zf: - for member in zf.infolist(): - zf.extract(member, dirOut) - print "Unpacked jar: %s" % pathJar + with zipfile.ZipFile(pathJar) as zf: + for member in zf.infolist(): + zf.extract(member, dirOut) + print "Unpacked jar: %s" % pathJar def unpackJars(dirOut, dirJars, recursive=False): - for name in os.listdir(dirJars): - path = os.path.join(dirJars, name) - if os.path.isfile(path): - if name[-4:] == ".jar": - unpackJar(dirOut, path) - elif os.path.isdir(path) and recursive: - unpackJars(dirOut, path, recursive) + for name in os.listdir(dirJars): + path = os.path.join(dirJars, name) + if os.path.isfile(path): + if name[-4:] == ".jar": + unpackJar(dirOut, path) + elif os.path.isdir(path) and recursive: + unpackJars(dirOut, path, recursive) + +def callJava(classpath, className, javaArgs): + subprocess.call(["java", "-cp", classpath, className] + javaArgs) + +def callJavaJar(jar, javaArgs): + subprocess.call(["java", "-jar", jar] + javaArgs) diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java index 45d27c40..6ac4c786 100644 --- a/test/cuchaz/enigma/TestDeobfuscator.java +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -26,7 +26,7 @@ import cuchaz.enigma.mapping.ClassEntry; public class TestDeobfuscator { private Deobfuscator getDeobfuscator() throws IOException { - return new Deobfuscator(new File("build/libs/testLoneClass.obf.jar")); + return new Deobfuscator(new File("build/testLoneClass.obf.jar")); } @Test diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java index c84d755e..51fb5e35 100644 --- a/test/cuchaz/enigma/TestInnerClasses.java +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -35,7 +35,7 @@ public class TestInnerClasses { public TestInnerClasses() throws Exception { m_index = new JarIndex(); - m_index.indexJar(new JarFile("build/libs/testInnerClasses.obf.jar"), true); + m_index.indexJar(new JarFile("build/testInnerClasses.obf.jar"), true); } @Test diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java index b5f4c7f3..8e3ad6d2 100644 --- a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -37,7 +37,7 @@ public class TestJarIndexConstructorReferences { private ClassEntry m_callerClass = new ClassEntry("none/b"); public TestJarIndexConstructorReferences() throws Exception { - File jarFile = new File("build/libs/testConstructors.obf.jar"); + File jarFile = new File("build/testConstructors.obf.jar"); m_index = new JarIndex(); m_index.indexJar(new JarFile(jarFile), false); } diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index caf6578d..4d663972 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -43,7 +43,7 @@ public class TestJarIndexInheritanceTree { public TestJarIndexInheritanceTree() throws Exception { m_index = new JarIndex(); - m_index.indexJar(new JarFile("build/libs/testInheritanceTree.obf.jar"), false); + m_index.indexJar(new JarFile("build/testInheritanceTree.obf.jar"), false); } @Test @@ -63,27 +63,27 @@ public class TestJarIndexInheritanceTree { TranslationIndex index = m_index.getTranslationIndex(); // base class - assertThat(index.getSuperclassName(m_baseClass.getName()), is(nullValue())); - assertThat(index.getAncestry(m_baseClass.getName()), is(empty())); - assertThat(index.getSubclassNames(m_baseClass.getName()), containsInAnyOrder( - m_subClassA.getName(), - m_subClassB.getName() + assertThat(index.getSuperclass(m_baseClass), is(nullValue())); + assertThat(index.getAncestry(m_baseClass), is(empty())); + assertThat(index.getSubclass(m_baseClass), containsInAnyOrder( + m_subClassA, + m_subClassB )); // subclass a - assertThat(index.getSuperclassName(m_subClassA.getName()), is(m_baseClass.getName())); - assertThat(index.getAncestry(m_subClassA.getName()), contains(m_baseClass.getName())); - assertThat(index.getSubclassNames(m_subClassA.getName()), contains(m_subClassAA.getName())); + assertThat(index.getSuperclass(m_subClassA), is(m_baseClass)); + assertThat(index.getAncestry(m_subClassA), contains(m_baseClass)); + assertThat(index.getSubclass(m_subClassA), contains(m_subClassAA)); // subclass aa - assertThat(index.getSuperclassName(m_subClassAA.getName()), is(m_subClassA.getName())); - assertThat(index.getAncestry(m_subClassAA.getName()), contains(m_subClassA.getName(), m_baseClass.getName())); - assertThat(index.getSubclassNames(m_subClassAA.getName()), is(empty())); + assertThat(index.getSuperclass(m_subClassAA), is(m_subClassA)); + assertThat(index.getAncestry(m_subClassAA), contains(m_subClassA, m_baseClass)); + assertThat(index.getSubclass(m_subClassAA), is(empty())); // subclass b - assertThat(index.getSuperclassName(m_subClassB.getName()), is(m_baseClass.getName())); - assertThat(index.getAncestry(m_subClassB.getName()), contains(m_baseClass.getName())); - assertThat(index.getSubclassNames(m_subClassB.getName()), is(empty())); + assertThat(index.getSuperclass(m_subClassB), is(m_baseClass)); + assertThat(index.getAncestry(m_subClassB), contains(m_baseClass)); + assertThat(index.getSubclass(m_subClassB), is(empty())); } @Test diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index 0575eec3..a061b72d 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -29,6 +29,7 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.analysis.MethodImplementationsTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; @@ -39,7 +40,7 @@ public class TestJarIndexLoneClass { public TestJarIndexLoneClass() throws Exception { m_index = new JarIndex(); - m_index.indexJar(new JarFile("build/libs/testLoneClass.obf.jar"), false); + m_index.indexJar(new JarFile("build/testLoneClass.obf.jar"), false); } @Test @@ -52,12 +53,12 @@ public class TestJarIndexLoneClass { @Test public void translationIndex() { - assertThat(m_index.getTranslationIndex().getSuperclassName("none/a"), is(nullValue())); - assertThat(m_index.getTranslationIndex().getSuperclassName("cuchaz/enigma/inputs/Keep"), is(nullValue())); - assertThat(m_index.getTranslationIndex().getAncestry("none/a"), is(empty())); - assertThat(m_index.getTranslationIndex().getAncestry("cuchaz/enigma/inputs/Keep"), is(empty())); - assertThat(m_index.getTranslationIndex().getSubclassNames("none/a"), is(empty())); - assertThat(m_index.getTranslationIndex().getSubclassNames("cuchaz/enigma/inputs/Keep"), is(empty())); + assertThat(m_index.getTranslationIndex().getSuperclass(new ClassEntry("none/a")), is(nullValue())); + assertThat(m_index.getTranslationIndex().getSuperclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(nullValue())); + assertThat(m_index.getTranslationIndex().getAncestry(new ClassEntry("none/a")), is(empty())); + assertThat(m_index.getTranslationIndex().getAncestry(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty())); + assertThat(m_index.getTranslationIndex().getSubclass(new ClassEntry("none/a")), is(empty())); + assertThat(m_index.getTranslationIndex().getSubclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty())); } @Test diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java index fb385e06..ba7fc797 100644 --- a/test/cuchaz/enigma/TestSourceIndex.java +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -23,6 +23,7 @@ import cuchaz.enigma.mapping.ClassEntry; public class TestSourceIndex { + // TEMP @Test public void indexEverything() throws Exception { Deobfuscator deobfuscator = new Deobfuscator(new File("input/1.8.jar")); diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java index f805a655..9f85e81d 100644 --- a/test/cuchaz/enigma/TestTokensConstructors.java +++ b/test/cuchaz/enigma/TestTokensConstructors.java @@ -27,7 +27,7 @@ import cuchaz.enigma.mapping.BehaviorEntry; public class TestTokensConstructors extends TokenChecker { public TestTokensConstructors() throws Exception { - super(new File("build/libs/testConstructors.obf.jar")); + super(new File("build/testConstructors.obf.jar")); } @Test -- cgit v1.2.3 From c5b7e0ffd03259660221020250bb80cc4006c01e Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 19 Jan 2015 23:14:30 -0500 Subject: fixed M3L-related issues with translation index --- src/cuchaz/enigma/analysis/TranslationIndex.java | 36 ++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index 4a356eb6..7597c3ae 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -10,10 +10,18 @@ ******************************************************************************/ package cuchaz.enigma.analysis; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; import java.io.Serializable; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; import javassist.CtBehavior; import javassist.CtClass; @@ -81,7 +89,7 @@ public class TranslationIndex implements Serializable { // add the superclass ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c); - if (!isJre(classEntry) && !isJre(superclassEntry)) { + if (!isJre(classEntry) && superclassEntry != null && !isJre(superclassEntry)) { m_superclasses.put(classEntry, superclassEntry); } @@ -190,6 +198,30 @@ public class TranslationIndex implements Serializable { } private boolean isJre(ClassEntry classEntry) { - return classEntry.getPackageName().startsWith("java") || classEntry.getPackageName().startsWith("javax"); + String packageName = classEntry.getPackageName(); + return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); + } + + public void write(OutputStream out) + throws IOException { + GZIPOutputStream gzipout = new GZIPOutputStream(out); + ObjectOutputStream oout = new ObjectOutputStream(gzipout); + oout.writeObject(m_superclasses); + oout.writeObject(m_fieldEntries); + oout.writeObject(m_behaviorEntries); + gzipout.finish(); + } + + @SuppressWarnings("unchecked") + public void read(InputStream in) + throws IOException { + try { + ObjectInputStream oin = new ObjectInputStream(new GZIPInputStream(in)); + m_superclasses = (HashMap)oin.readObject(); + m_fieldEntries = (HashMultimap)oin.readObject(); + m_behaviorEntries = (HashMultimap)oin.readObject(); + } catch (ClassNotFoundException ex) { + throw new Error(ex); + } } } -- cgit v1.2.3 From c349556a15c1bbd1d714616e604491a02a8ecf79 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 19 Jan 2015 23:16:55 -0500 Subject: remove debug code, silly... --- src/cuchaz/enigma/bytecode/ClassTranslator.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 64418307..fb2fb27f 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -56,11 +56,6 @@ public class ClassTranslator { ); FieldEntry translatedEntry = m_translator.translateEntry(entry); - // TEMP - if (entry.toString().equals("none/bxq.m")) { - System.out.println("FIELD: " + entry + " -> " + translatedEntry); - } - // translate the type String type = constants.getFieldrefType(i); String translatedType = m_translator.translateSignature(type); -- cgit v1.2.3 From 74781ff8002741ec83ca1c44f9a125b7498f8f31 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 20 Jan 2015 23:17:03 -0500 Subject: update ignore list so we save the Eclipse build settings --- .classpath | 10 ++++++++++ .hgignore | 21 +++++++-------------- .project | 18 ++++++++++++++++++ .settings/org.apache.ivyde.eclipse.prefs | 2 ++ .settings/org.eclipse.jdt.core.prefs | 13 +++++++++++++ 5 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 .classpath create mode 100644 .project create mode 100644 .settings/org.apache.ivyde.eclipse.prefs create mode 100644 .settings/org.eclipse.jdt.core.prefs diff --git a/.classpath b/.classpath new file mode 100644 index 00000000..a42f7a1c --- /dev/null +++ b/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.hgignore b/.hgignore index 9ff982e6..aa6a93f2 100644 --- a/.hgignore +++ b/.hgignore @@ -1,15 +1,8 @@ -syntax: regexp -^\. -syntax: regexp -^bin$ -syntax: regexp -^lib$ -syntax: regexp -^build$ -syntax: regexp -^data$ -syntax: regexp -^input$ -syntax: regexp -^ivy$ \ No newline at end of file +syntax: glob +bin +lib +build +data +input +ivy diff --git a/.project b/.project new file mode 100644 index 00000000..53500740 --- /dev/null +++ b/.project @@ -0,0 +1,18 @@ + + + Enigma + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + org.apache.ivyde.eclipse.ivynature + + diff --git a/.settings/org.apache.ivyde.eclipse.prefs b/.settings/org.apache.ivyde.eclipse.prefs new file mode 100644 index 00000000..eaa94818 --- /dev/null +++ b/.settings/org.apache.ivyde.eclipse.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.apache.ivyde.eclipse.standaloneretrieve= diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..b5d234f2 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,13 @@ +# +#Mon Sep 22 22:51:23 EDT 2014 +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -- cgit v1.2.3 From adfe7f87ffb77fd8285af56140480c242d324de8 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 21 Jan 2015 00:16:31 -0500 Subject: update build system to make maven artifacts --- .hgignore | 1 + build.py | 96 ++++++++++++++++++++++++---------------------------- readme.translate.txt | 12 ------- ssjb.py | 17 +++++++++- 4 files changed, 61 insertions(+), 65 deletions(-) delete mode 100644 readme.translate.txt diff --git a/.hgignore b/.hgignore index aa6a93f2..659df811 100644 --- a/.hgignore +++ b/.hgignore @@ -6,3 +6,4 @@ build data input ivy +*.pyc \ No newline at end of file diff --git a/build.py b/build.py index b9d1121f..2868b750 100644 --- a/build.py +++ b/build.py @@ -3,67 +3,61 @@ import os import ssjb # settings -projectName = "enigma" -version = "0.6b" -author = "Cuchaz" +GroupId = "cuchaz" +ProjectName = "enigma" +Version = "0.6b" +Author = "Cuchaz" -dirBin = "bin" -dirBuild = "build" -dirTemp = os.path.join(dirBuild, "tmp") +DirBin = "bin" +DirBuild = "build" +DirTemp = os.path.join(DirBuild, "tmp") +PathLocalMavenRepo = "../maven" def getJarFullName(name=None) : if name is not None: - return "%s-%s-%s.jar" % (projectName, name, version) + return "%s-%s-%s.jar" % (ProjectName, name, Version) else: - return "%s-%s.jar" % (projectName, version) - -def buildGuiJar(): - jarName = "gui" - os.makedirs(dirTemp) - ssjb.copyFiles(dirTemp, dirBin, ssjb.findFiles(dirBin)) - ssjb.unpackJars(dirTemp, "ivy/bundles", recursive=True) - ssjb.unpackJars(dirTemp, "ivy/jars", recursive=True) - ssjb.unpackJars(dirTemp, "libs", recursive=True) - ssjb.delete(os.path.join(dirTemp, "LICENSE.txt")) - ssjb.copyFile(dirTemp, "license.APL2.txt") - ssjb.copyFile(dirTemp, "license.GPL3.txt") - ssjb.copyFile(dirTemp, "readme.txt") - manifest = ssjb.buildManifest(projectName, version, author, "cuchaz.enigma.Main") - ssjb.jar(os.path.join(dirBuild, getJarFullName()), dirTemp, manifest=manifest) - ssjb.delete(dirTemp) - -def buildTranslateJar(): - jarName = "translate" - os.makedirs(dirTemp) - files = ssjb.findFiles(dirBin, "cuchaz/enigma/mapping/*") - files += ssjb.findFiles(dirBin, "cuchaz/enigma/bytecode/*") - ssjb.copyFiles(dirTemp, dirBin, files) - ssjb.copyFile(dirTemp, "license.GPL3.txt", renameTo="license.txt") - ssjb.copyFile(dirTemp, "readme.translate.txt", renameTo="readme.txt") - manifest = ssjb.buildManifest("%s-%s" % (projectName, jarName), version, author) - ssjb.jar(os.path.join(dirBuild, getJarFullName(jarName)), dirTemp, manifest=manifest) - ssjb.delete(dirTemp) + return "%s-%s.jar" % (ProjectName, Version) + +def buildJar(): + os.makedirs(DirTemp) + ssjb.copyFiles(DirTemp, DirBin, ssjb.findFiles(DirBin)) + # TODO: teach ssjb where to find ivy jars + ssjb.unpackJars(DirTemp, "ivy/bundles", recursive=True) + ssjb.unpackJars(DirTemp, "ivy/jars", recursive=True) + ssjb.unpackJars(DirTemp, "libs", recursive=True) + ssjb.delete(os.path.join(DirTemp, "LICENSE.txt")) + ssjb.delete(os.path.join(DirTemp, "META-INF/maven")) + ssjb.copyFile(DirTemp, "license.APL2.txt") + ssjb.copyFile(DirTemp, "license.GPL3.txt") + ssjb.copyFile(DirTemp, "readme.txt") + manifest = ssjb.buildManifest(ProjectName, Version, Author, "cuchaz.enigma.Main") + ssjb.jar(os.path.join(DirBuild, getJarFullName()), DirTemp, manifest=manifest) + ssjb.delete(DirTemp) + ssjb.deployJarToLocalMavenRepo( + PathLocalMavenRepo, + getJarFullName(), + "%s:%s:%s" % (GroupId, ProjectName, Version) + ) def taskMain(): - ssjb.delete(dirBuild) - os.makedirs(dirBuild) - buildGuiJar() - buildTranslateJar() - + ssjb.delete(DirBuild) + os.makedirs(DirBuild) + buildJar() def makeTestJar(name, glob): - pathJar = os.path.join(dirBuild, "%s.jar" % name) - pathObfJar = os.path.join(dirBuild, "%s.obf.jar" % name) + pathJar = os.path.join(DirBuild, "%s.jar" % name) + pathObfJar = os.path.join(DirBuild, "%s.obf.jar" % name) # build the deobf jar - ssjb.delete(dirTemp) - os.makedirs(dirTemp) - ssjb.copyFiles(dirTemp, dirBin, ssjb.findFiles(dirBin, "cuchaz/enigma/inputs/Keep.class")) - ssjb.copyFiles(dirTemp, dirBin, ssjb.findFiles(dirBin, glob)) - ssjb.jar(pathJar, dirTemp) - ssjb.delete(dirTemp) + ssjb.delete(DirTemp) + os.makedirs(DirTemp) + ssjb.copyFiles(DirTemp, DirBin, ssjb.findFiles(DirBin, "cuchaz/enigma/inputs/Keep.class")) + ssjb.copyFiles(DirTemp, DirBin, ssjb.findFiles(DirBin, glob)) + ssjb.jar(pathJar, DirTemp) + ssjb.delete(DirTemp) # build the obf jar ssjb.callJavaJar("libs/proguard.jar", ["@proguard.conf", "-injars", pathJar, "-outjars", pathObfJar]) @@ -74,10 +68,8 @@ def taskBuildTestJars(): makeTestJar("testInheritanceTree", "cuchaz/enigma/inputs/inheritanceTree/*.class") makeTestJar("testInnerClasses", "cuchaz/enigma/inputs/innerClasses/*.class") + ssjb.registerTask("main", taskMain) ssjb.registerTask("buildTestJars", taskBuildTestJars) - - -if __name__ == "__main__": - ssjb.run() +ssjb.run() diff --git a/readme.translate.txt b/readme.translate.txt deleted file mode 100644 index 455d16ed..00000000 --- a/readme.translate.txt +++ /dev/null @@ -1,12 +0,0 @@ - -Enigma v0.6 beta -A tool for deobfuscation of Java bytecode - -Copyright Jeff Martin, 2014 - - -LICENSE - -Enigma is distributed under the GNU General Public license version 3 - -A copy of the GNU General Public license verion 3 has been included in this distribution. diff --git a/ssjb.py b/ssjb.py index 7f051cc5..fa98c823 100644 --- a/ssjb.py +++ b/ssjb.py @@ -1,7 +1,6 @@ # stupidly simple jar builder # Jeff Martin -# 2015-01-05 import os import sys @@ -168,3 +167,19 @@ def callJava(classpath, className, javaArgs): def callJavaJar(jar, javaArgs): subprocess.call(["java", "-jar", jar] + javaArgs) +def deployJarToLocalMavenRepo(pathLocalRepo, pathJar, artifactDesc): + + # parse the artifact description + (groupId, artifactId, version) = artifactDesc.split(":") + + args = ["mvn", "install:install-file", + "-Dmaven.repo.local=%s" % pathLocalRepo, + "-Dfile=%s" % pathJar, + "-Dpackaging=jar", + "-DgroupId=%s" % groupId, + "-DartifactId=%s" % artifactId, + "-Dversion=%s" % version + ] + subprocess.call(args) + print "Deployed Maven artifact %s to %s" % (artifactDesc, pathLocalRepo) + -- cgit v1.2.3 From bec00b76fb2ea06e35bbf37fca376499527b596e Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 21 Jan 2015 00:32:41 -0500 Subject: fix build script to deploy jar correctly --- build.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build.py b/build.py index 2868b750..082577a0 100644 --- a/build.py +++ b/build.py @@ -33,11 +33,12 @@ def buildJar(): ssjb.copyFile(DirTemp, "license.GPL3.txt") ssjb.copyFile(DirTemp, "readme.txt") manifest = ssjb.buildManifest(ProjectName, Version, Author, "cuchaz.enigma.Main") - ssjb.jar(os.path.join(DirBuild, getJarFullName()), DirTemp, manifest=manifest) + pathJar = os.path.join(DirBuild, getJarFullName()) + ssjb.jar(pathJar, DirTemp, manifest=manifest) ssjb.delete(DirTemp) ssjb.deployJarToLocalMavenRepo( PathLocalMavenRepo, - getJarFullName(), + pathJar, "%s:%s:%s" % (GroupId, ProjectName, Version) ) -- cgit v1.2.3 From b647390c9128a9cc7814b7016a02b61def4ddc3e Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 24 Jan 2015 13:13:30 -0500 Subject: remove libs from repo --- libs/procyon-decompiler-0.5.26-enigma.jar | Bin 1757481 -> 0 bytes libs/proguard.jar | Bin 853203 -> 0 bytes ssjb.py | 185 ------------------------------ 3 files changed, 185 deletions(-) delete mode 100644 libs/procyon-decompiler-0.5.26-enigma.jar delete mode 100644 libs/proguard.jar delete mode 100644 ssjb.py diff --git a/libs/procyon-decompiler-0.5.26-enigma.jar b/libs/procyon-decompiler-0.5.26-enigma.jar deleted file mode 100644 index 4a3737ee..00000000 Binary files a/libs/procyon-decompiler-0.5.26-enigma.jar and /dev/null differ diff --git a/libs/proguard.jar b/libs/proguard.jar deleted file mode 100644 index a948c89b..00000000 Binary files a/libs/proguard.jar and /dev/null differ diff --git a/ssjb.py b/ssjb.py deleted file mode 100644 index fa98c823..00000000 --- a/ssjb.py +++ /dev/null @@ -1,185 +0,0 @@ - -# stupidly simple jar builder -# Jeff Martin - -import os -import sys -import shutil -import subprocess -import zipfile -import tempfile -import re -import fnmatch - - -# setup tasks -tasks = {} - -def registerTask(name, func): - tasks[name] = func - -def run(): - - # get the task name - taskName = "main" - if len(sys.argv) > 1: - taskName = sys.argv[1] - - # find that task - try: - task = tasks[taskName] - except: - print "Couldn't find task: %s" % taskName - return - - # run it! - print "Running task: %s" % taskName - task() - - -# set up the default main task -def mainTask(): - print "The main task doesn't do anything by default" - -registerTask("main", mainTask) - - -# library of useful functions - -def findFiles(dirSrc, pattern=None): - out = [] - for root, dirs, files in os.walk(dirSrc): - for file in files: - path = os.path.join(root, file)[len(dirSrc) + 1:] - if pattern is None or fnmatch.fnmatch(path, pattern): - out.append(path) - return out - -def copyFile(dirDest, pathSrc, renameTo=None): - (dirParent, filename) = os.path.split(pathSrc) - if renameTo is None: - renameTo = filename - pathDest = os.path.join(dirDest, renameTo) - shutil.copy2(pathSrc, pathDest) - -def copyFiles(dirDest, dirSrc, paths): - for path in paths: - pathSrc = os.path.join(dirSrc, path) - pathDest = os.path.join(dirDest, path) - dirParent = os.path.dirname(pathDest) - if not os.path.isdir(dirParent): - os.makedirs(dirParent) - shutil.copy2(pathSrc, pathDest) - -def patternStringToRegex(patternString): - - # escape special chars - patternString = re.escape(patternString) - - # process ** and * wildcards - replacements = { - re.escape("**"): ".*", - re.escape("*"): "[^" + os.sep + "]+" - } - def getReplacement(match): - print "matched", match - return "a" - patternString = re.compile("(\\\\\*)+").sub(lambda m: replacements[m.group(0)], patternString) - - return re.compile("^" + patternString + "$") - -def matchesAnyPath(path, patternStrings): - for patternString in patternStrings: - pattern = patternStringToRegex(patternString) - # TEMP - print path, pattern.match(path) is not None - if pattern.match(path) is not None: - return True - return False - -def delete(path): - try: - if os.path.isdir(path): - shutil.rmtree(path) - elif os.path.isfile(path): - os.remove(path) - except: - # don't care if it failed - pass - -def buildManifest(title, version, author, mainClass=None): - manifest = { - "Title": title, - "Version": version, - "Created-by": author - } - if mainClass is not None: - manifest["Main-Class"] = mainClass - return manifest - -def jar(pathOut, dirIn, dirRoot=None, manifest=None): - - # build the base args - if dirRoot is None: - dirRoot = dirIn - dirIn = "." - invokeArgs = ["jar"] - filesArgs = ["-C", dirRoot, dirIn] - - if manifest is not None: - # make a temp file for the manifest - tempFile, tempFilename = tempfile.mkstemp(text=True) - try: - # write the manifest - for (key, value) in manifest.iteritems(): - os.write(tempFile, "%s: %s\n" % (key, value)) - os.close(tempFile) - - # build the jar with a manifest - subprocess.call(invokeArgs + ["cmf", tempFilename, pathOut] + filesArgs) - - finally: - os.remove(tempFilename) - else: - # just build the jar without a manifest - subprocess.call(invokeArgs + ["cf", pathOut] + filesArgs) - - print "Wrote jar: %s" % pathOut - -def unpackJar(dirOut, pathJar): - with zipfile.ZipFile(pathJar) as zf: - for member in zf.infolist(): - zf.extract(member, dirOut) - print "Unpacked jar: %s" % pathJar - -def unpackJars(dirOut, dirJars, recursive=False): - for name in os.listdir(dirJars): - path = os.path.join(dirJars, name) - if os.path.isfile(path): - if name[-4:] == ".jar": - unpackJar(dirOut, path) - elif os.path.isdir(path) and recursive: - unpackJars(dirOut, path, recursive) - -def callJava(classpath, className, javaArgs): - subprocess.call(["java", "-cp", classpath, className] + javaArgs) - -def callJavaJar(jar, javaArgs): - subprocess.call(["java", "-jar", jar] + javaArgs) - -def deployJarToLocalMavenRepo(pathLocalRepo, pathJar, artifactDesc): - - # parse the artifact description - (groupId, artifactId, version) = artifactDesc.split(":") - - args = ["mvn", "install:install-file", - "-Dmaven.repo.local=%s" % pathLocalRepo, - "-Dfile=%s" % pathJar, - "-Dpackaging=jar", - "-DgroupId=%s" % groupId, - "-DartifactId=%s" % artifactId, - "-Dversion=%s" % version - ] - subprocess.call(args) - print "Deployed Maven artifact %s to %s" % (artifactDesc, pathLocalRepo) - -- cgit v1.2.3 From c45dbbe43eaa9ecbfa9d59fc80a19f2aa4adb6d9 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 24 Jan 2015 14:59:02 -0500 Subject: switched to new and improved ssjb ssjb can handle all of the dependencies now --- .classpath | 4 +-- .project | 1 - .settings/org.apache.ivyde.eclipse.prefs | 2 -- build.py | 56 +++++++++++++++++++++++++++----- ivy.xml | 13 -------- 5 files changed, 50 insertions(+), 26 deletions(-) delete mode 100644 .settings/org.apache.ivyde.eclipse.prefs delete mode 100644 ivy.xml diff --git a/.classpath b/.classpath index a42f7a1c..7a9ca3de 100644 --- a/.classpath +++ b/.classpath @@ -4,7 +4,7 @@ - - + + diff --git a/.project b/.project index 53500740..08dff6c0 100644 --- a/.project +++ b/.project @@ -13,6 +13,5 @@ org.eclipse.jdt.core.javanature - org.apache.ivyde.eclipse.ivynature diff --git a/.settings/org.apache.ivyde.eclipse.prefs b/.settings/org.apache.ivyde.eclipse.prefs deleted file mode 100644 index eaa94818..00000000 --- a/.settings/org.apache.ivyde.eclipse.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -org.apache.ivyde.eclipse.standaloneretrieve= diff --git a/build.py b/build.py index 082577a0..aa7c6f84 100644 --- a/build.py +++ b/build.py @@ -1,18 +1,45 @@ import os -import ssjb +import sys # settings +PathSsjb = "../ssjb" GroupId = "cuchaz" ProjectName = "enigma" Version = "0.6b" Author = "Cuchaz" DirBin = "bin" +DirLib = "lib" DirBuild = "build" DirTemp = os.path.join(DirBuild, "tmp") PathLocalMavenRepo = "../maven" +# import ssjb +sys.path.insert(0, PathSsjb) +import ssjb +import ssjb.ivy + + +# dependencies +ExtraRepos = [ + "http://maven.cuchazinteractive.com" +] +Deps = [ + ssjb.ivy.Dep("com.google.guava:guava:17.0"), + ssjb.ivy.Dep("de.sciss:jsyntaxpane:1.0.0"), + ssjb.ivy.Dep("org.javassist:javassist:3.18.1-GA"), + ssjb.ivy.Dep("org.bitbucket.mstrobel:procyon-decompiler:0.5.26-enigma") +] +ProguardDeps = [ + ssjb.ivy.Dep("net.sf.proguard:proguard-base:5.1") +] +TestDeps = [ + ssjb.ivy.Dep("junit:junit:4.12"), + ssjb.ivy.Dep("org.hamcrest:hamcrest-all:1.3") +] + +# functions def getJarFullName(name=None) : if name is not None: @@ -53,15 +80,27 @@ def makeTestJar(name, glob): pathObfJar = os.path.join(DirBuild, "%s.obf.jar" % name) # build the deobf jar - ssjb.delete(DirTemp) - os.makedirs(DirTemp) - ssjb.copyFiles(DirTemp, DirBin, ssjb.findFiles(DirBin, "cuchaz/enigma/inputs/Keep.class")) - ssjb.copyFiles(DirTemp, DirBin, ssjb.findFiles(DirBin, glob)) - ssjb.jar(pathJar, DirTemp) - ssjb.delete(DirTemp) + ssjb.file.delete(DirTemp) + ssjb.file.mkdir(DirTemp) + ssjb.file.copyTree(DirTemp, DirBin, ssjb.file.find(DirBin, "cuchaz/enigma/inputs/Keep.class")) + ssjb.file.copyTree(DirTemp, DirBin, ssjb.file.find(DirBin, glob)) + ssjb.jar.makeJar(pathJar, DirTemp) + ssjb.file.delete(DirTemp) # build the obf jar - ssjb.callJavaJar("libs/proguard.jar", ["@proguard.conf", "-injars", pathJar, "-outjars", pathObfJar]) + ssjb.callJavaJar( + os.path.join(DirLib, "proguard.jar"), + ["@proguard.conf", "-injars", pathJar, "-outjars", pathObfJar] + ) + + +# tasks + +def taskGetDeps(): + ssjb.file.mkdir(DirLib) + ssjb.ivy.makeLibsJar(os.path.join(DirLib, "deps.jar"), Deps, extraRepos=ExtraRepos) + ssjb.ivy.makeLibsJar(os.path.join(DirLib, "test-deps.jar"), TestDeps) + ssjb.ivy.makeJar(os.path.join(DirLib, "proguard.jar"), ProguardDeps) def taskBuildTestJars(): makeTestJar("testLoneClass", "cuchaz/enigma/inputs/loneClass/*.class") @@ -70,6 +109,7 @@ def taskBuildTestJars(): makeTestJar("testInnerClasses", "cuchaz/enigma/inputs/innerClasses/*.class") +ssjb.registerTask("getDeps", taskGetDeps) ssjb.registerTask("main", taskMain) ssjb.registerTask("buildTestJars", taskBuildTestJars) ssjb.run() diff --git a/ivy.xml b/ivy.xml deleted file mode 100644 index aa9d1f84..00000000 --- a/ivy.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - -- cgit v1.2.3 From c8b77b7444cd6ebd30f34ec31e32447d12b94b9c Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 24 Jan 2015 15:31:59 -0500 Subject: finish build script --- build.py | 98 +++++++++++++++++++++++++++++----------------------------------- 1 file changed, 45 insertions(+), 53 deletions(-) diff --git a/build.py b/build.py index aa7c6f84..1349d1db 100644 --- a/build.py +++ b/build.py @@ -4,9 +4,6 @@ import sys # settings PathSsjb = "../ssjb" -GroupId = "cuchaz" -ProjectName = "enigma" -Version = "0.6b" Author = "Cuchaz" DirBin = "bin" @@ -15,12 +12,15 @@ DirBuild = "build" DirTemp = os.path.join(DirBuild, "tmp") PathLocalMavenRepo = "../maven" + # import ssjb sys.path.insert(0, PathSsjb) import ssjb import ssjb.ivy +ThisArtifact = ssjb.ivy.Dep("cuchaz:enigma:0.6b") + # dependencies ExtraRepos = [ "http://maven.cuchazinteractive.com" @@ -31,9 +31,7 @@ Deps = [ ssjb.ivy.Dep("org.javassist:javassist:3.18.1-GA"), ssjb.ivy.Dep("org.bitbucket.mstrobel:procyon-decompiler:0.5.26-enigma") ] -ProguardDeps = [ - ssjb.ivy.Dep("net.sf.proguard:proguard-base:5.1") -] +ProguardDep = ssjb.ivy.Dep("net.sf.proguard:proguard-base:5.1") TestDeps = [ ssjb.ivy.Dep("junit:junit:4.12"), ssjb.ivy.Dep("org.hamcrest:hamcrest-all:1.3") @@ -41,51 +39,16 @@ TestDeps = [ # functions -def getJarFullName(name=None) : - if name is not None: - return "%s-%s-%s.jar" % (ProjectName, name, Version) - else: - return "%s-%s.jar" % (ProjectName, Version) - -def buildJar(): - os.makedirs(DirTemp) - ssjb.copyFiles(DirTemp, DirBin, ssjb.findFiles(DirBin)) - # TODO: teach ssjb where to find ivy jars - ssjb.unpackJars(DirTemp, "ivy/bundles", recursive=True) - ssjb.unpackJars(DirTemp, "ivy/jars", recursive=True) - ssjb.unpackJars(DirTemp, "libs", recursive=True) - ssjb.delete(os.path.join(DirTemp, "LICENSE.txt")) - ssjb.delete(os.path.join(DirTemp, "META-INF/maven")) - ssjb.copyFile(DirTemp, "license.APL2.txt") - ssjb.copyFile(DirTemp, "license.GPL3.txt") - ssjb.copyFile(DirTemp, "readme.txt") - manifest = ssjb.buildManifest(ProjectName, Version, Author, "cuchaz.enigma.Main") - pathJar = os.path.join(DirBuild, getJarFullName()) - ssjb.jar(pathJar, DirTemp, manifest=manifest) - ssjb.delete(DirTemp) - ssjb.deployJarToLocalMavenRepo( - PathLocalMavenRepo, - pathJar, - "%s:%s:%s" % (GroupId, ProjectName, Version) - ) - -def taskMain(): - ssjb.delete(DirBuild) - os.makedirs(DirBuild) - buildJar() - -def makeTestJar(name, glob): +def buildTestJar(name, glob): pathJar = os.path.join(DirBuild, "%s.jar" % name) pathObfJar = os.path.join(DirBuild, "%s.obf.jar" % name) # build the deobf jar - ssjb.file.delete(DirTemp) - ssjb.file.mkdir(DirTemp) - ssjb.file.copyTree(DirTemp, DirBin, ssjb.file.find(DirBin, "cuchaz/enigma/inputs/Keep.class")) - ssjb.file.copyTree(DirTemp, DirBin, ssjb.file.find(DirBin, glob)) - ssjb.jar.makeJar(pathJar, DirTemp) - ssjb.file.delete(DirTemp) + with ssjb.file.TempDir(DirTemp) as dirTemp: + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin, "cuchaz/enigma/inputs/Keep.class")) + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin, glob)) + ssjb.jar.makeJar(pathJar, dirTemp) # build the obf jar ssjb.callJavaJar( @@ -100,17 +63,46 @@ def taskGetDeps(): ssjb.file.mkdir(DirLib) ssjb.ivy.makeLibsJar(os.path.join(DirLib, "deps.jar"), Deps, extraRepos=ExtraRepos) ssjb.ivy.makeLibsJar(os.path.join(DirLib, "test-deps.jar"), TestDeps) - ssjb.ivy.makeJar(os.path.join(DirLib, "proguard.jar"), ProguardDeps) + ssjb.ivy.makeJar(os.path.join(DirLib, "proguard.jar"), ProguardDep) def taskBuildTestJars(): - makeTestJar("testLoneClass", "cuchaz/enigma/inputs/loneClass/*.class") - makeTestJar("testConstructors", "cuchaz/enigma/inputs/constructors/*.class") - makeTestJar("testInheritanceTree", "cuchaz/enigma/inputs/inheritanceTree/*.class") - makeTestJar("testInnerClasses", "cuchaz/enigma/inputs/innerClasses/*.class") - + buildTestJar("testLoneClass", "cuchaz/enigma/inputs/loneClass/*.class") + buildTestJar("testConstructors", "cuchaz/enigma/inputs/constructors/*.class") + buildTestJar("testInheritanceTree", "cuchaz/enigma/inputs/inheritanceTree/*.class") + buildTestJar("testInnerClasses", "cuchaz/enigma/inputs/innerClasses/*.class") + +def taskBuild(): + + # make the build directory + ssjb.file.delete(DirBuild) + ssjb.file.mkdir(DirBuild) + + # make the main jar + with ssjb.file.TempDir(DirTemp) as dirTemp: + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin)) + ssjb.jar.unpackJar(dirTemp, os.path.join(DirLib, "deps.jar")) + ssjb.file.delete(os.path.join(dirTemp, "LICENSE.txt")) + ssjb.file.delete(os.path.join(dirTemp, "META-INF/maven")) + ssjb.file.copy(dirTemp, "license.APL2.txt") + ssjb.file.copy(dirTemp, "license.GPL3.txt") + ssjb.file.copy(dirTemp, "readme.txt") + manifest = ssjb.jar.buildManifest( + ThisArtifact.artifactId, + ThisArtifact.version, + Author, + "cuchaz.enigma.Main" + ) + pathJar = os.path.join(DirBuild, "%s.jar" % ThisArtifact.getName()) + ssjb.jar.makeJar(pathJar, dirTemp, manifest=manifest) + + ssjb.ivy.deployJarToLocalMavenRepo( + PathLocalMavenRepo, + pathJar, + ThisArtifact + ) ssjb.registerTask("getDeps", taskGetDeps) -ssjb.registerTask("main", taskMain) ssjb.registerTask("buildTestJars", taskBuildTestJars) +ssjb.registerTask("build", taskBuild) ssjb.run() -- cgit v1.2.3 From 1db85d556d36567bd65df59d9877387b892c2961 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 24 Jan 2015 15:51:36 -0500 Subject: don't deploy sources/javadoc of libs --- build.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.py b/build.py index 1349d1db..cc77c431 100644 --- a/build.py +++ b/build.py @@ -80,7 +80,8 @@ def taskBuild(): # make the main jar with ssjb.file.TempDir(DirTemp) as dirTemp: ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin)) - ssjb.jar.unpackJar(dirTemp, os.path.join(DirLib, "deps.jar")) + for path in ssjb.ivy.getJarPaths(Deps, ExtraRepos): + ssjb.jar.unpackJar(dirTemp, path) ssjb.file.delete(os.path.join(dirTemp, "LICENSE.txt")) ssjb.file.delete(os.path.join(dirTemp, "META-INF/maven")) ssjb.file.copy(dirTemp, "license.APL2.txt") -- cgit v1.2.3 From f870848996c5ca1191b62ab2ca4555e8ad5c2e29 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 24 Jan 2015 18:18:24 -0500 Subject: split enigma artifacts into standalone and library jars --- build.py | 72 ++++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/build.py b/build.py index cc77c431..09718cb2 100644 --- a/build.py +++ b/build.py @@ -9,7 +9,6 @@ Author = "Cuchaz" DirBin = "bin" DirLib = "lib" DirBuild = "build" -DirTemp = os.path.join(DirBuild, "tmp") PathLocalMavenRepo = "../maven" @@ -19,18 +18,21 @@ import ssjb import ssjb.ivy -ThisArtifact = ssjb.ivy.Dep("cuchaz:enigma:0.6b") +ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.6b") +ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.6b") # dependencies ExtraRepos = [ "http://maven.cuchazinteractive.com" ] -Deps = [ +LibDeps = [ ssjb.ivy.Dep("com.google.guava:guava:17.0"), - ssjb.ivy.Dep("de.sciss:jsyntaxpane:1.0.0"), ssjb.ivy.Dep("org.javassist:javassist:3.18.1-GA"), ssjb.ivy.Dep("org.bitbucket.mstrobel:procyon-decompiler:0.5.26-enigma") ] +StandaloneDeps = LibDeps + [ + ssjb.ivy.Dep("de.sciss:jsyntaxpane:1.0.0") +] ProguardDep = ssjb.ivy.Dep("net.sf.proguard:proguard-base:5.1") TestDeps = [ ssjb.ivy.Dep("junit:junit:4.12"), @@ -57,6 +59,39 @@ def buildTestJar(name, glob): ) +def applyReadme(dirTemp): + ssjb.file.copy(dirTemp, "license.APL2.txt") + ssjb.file.copy(dirTemp, "license.GPL3.txt") + ssjb.file.copy(dirTemp, "readme.txt") + + +def buildStandaloneJar(dirOut): + with ssjb.file.TempDir(os.path.join(dirOut, "tmp")) as dirTemp: + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin)) + for path in ssjb.ivy.getJarPaths(StandaloneDeps, ExtraRepos): + ssjb.jar.unpackJar(dirTemp, path) + ssjb.file.delete(os.path.join(dirTemp, "LICENSE.txt")) + ssjb.file.delete(os.path.join(dirTemp, "META-INF/maven")) + applyReadme(dirTemp) + manifest = ssjb.jar.buildManifest( + ArtifactStandalone.artifactId, + ArtifactStandalone.version, + Author, + "cuchaz.enigma.Main" + ) + pathJar = os.path.join(DirBuild, "%s.jar" % ArtifactStandalone.getName()) + ssjb.jar.makeJar(pathJar, dirTemp, manifest=manifest) + ssjb.ivy.deployJarToLocalMavenRepo(PathLocalMavenRepo, pathJar, ArtifactStandalone) + +def buildLibJar(dirOut): + with ssjb.file.TempDir(os.path.join(dirOut, "tmp")) as dirTemp: + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin)) + applyReadme(dirTemp) + pathJar = os.path.join(DirBuild, "%s.jar" % ArtifactLib.getName()) + ssjb.jar.makeJar(pathJar, dirTemp) + ssjb.ivy.deployJarToLocalMavenRepo(PathLocalMavenRepo, pathJar, ArtifactLib, deps=LibDeps) + + # tasks def taskGetDeps(): @@ -72,35 +107,10 @@ def taskBuildTestJars(): buildTestJar("testInnerClasses", "cuchaz/enigma/inputs/innerClasses/*.class") def taskBuild(): - - # make the build directory ssjb.file.delete(DirBuild) ssjb.file.mkdir(DirBuild) - - # make the main jar - with ssjb.file.TempDir(DirTemp) as dirTemp: - ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin)) - for path in ssjb.ivy.getJarPaths(Deps, ExtraRepos): - ssjb.jar.unpackJar(dirTemp, path) - ssjb.file.delete(os.path.join(dirTemp, "LICENSE.txt")) - ssjb.file.delete(os.path.join(dirTemp, "META-INF/maven")) - ssjb.file.copy(dirTemp, "license.APL2.txt") - ssjb.file.copy(dirTemp, "license.GPL3.txt") - ssjb.file.copy(dirTemp, "readme.txt") - manifest = ssjb.jar.buildManifest( - ThisArtifact.artifactId, - ThisArtifact.version, - Author, - "cuchaz.enigma.Main" - ) - pathJar = os.path.join(DirBuild, "%s.jar" % ThisArtifact.getName()) - ssjb.jar.makeJar(pathJar, dirTemp, manifest=manifest) - - ssjb.ivy.deployJarToLocalMavenRepo( - PathLocalMavenRepo, - pathJar, - ThisArtifact - ) + buildStandaloneJar(DirBuild) + buildLibJar(DirBuild) ssjb.registerTask("getDeps", taskGetDeps) ssjb.registerTask("buildTestJars", taskBuildTestJars) -- cgit v1.2.3 From e66c456a9595d33c1db0d26e78347d4e0018b77e Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 24 Jan 2015 20:20:39 -0500 Subject: avoid concurrent modification exception --- src/cuchaz/enigma/Deobfuscator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 23057223..22c517a1 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -138,7 +138,7 @@ public class Deobfuscator { // pass 2: look for fields/methods that are actually declared in superclasses MappingsRenamer renamer = new MappingsRenamer(m_jarIndex, val); - for (ClassMapping classMapping : val.classes()) { + for (ClassMapping classMapping : Lists.newArrayList(val.classes())) { ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfName()); // fields -- cgit v1.2.3 From 448685653e90415ebe10b08e8335462b81c30421 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 2 Feb 2015 21:26:10 -0500 Subject: fix issue with bridge methods --- .classpath | 1 + build.py | 6 +- src/cuchaz/enigma/CommandMain.java | 9 +-- src/cuchaz/enigma/Deobfuscator.java | 8 +-- src/cuchaz/enigma/TranslatingTypeLoader.java | 4 +- src/cuchaz/enigma/analysis/BridgeFixer.java | 72 ---------------------- src/cuchaz/enigma/gui/Gui.java | 3 +- src/cuchaz/enigma/gui/GuiController.java | 5 +- test/cuchaz/enigma/TestDeobfuscator.java | 4 +- test/cuchaz/enigma/TestInnerClasses.java | 23 ++++++- test/cuchaz/enigma/TestSourceIndex.java | 4 +- test/cuchaz/enigma/TestTokensConstructors.java | 18 +++--- test/cuchaz/enigma/TokenChecker.java | 4 +- .../enigma/inputs/innerClasses/A_Anonymous.java | 14 +++++ .../enigma/inputs/innerClasses/Anonymous.java | 14 ----- .../innerClasses/AnonymousWithScopeArgs.java | 13 ---- .../innerClasses/B_AnonymousWithScopeArgs.java | 13 ++++ .../inputs/innerClasses/C_ConstructorArgs.java | 20 ++++++ .../inputs/innerClasses/ConstructorArgs.java | 20 ------ .../enigma/inputs/innerClasses/D_Simple.java | 8 +++ .../innerClasses/E_AnonymousWithOuterAccess.java | 21 +++++++ test/cuchaz/enigma/inputs/innerClasses/Simple.java | 8 --- 22 files changed, 129 insertions(+), 163 deletions(-) delete mode 100644 src/cuchaz/enigma/analysis/BridgeFixer.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java delete mode 100644 test/cuchaz/enigma/inputs/innerClasses/Anonymous.java delete mode 100644 test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java delete mode 100644 test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/D_Simple.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java delete mode 100644 test/cuchaz/enigma/inputs/innerClasses/Simple.java diff --git a/.classpath b/.classpath index 7a9ca3de..16e0b310 100644 --- a/.classpath +++ b/.classpath @@ -4,6 +4,7 @@ + diff --git a/build.py b/build.py index 09718cb2..8e37d0ff 100644 --- a/build.py +++ b/build.py @@ -28,7 +28,7 @@ ExtraRepos = [ LibDeps = [ ssjb.ivy.Dep("com.google.guava:guava:17.0"), ssjb.ivy.Dep("org.javassist:javassist:3.18.1-GA"), - ssjb.ivy.Dep("org.bitbucket.mstrobel:procyon-decompiler:0.5.26-enigma") + ssjb.ivy.Dep("org.bitbucket.mstrobel:procyon-decompiler:0.5.28-enigma") ] StandaloneDeps = LibDeps + [ ssjb.ivy.Dep("de.sciss:jsyntaxpane:1.0.0") @@ -47,7 +47,7 @@ def buildTestJar(name, glob): pathObfJar = os.path.join(DirBuild, "%s.obf.jar" % name) # build the deobf jar - with ssjb.file.TempDir(DirTemp) as dirTemp: + with ssjb.file.TempDir("tmp") as dirTemp: ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin, "cuchaz/enigma/inputs/Keep.class")) ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin, glob)) ssjb.jar.makeJar(pathJar, dirTemp) @@ -96,7 +96,7 @@ def buildLibJar(dirOut): def taskGetDeps(): ssjb.file.mkdir(DirLib) - ssjb.ivy.makeLibsJar(os.path.join(DirLib, "deps.jar"), Deps, extraRepos=ExtraRepos) + ssjb.ivy.makeLibsJar(os.path.join(DirLib, "deps.jar"), StandaloneDeps, extraRepos=ExtraRepos) ssjb.ivy.makeLibsJar(os.path.join(DirLib, "test-deps.jar"), TestDeps) ssjb.ivy.makeJar(os.path.join(DirLib, "proguard.jar"), ProguardDep) diff --git a/src/cuchaz/enigma/CommandMain.java b/src/cuchaz/enigma/CommandMain.java index 74bd4991..1ec2ad23 100644 --- a/src/cuchaz/enigma/CommandMain.java +++ b/src/cuchaz/enigma/CommandMain.java @@ -2,6 +2,7 @@ package cuchaz.enigma; import java.io.File; import java.io.FileReader; +import java.util.jar.JarFile; import cuchaz.enigma.Deobfuscator.ProgressListener; import cuchaz.enigma.mapping.Mappings; @@ -78,7 +79,7 @@ public class CommandMain { File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); File fileJarOut = getWritableFolder(getArg(args, 3, "out folder")); - Deobfuscator deobfuscator = getDeobfuscator(fileMappings, fileJarIn); + Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); } @@ -87,16 +88,16 @@ public class CommandMain { File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); File fileJarOut = getWritableFile(getArg(args, 3, "out jar")); - Deobfuscator deobfuscator = getDeobfuscator(fileMappings, fileJarIn); + Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); } - private static Deobfuscator getDeobfuscator(File fileMappings, File fileJar) + private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) throws Exception { System.out.println("Reading mappings..."); Mappings mappings = new MappingsReader().read(new FileReader(fileMappings)); System.out.println("Reading jar..."); - Deobfuscator deobfuscator = new Deobfuscator(fileJar); + Deobfuscator deobfuscator = new Deobfuscator(jar); deobfuscator.setMappings(mappings); return deobfuscator; } diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 22c517a1..62c062a5 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -68,7 +68,6 @@ public class Deobfuscator { void onProgress(int numDone, String message); } - private File m_file; private JarFile m_jar; private DecompilerSettings m_settings; private JarIndex m_jarIndex; @@ -76,9 +75,8 @@ public class Deobfuscator { private MappingsRenamer m_renamer; private Map m_translatorCache; - public Deobfuscator(File file) throws IOException { - m_file = file; - m_jar = new JarFile(m_file); + public Deobfuscator(JarFile jar) throws IOException { + m_jar = jar; // build the jar index m_jarIndex = new JarIndex(); @@ -100,7 +98,7 @@ public class Deobfuscator { } public String getJarName() { - return m_file.getName(); + return m_jar.getName(); } public JarIndex getJarIndex() { diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 091f916d..9287999b 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -29,7 +29,6 @@ import com.strobel.assembler.metadata.Buffer; import com.strobel.assembler.metadata.ClasspathTypeLoader; import com.strobel.assembler.metadata.ITypeLoader; -import cuchaz.enigma.analysis.BridgeFixer; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.bytecode.ClassRenamer; import cuchaz.enigma.bytecode.ClassTranslator; @@ -168,7 +167,7 @@ public class TranslatingTypeLoader implements ITypeLoader { assertClassName(c, deobfClassEntry); // DEBUG - // Util.writeClass( c ); + Util.writeClass( c ); // we have a transformed class! return c.toBytecode(); @@ -196,7 +195,6 @@ public class TranslatingTypeLoader implements ITypeLoader { assertClassName(c, obfClassEntry); // do all kinds of deobfuscating transformations on the class - new BridgeFixer(m_jarIndex).fixBridges(c); new MethodParameterWriter(m_deobfuscatingTranslator).writeMethodArguments(c); new ClassTranslator(m_deobfuscatingTranslator).translate(c); diff --git a/src/cuchaz/enigma/analysis/BridgeFixer.java b/src/cuchaz/enigma/analysis/BridgeFixer.java deleted file mode 100644 index ad23b000..00000000 --- a/src/cuchaz/enigma/analysis/BridgeFixer.java +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import javassist.CtClass; -import javassist.CtMethod; -import javassist.bytecode.ConstPool; -import javassist.bytecode.Descriptor; -import cuchaz.enigma.bytecode.ConstPoolEditor; -import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.MethodEntry; - -public class BridgeFixer { - - private JarIndex m_index; - - public BridgeFixer(JarIndex index) { - m_index = index; - } - - public void fixBridges(CtClass c) { - // rename declared methods - for (CtMethod method : c.getDeclaredMethods()) { - // get the method entry - MethodEntry methodEntry = new MethodEntry( - new ClassEntry(Descriptor.toJvmName(c.getName())), - method.getName(), - method.getSignature() - ); - MethodEntry bridgeMethodEntry = m_index.getBridgeMethod(methodEntry); - if (bridgeMethodEntry != null) { - // fix this bridged method - method.setName(bridgeMethodEntry.getName()); - } - } - - // rename method references - // translate all the field and method references in the code by editing the constant pool - ConstPool constants = c.getClassFile().getConstPool(); - ConstPoolEditor editor = new ConstPoolEditor(constants); - for (int i = 1; i < constants.getSize(); i++) { - switch (constants.getTag(i)) { - case ConstPool.CONST_Methodref: - case ConstPool.CONST_InterfaceMethodref: { - BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(Descriptor.toJvmName(editor.getMemberrefClassname(i)), editor.getMemberrefName(i), editor.getMemberrefType(i)); - - if (behaviorEntry instanceof MethodEntry) { - MethodEntry methodEntry = (MethodEntry)behaviorEntry; - - // translate the name and type - MethodEntry bridgeMethodEntry = m_index.getBridgeMethod(methodEntry); - if (bridgeMethodEntry != null) { - // FIXIT FIXIT FIXIT FIXIT FIXIT FIXIT FIXIT - editor.changeMemberrefNameAndType(i, bridgeMethodEntry.getName(), bridgeMethodEntry.getSignature()); - } - } - } - break; - } - } - } -} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 86ba93b2..e652202c 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -33,6 +33,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Vector; +import java.util.jar.JarFile; import javax.swing.BorderFactory; import javax.swing.JEditorPane; @@ -499,7 +500,7 @@ public class Gui { @Override public void run() { try { - m_controller.openJar(m_jarFileChooser.getSelectedFile()); + m_controller.openJar(new JarFile(m_jarFileChooser.getSelectedFile())); } catch (IOException ex) { throw new Error(ex); } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 908c16fa..61fea9c0 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.util.Collection; import java.util.Deque; import java.util.List; +import java.util.jar.JarFile; import com.google.common.collect.Lists; import com.google.common.collect.Queues; @@ -66,9 +67,9 @@ public class GuiController { return m_isDirty; } - public void openJar(final File file) throws IOException { + public void openJar(final JarFile jar) throws IOException { m_gui.onStartOpenJar(); - m_deobfuscator = new Deobfuscator(file); + m_deobfuscator = new Deobfuscator(jar); m_gui.onFinishOpenJar(m_deobfuscator.getJarName()); refreshClasses(); } diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java index 6ac4c786..129d7b25 100644 --- a/test/cuchaz/enigma/TestDeobfuscator.java +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -13,9 +13,9 @@ package cuchaz.enigma; import static org.junit.Assert.*; -import java.io.File; import java.io.IOException; import java.util.List; +import java.util.jar.JarFile; import org.junit.Test; @@ -26,7 +26,7 @@ import cuchaz.enigma.mapping.ClassEntry; public class TestDeobfuscator { private Deobfuscator getDeobfuscator() throws IOException { - return new Deobfuscator(new File("build/testLoneClass.obf.jar")); + return new Deobfuscator(new JarFile("build/testLoneClass.obf.jar")); } @Test diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java index 51fb5e35..63c9b719 100644 --- a/test/cuchaz/enigma/TestInnerClasses.java +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -23,6 +23,7 @@ import cuchaz.enigma.analysis.JarIndex; public class TestInnerClasses { private JarIndex m_index; + private Deobfuscator m_deobfuscator; private static final String AnonymousOuter = "none/a"; private static final String AnonymousInner = "b"; @@ -32,10 +33,14 @@ public class TestInnerClasses { private static final String ConstructorArgsInner = "f"; private static final String AnonymousWithScopeArgsOuter = "none/c"; private static final String AnonymousWithScopeArgsInner = "d"; + private static final String AnonymousWithOuterAccessOuter = "none/i"; + private static final String AnonymousWithOuterAccessInner = "j"; public TestInnerClasses() throws Exception { m_index = new JarIndex(); - m_index.indexJar(new JarFile("build/testInnerClasses.obf.jar"), true); + JarFile jar = new JarFile("build/testInnerClasses.obf.jar"); + m_index.indexJar(jar, true); + m_deobfuscator = new Deobfuscator(jar); } @Test @@ -43,6 +48,7 @@ public class TestInnerClasses { assertThat(m_index.getOuterClass(SimpleInner), is(SimpleOuter)); assertThat(m_index.getInnerClasses(SimpleOuter), containsInAnyOrder(SimpleInner)); assertThat(m_index.isAnonymousClass(SimpleInner), is(false)); + decompile(SimpleOuter); } @Test @@ -50,6 +56,7 @@ public class TestInnerClasses { assertThat(m_index.getOuterClass(AnonymousInner), is(AnonymousOuter)); assertThat(m_index.getInnerClasses(AnonymousOuter), containsInAnyOrder(AnonymousInner)); assertThat(m_index.isAnonymousClass(AnonymousInner), is(true)); + decompile(AnonymousOuter); } @Test @@ -57,6 +64,7 @@ public class TestInnerClasses { assertThat(m_index.getOuterClass(ConstructorArgsInner), is(ConstructorArgsOuter)); assertThat(m_index.getInnerClasses(ConstructorArgsOuter), containsInAnyOrder(ConstructorArgsInner)); assertThat(m_index.isAnonymousClass(ConstructorArgsInner), is(false)); + decompile(ConstructorArgsOuter); } @Test @@ -64,5 +72,18 @@ public class TestInnerClasses { assertThat(m_index.getOuterClass(AnonymousWithScopeArgsInner), is(AnonymousWithScopeArgsOuter)); assertThat(m_index.getInnerClasses(AnonymousWithScopeArgsOuter), containsInAnyOrder(AnonymousWithScopeArgsInner)); assertThat(m_index.isAnonymousClass(AnonymousWithScopeArgsInner), is(true)); + decompile(AnonymousWithScopeArgsOuter); + } + + @Test + public void anonymousWithOuterAccess() { + assertThat(m_index.getOuterClass(AnonymousWithOuterAccessInner), is(AnonymousWithOuterAccessOuter)); + assertThat(m_index.getInnerClasses(AnonymousWithOuterAccessOuter), containsInAnyOrder(AnonymousWithOuterAccessInner)); + assertThat(m_index.isAnonymousClass(AnonymousWithOuterAccessInner), is(true)); + decompile(AnonymousWithOuterAccessOuter); + } + + private void decompile(String name) { + m_deobfuscator.getSourceTree(name); } } diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java index ba7fc797..70a5ee4c 100644 --- a/test/cuchaz/enigma/TestSourceIndex.java +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -11,8 +11,8 @@ ******************************************************************************/ package cuchaz.enigma; -import java.io.File; import java.util.Set; +import java.util.jar.JarFile; import org.junit.Test; @@ -26,7 +26,7 @@ public class TestSourceIndex { // TEMP @Test public void indexEverything() throws Exception { - Deobfuscator deobfuscator = new Deobfuscator(new File("input/1.8.jar")); + Deobfuscator deobfuscator = new Deobfuscator(new JarFile("input/1.8.jar")); // get all classes that aren't inner classes Set classEntries = Sets.newHashSet(); diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java index 9f85e81d..56424ae8 100644 --- a/test/cuchaz/enigma/TestTokensConstructors.java +++ b/test/cuchaz/enigma/TestTokensConstructors.java @@ -10,15 +10,11 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByConstructor; -import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByMethod; -import static cuchaz.enigma.EntryFactory.newConstructor; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; -import java.io.File; +import java.util.jar.JarFile; import org.junit.Test; @@ -27,7 +23,7 @@ import cuchaz.enigma.mapping.BehaviorEntry; public class TestTokensConstructors extends TokenChecker { public TestTokensConstructors() throws Exception { - super(new File("build/testConstructors.obf.jar")); + super(new JarFile("build/testConstructors.obf.jar")); } @Test @@ -63,11 +59,11 @@ public class TestTokensConstructors extends TokenChecker { ); assertThat( getReferenceTokens(newBehaviorReferenceByConstructor(source, "none/d", "()V")), - containsInAnyOrder("super") // implicit call, decompiled to "super" + is(empty()) // implicit call, not decompiled to token ); assertThat( getReferenceTokens(newBehaviorReferenceByConstructor(source, "none/d", "(III)V")), - containsInAnyOrder("super") // implicit call, decompiled to "super" + is(empty()) // implicit call, not decompiled to token ); } diff --git a/test/cuchaz/enigma/TokenChecker.java b/test/cuchaz/enigma/TokenChecker.java index 524c5ec5..febea2ae 100644 --- a/test/cuchaz/enigma/TokenChecker.java +++ b/test/cuchaz/enigma/TokenChecker.java @@ -10,10 +10,10 @@ ******************************************************************************/ package cuchaz.enigma; -import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.List; +import java.util.jar.JarFile; import com.google.common.collect.Lists; import com.strobel.decompiler.languages.java.ast.CompilationUnit; @@ -27,7 +27,7 @@ public class TokenChecker { private Deobfuscator m_deobfuscator; - protected TokenChecker(File jarFile) throws IOException { + protected TokenChecker(JarFile jarFile) throws IOException { m_deobfuscator = new Deobfuscator(jarFile); } diff --git a/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java b/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java new file mode 100644 index 00000000..f7118f63 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java @@ -0,0 +1,14 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class A_Anonymous { + + public void foo() { + Runnable runnable = new Runnable() { + @Override + public void run() { + // don't care + } + }; + runnable.run(); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java deleted file mode 100644 index f5d9d1cf..00000000 --- a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java +++ /dev/null @@ -1,14 +0,0 @@ -package cuchaz.enigma.inputs.innerClasses; - -public class Anonymous { - - public void foo() { - Runnable runnable = new Runnable() { - @Override - public void run() { - // don't care - } - }; - runnable.run(); - } -} diff --git a/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java b/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java deleted file mode 100644 index b3ba1af8..00000000 --- a/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java +++ /dev/null @@ -1,13 +0,0 @@ -package cuchaz.enigma.inputs.innerClasses; - -public class AnonymousWithScopeArgs { - - public static void foo(final Simple arg) { - System.out.println(new Object() { - @Override - public String toString() { - return arg.toString(); - } - }); - } -} diff --git a/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java b/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java new file mode 100644 index 00000000..42fba9a8 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java @@ -0,0 +1,13 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class B_AnonymousWithScopeArgs { + + public static void foo(final D_Simple arg) { + System.out.println(new Object() { + @Override + public String toString() { + return arg.toString(); + } + }); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java b/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java new file mode 100644 index 00000000..8fa6c5b8 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java @@ -0,0 +1,20 @@ +package cuchaz.enigma.inputs.innerClasses; + +@SuppressWarnings("unused") +public class C_ConstructorArgs { + + class Inner { + + private int a; + + public Inner(int a) { + this.a = a; + } + } + + Inner i; + + public void foo() { + i = new Inner(5); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java deleted file mode 100644 index 08135fe5..00000000 --- a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java +++ /dev/null @@ -1,20 +0,0 @@ -package cuchaz.enigma.inputs.innerClasses; - -@SuppressWarnings("unused") -public class ConstructorArgs { - - class Inner { - - private int a; - - public Inner(int a) { - this.a = a; - } - } - - Inner i; - - public void foo() { - i = new Inner(5); - } -} diff --git a/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java b/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java new file mode 100644 index 00000000..c4fc0ef9 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java @@ -0,0 +1,8 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class D_Simple { + + class Inner { + // nothing to do + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java b/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java new file mode 100644 index 00000000..e1de53cb --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java @@ -0,0 +1,21 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class E_AnonymousWithOuterAccess { + + // reproduction of error case documented at: + // https://bitbucket.org/cuchaz/enigma/issue/61/stackoverflowerror-when-deobfuscating + + public Object makeInner() { + outerMethod(); + return new Object() { + @Override + public String toString() { + return outerMethod(); + } + }; + } + + private String outerMethod() { + return "foo"; + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/Simple.java b/test/cuchaz/enigma/inputs/innerClasses/Simple.java deleted file mode 100644 index cb536fa6..00000000 --- a/test/cuchaz/enigma/inputs/innerClasses/Simple.java +++ /dev/null @@ -1,8 +0,0 @@ -package cuchaz.enigma.inputs.innerClasses; - -public class Simple { - - class Inner { - // nothing to do - } -} -- cgit v1.2.3 From fccf9aa1e78cb9413316850731f194e9d3f7de71 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 2 Feb 2015 22:37:00 -0500 Subject: herp a derp --- src/cuchaz/enigma/Deobfuscator.java | 2 +- src/cuchaz/enigma/Main.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 62c062a5..5f61686b 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -88,7 +88,7 @@ public class Deobfuscator { m_settings.setForceExplicitImports(true); m_settings.setForceExplicitTypeArguments(true); // DEBUG - // m_settings.setShowSyntheticMembers( true ); + //m_settings.setShowSyntheticMembers(true); // init defaults m_translatorCache = Maps.newTreeMap(); diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index 1891ded8..acae94b1 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -11,6 +11,7 @@ package cuchaz.enigma; import java.io.File; +import java.util.jar.JarFile; import cuchaz.enigma.gui.Gui; @@ -21,7 +22,7 @@ public class Main { // parse command-line args if (args.length >= 1) { - gui.getController().openJar(getFile(args[0])); + gui.getController().openJar(new JarFile(getFile(args[0]))); } if (args.length >= 2) { gui.getController().openMappings(getFile(args[1])); -- cgit v1.2.3 From 52ab426d8fad3dbee7e728f523a35af94facebda Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 3 Feb 2015 22:00:53 -0500 Subject: oops, don't depend on local procyon project --- .classpath | 10 + .hgignore | 9 + .project | 17 + .settings/org.eclipse.jdt.core.prefs | 13 + build.py | 119 ++ conf/about.html | 6 + license.APL2.txt | 55 + license.GPL3.txt | 674 ++++++++++++ proguard.conf | 7 + readme.txt | 28 + src/cuchaz/enigma/CommandMain.java | 136 +++ src/cuchaz/enigma/Constants.java | 20 + src/cuchaz/enigma/Deobfuscator.java | 539 +++++++++ src/cuchaz/enigma/Main.java | 51 + src/cuchaz/enigma/TranslatingTypeLoader.java | 211 ++++ src/cuchaz/enigma/Util.java | 104 ++ src/cuchaz/enigma/analysis/Access.java | 43 + .../enigma/analysis/BehaviorReferenceTreeNode.java | 93 ++ .../analysis/ClassImplementationsTreeNode.java | 80 ++ .../enigma/analysis/ClassInheritanceTreeNode.java | 85 ++ src/cuchaz/enigma/analysis/EntryReference.java | 126 +++ src/cuchaz/enigma/analysis/EntryRenamer.java | 171 +++ .../enigma/analysis/FieldReferenceTreeNode.java | 81 ++ src/cuchaz/enigma/analysis/JarClassIterator.java | 137 +++ src/cuchaz/enigma/analysis/JarIndex.java | 828 ++++++++++++++ .../analysis/MethodImplementationsTreeNode.java | 100 ++ .../enigma/analysis/MethodInheritanceTreeNode.java | 114 ++ src/cuchaz/enigma/analysis/ReferenceTreeNode.java | 18 + src/cuchaz/enigma/analysis/SourceIndex.java | 173 +++ .../analysis/SourceIndexBehaviorVisitor.java | 163 +++ .../enigma/analysis/SourceIndexClassVisitor.java | 114 ++ src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 452 ++++++++ src/cuchaz/enigma/analysis/Token.java | 56 + src/cuchaz/enigma/analysis/TranslationIndex.java | 227 ++++ src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 512 +++++++++ src/cuchaz/enigma/bytecode/CheckCastIterator.java | 126 +++ src/cuchaz/enigma/bytecode/ClassRenamer.java | 110 ++ src/cuchaz/enigma/bytecode/ClassTranslator.java | 141 +++ src/cuchaz/enigma/bytecode/ConstPoolEditor.java | 263 +++++ src/cuchaz/enigma/bytecode/InfoType.java | 317 ++++++ src/cuchaz/enigma/bytecode/InnerClassWriter.java | 102 ++ .../enigma/bytecode/MethodParameterWriter.java | 66 ++ .../enigma/bytecode/MethodParametersAttribute.java | 85 ++ .../bytecode/accessors/ClassInfoAccessor.java | 55 + .../bytecode/accessors/ConstInfoAccessor.java | 156 +++ .../accessors/InvokeDynamicInfoAccessor.java | 74 ++ .../bytecode/accessors/MemberRefInfoAccessor.java | 74 ++ .../accessors/MethodHandleInfoAccessor.java | 74 ++ .../bytecode/accessors/MethodTypeInfoAccessor.java | 55 + .../accessors/NameAndTypeInfoAccessor.java | 74 ++ .../bytecode/accessors/StringInfoAccessor.java | 55 + .../bytecode/accessors/Utf8InfoAccessor.java | 28 + src/cuchaz/enigma/convert/ClassIdentity.java | 411 +++++++ src/cuchaz/enigma/convert/ClassMatcher.java | 415 +++++++ src/cuchaz/enigma/convert/ClassMatching.java | 173 +++ src/cuchaz/enigma/convert/ClassNamer.java | 64 ++ src/cuchaz/enigma/gui/AboutDialog.java | 86 ++ src/cuchaz/enigma/gui/BoxHighlightPainter.java | 64 ++ src/cuchaz/enigma/gui/BrowserCaret.java | 45 + src/cuchaz/enigma/gui/ClassListCellRenderer.java | 36 + src/cuchaz/enigma/gui/ClassSelector.java | 164 +++ src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 35 + .../enigma/gui/ClassSelectorPackageNode.java | 33 + src/cuchaz/enigma/gui/CrashDialog.java | 101 ++ .../enigma/gui/DeobfuscatedHighlightPainter.java | 21 + src/cuchaz/enigma/gui/Gui.java | 1164 ++++++++++++++++++++ src/cuchaz/enigma/gui/GuiController.java | 355 ++++++ src/cuchaz/enigma/gui/GuiTricks.java | 36 + .../enigma/gui/ObfuscatedHighlightPainter.java | 21 + src/cuchaz/enigma/gui/OtherHighlightPainter.java | 21 + src/cuchaz/enigma/gui/ProgressDialog.java | 105 ++ src/cuchaz/enigma/gui/ReadableToken.java | 36 + src/cuchaz/enigma/gui/RenameListener.java | 17 + .../enigma/gui/SelectionHighlightPainter.java | 34 + src/cuchaz/enigma/gui/TokenListCellRenderer.java | 38 + src/cuchaz/enigma/mapping/ArgumentEntry.java | 116 ++ src/cuchaz/enigma/mapping/ArgumentMapping.java | 44 + src/cuchaz/enigma/mapping/BehaviorEntry.java | 15 + .../enigma/mapping/BehaviorEntryFactory.java | 57 + src/cuchaz/enigma/mapping/ClassEntry.java | 123 +++ src/cuchaz/enigma/mapping/ClassMapping.java | 405 +++++++ src/cuchaz/enigma/mapping/ConstructorEntry.java | 116 ++ src/cuchaz/enigma/mapping/Entry.java | 18 + src/cuchaz/enigma/mapping/EntryPair.java | 22 + src/cuchaz/enigma/mapping/FieldEntry.java | 88 ++ src/cuchaz/enigma/mapping/FieldMapping.java | 43 + .../enigma/mapping/IllegalNameException.java | 44 + src/cuchaz/enigma/mapping/JavassistUtil.java | 55 + .../enigma/mapping/MappingParseException.java | 29 + src/cuchaz/enigma/mapping/Mappings.java | 213 ++++ src/cuchaz/enigma/mapping/MappingsReader.java | 176 +++ src/cuchaz/enigma/mapping/MappingsRenamer.java | 237 ++++ src/cuchaz/enigma/mapping/MappingsWriter.java | 88 ++ src/cuchaz/enigma/mapping/MethodEntry.java | 104 ++ src/cuchaz/enigma/mapping/MethodMapping.java | 162 +++ src/cuchaz/enigma/mapping/NameValidator.java | 80 ++ src/cuchaz/enigma/mapping/SignatureUpdater.java | 94 ++ .../enigma/mapping/TranslationDirection.java | 29 + src/cuchaz/enigma/mapping/Translator.java | 235 ++++ test/cuchaz/enigma/EntryFactory.java | 54 + test/cuchaz/enigma/TestDeobfuscator.java | 54 + test/cuchaz/enigma/TestInnerClasses.java | 89 ++ .../enigma/TestJarIndexConstructorReferences.java | 124 +++ .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 228 ++++ test/cuchaz/enigma/TestJarIndexLoneClass.java | 169 +++ test/cuchaz/enigma/TestSourceIndex.java | 49 + test/cuchaz/enigma/TestTokensConstructors.java | 135 +++ test/cuchaz/enigma/TokenChecker.java | 64 ++ test/cuchaz/enigma/inputs/Keep.java | 7 + .../enigma/inputs/constructors/BaseClass.java | 15 + test/cuchaz/enigma/inputs/constructors/Caller.java | 47 + .../inputs/constructors/DefaultConstructable.java | 5 + .../enigma/inputs/constructors/SubClass.java | 28 + .../enigma/inputs/constructors/SubSubClass.java | 11 + .../enigma/inputs/inheritanceTree/BaseClass.java | 21 + .../enigma/inputs/inheritanceTree/SubclassA.java | 11 + .../enigma/inputs/inheritanceTree/SubclassB.java | 30 + .../inputs/inheritanceTree/SubsubclassAA.java | 24 + .../enigma/inputs/innerClasses/A_Anonymous.java | 14 + .../innerClasses/B_AnonymousWithScopeArgs.java | 13 + .../inputs/innerClasses/C_ConstructorArgs.java | 20 + .../enigma/inputs/innerClasses/D_Simple.java | 8 + .../innerClasses/E_AnonymousWithOuterAccess.java | 21 + test/cuchaz/enigma/inputs/loneClass/LoneClass.java | 14 + 124 files changed, 14750 insertions(+) create mode 100644 .classpath create mode 100644 .hgignore create mode 100644 .project create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 build.py create mode 100644 conf/about.html create mode 100644 license.APL2.txt create mode 100644 license.GPL3.txt create mode 100644 proguard.conf create mode 100644 readme.txt create mode 100644 src/cuchaz/enigma/CommandMain.java create mode 100644 src/cuchaz/enigma/Constants.java create mode 100644 src/cuchaz/enigma/Deobfuscator.java create mode 100644 src/cuchaz/enigma/Main.java create mode 100644 src/cuchaz/enigma/TranslatingTypeLoader.java create mode 100644 src/cuchaz/enigma/Util.java create mode 100644 src/cuchaz/enigma/analysis/Access.java create mode 100644 src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/EntryReference.java create mode 100644 src/cuchaz/enigma/analysis/EntryRenamer.java create mode 100644 src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/JarClassIterator.java create mode 100644 src/cuchaz/enigma/analysis/JarIndex.java create mode 100644 src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/ReferenceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndex.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndexVisitor.java create mode 100644 src/cuchaz/enigma/analysis/Token.java create mode 100644 src/cuchaz/enigma/analysis/TranslationIndex.java create mode 100644 src/cuchaz/enigma/analysis/TreeDumpVisitor.java create mode 100644 src/cuchaz/enigma/bytecode/CheckCastIterator.java create mode 100644 src/cuchaz/enigma/bytecode/ClassRenamer.java create mode 100644 src/cuchaz/enigma/bytecode/ClassTranslator.java create mode 100644 src/cuchaz/enigma/bytecode/ConstPoolEditor.java create mode 100644 src/cuchaz/enigma/bytecode/InfoType.java create mode 100644 src/cuchaz/enigma/bytecode/InnerClassWriter.java create mode 100644 src/cuchaz/enigma/bytecode/MethodParameterWriter.java create mode 100644 src/cuchaz/enigma/bytecode/MethodParametersAttribute.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java create mode 100644 src/cuchaz/enigma/convert/ClassIdentity.java create mode 100644 src/cuchaz/enigma/convert/ClassMatcher.java create mode 100644 src/cuchaz/enigma/convert/ClassMatching.java create mode 100644 src/cuchaz/enigma/convert/ClassNamer.java create mode 100644 src/cuchaz/enigma/gui/AboutDialog.java create mode 100644 src/cuchaz/enigma/gui/BoxHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/BrowserCaret.java create mode 100644 src/cuchaz/enigma/gui/ClassListCellRenderer.java create mode 100644 src/cuchaz/enigma/gui/ClassSelector.java create mode 100644 src/cuchaz/enigma/gui/ClassSelectorClassNode.java create mode 100644 src/cuchaz/enigma/gui/ClassSelectorPackageNode.java create mode 100644 src/cuchaz/enigma/gui/CrashDialog.java create mode 100644 src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/Gui.java create mode 100644 src/cuchaz/enigma/gui/GuiController.java create mode 100644 src/cuchaz/enigma/gui/GuiTricks.java create mode 100644 src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/OtherHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/ProgressDialog.java create mode 100644 src/cuchaz/enigma/gui/ReadableToken.java create mode 100644 src/cuchaz/enigma/gui/RenameListener.java create mode 100644 src/cuchaz/enigma/gui/SelectionHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/TokenListCellRenderer.java create mode 100644 src/cuchaz/enigma/mapping/ArgumentEntry.java create mode 100644 src/cuchaz/enigma/mapping/ArgumentMapping.java create mode 100644 src/cuchaz/enigma/mapping/BehaviorEntry.java create mode 100644 src/cuchaz/enigma/mapping/BehaviorEntryFactory.java create mode 100644 src/cuchaz/enigma/mapping/ClassEntry.java create mode 100644 src/cuchaz/enigma/mapping/ClassMapping.java create mode 100644 src/cuchaz/enigma/mapping/ConstructorEntry.java create mode 100644 src/cuchaz/enigma/mapping/Entry.java create mode 100644 src/cuchaz/enigma/mapping/EntryPair.java create mode 100644 src/cuchaz/enigma/mapping/FieldEntry.java create mode 100644 src/cuchaz/enigma/mapping/FieldMapping.java create mode 100644 src/cuchaz/enigma/mapping/IllegalNameException.java create mode 100644 src/cuchaz/enigma/mapping/JavassistUtil.java create mode 100644 src/cuchaz/enigma/mapping/MappingParseException.java create mode 100644 src/cuchaz/enigma/mapping/Mappings.java create mode 100644 src/cuchaz/enigma/mapping/MappingsReader.java create mode 100644 src/cuchaz/enigma/mapping/MappingsRenamer.java create mode 100644 src/cuchaz/enigma/mapping/MappingsWriter.java create mode 100644 src/cuchaz/enigma/mapping/MethodEntry.java create mode 100644 src/cuchaz/enigma/mapping/MethodMapping.java create mode 100644 src/cuchaz/enigma/mapping/NameValidator.java create mode 100644 src/cuchaz/enigma/mapping/SignatureUpdater.java create mode 100644 src/cuchaz/enigma/mapping/TranslationDirection.java create mode 100644 src/cuchaz/enigma/mapping/Translator.java create mode 100644 test/cuchaz/enigma/EntryFactory.java create mode 100644 test/cuchaz/enigma/TestDeobfuscator.java create mode 100644 test/cuchaz/enigma/TestInnerClasses.java create mode 100644 test/cuchaz/enigma/TestJarIndexConstructorReferences.java create mode 100644 test/cuchaz/enigma/TestJarIndexInheritanceTree.java create mode 100644 test/cuchaz/enigma/TestJarIndexLoneClass.java create mode 100644 test/cuchaz/enigma/TestSourceIndex.java create mode 100644 test/cuchaz/enigma/TestTokensConstructors.java create mode 100644 test/cuchaz/enigma/TokenChecker.java create mode 100644 test/cuchaz/enigma/inputs/Keep.java create mode 100644 test/cuchaz/enigma/inputs/constructors/BaseClass.java create mode 100644 test/cuchaz/enigma/inputs/constructors/Caller.java create mode 100644 test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java create mode 100644 test/cuchaz/enigma/inputs/constructors/SubClass.java create mode 100644 test/cuchaz/enigma/inputs/constructors/SubSubClass.java create mode 100644 test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java create mode 100644 test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java create mode 100644 test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java create mode 100644 test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/D_Simple.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java create mode 100644 test/cuchaz/enigma/inputs/loneClass/LoneClass.java diff --git a/.classpath b/.classpath new file mode 100644 index 00000000..7a9ca3de --- /dev/null +++ b/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..659df811 --- /dev/null +++ b/.hgignore @@ -0,0 +1,9 @@ + +syntax: glob +bin +lib +build +data +input +ivy +*.pyc \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 00000000..08dff6c0 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + Enigma + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..b5d234f2 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,13 @@ +# +#Mon Sep 22 22:51:23 EDT 2014 +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error diff --git a/build.py b/build.py new file mode 100644 index 00000000..8e37d0ff --- /dev/null +++ b/build.py @@ -0,0 +1,119 @@ + +import os +import sys + +# settings +PathSsjb = "../ssjb" +Author = "Cuchaz" + +DirBin = "bin" +DirLib = "lib" +DirBuild = "build" +PathLocalMavenRepo = "../maven" + + +# import ssjb +sys.path.insert(0, PathSsjb) +import ssjb +import ssjb.ivy + + +ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.6b") +ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.6b") + +# dependencies +ExtraRepos = [ + "http://maven.cuchazinteractive.com" +] +LibDeps = [ + ssjb.ivy.Dep("com.google.guava:guava:17.0"), + ssjb.ivy.Dep("org.javassist:javassist:3.18.1-GA"), + ssjb.ivy.Dep("org.bitbucket.mstrobel:procyon-decompiler:0.5.28-enigma") +] +StandaloneDeps = LibDeps + [ + ssjb.ivy.Dep("de.sciss:jsyntaxpane:1.0.0") +] +ProguardDep = ssjb.ivy.Dep("net.sf.proguard:proguard-base:5.1") +TestDeps = [ + ssjb.ivy.Dep("junit:junit:4.12"), + ssjb.ivy.Dep("org.hamcrest:hamcrest-all:1.3") +] + +# functions + +def buildTestJar(name, glob): + + pathJar = os.path.join(DirBuild, "%s.jar" % name) + pathObfJar = os.path.join(DirBuild, "%s.obf.jar" % name) + + # build the deobf jar + with ssjb.file.TempDir("tmp") as dirTemp: + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin, "cuchaz/enigma/inputs/Keep.class")) + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin, glob)) + ssjb.jar.makeJar(pathJar, dirTemp) + + # build the obf jar + ssjb.callJavaJar( + os.path.join(DirLib, "proguard.jar"), + ["@proguard.conf", "-injars", pathJar, "-outjars", pathObfJar] + ) + + +def applyReadme(dirTemp): + ssjb.file.copy(dirTemp, "license.APL2.txt") + ssjb.file.copy(dirTemp, "license.GPL3.txt") + ssjb.file.copy(dirTemp, "readme.txt") + + +def buildStandaloneJar(dirOut): + with ssjb.file.TempDir(os.path.join(dirOut, "tmp")) as dirTemp: + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin)) + for path in ssjb.ivy.getJarPaths(StandaloneDeps, ExtraRepos): + ssjb.jar.unpackJar(dirTemp, path) + ssjb.file.delete(os.path.join(dirTemp, "LICENSE.txt")) + ssjb.file.delete(os.path.join(dirTemp, "META-INF/maven")) + applyReadme(dirTemp) + manifest = ssjb.jar.buildManifest( + ArtifactStandalone.artifactId, + ArtifactStandalone.version, + Author, + "cuchaz.enigma.Main" + ) + pathJar = os.path.join(DirBuild, "%s.jar" % ArtifactStandalone.getName()) + ssjb.jar.makeJar(pathJar, dirTemp, manifest=manifest) + ssjb.ivy.deployJarToLocalMavenRepo(PathLocalMavenRepo, pathJar, ArtifactStandalone) + +def buildLibJar(dirOut): + with ssjb.file.TempDir(os.path.join(dirOut, "tmp")) as dirTemp: + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin)) + applyReadme(dirTemp) + pathJar = os.path.join(DirBuild, "%s.jar" % ArtifactLib.getName()) + ssjb.jar.makeJar(pathJar, dirTemp) + ssjb.ivy.deployJarToLocalMavenRepo(PathLocalMavenRepo, pathJar, ArtifactLib, deps=LibDeps) + + +# tasks + +def taskGetDeps(): + ssjb.file.mkdir(DirLib) + ssjb.ivy.makeLibsJar(os.path.join(DirLib, "deps.jar"), StandaloneDeps, extraRepos=ExtraRepos) + ssjb.ivy.makeLibsJar(os.path.join(DirLib, "test-deps.jar"), TestDeps) + ssjb.ivy.makeJar(os.path.join(DirLib, "proguard.jar"), ProguardDep) + +def taskBuildTestJars(): + buildTestJar("testLoneClass", "cuchaz/enigma/inputs/loneClass/*.class") + buildTestJar("testConstructors", "cuchaz/enigma/inputs/constructors/*.class") + buildTestJar("testInheritanceTree", "cuchaz/enigma/inputs/inheritanceTree/*.class") + buildTestJar("testInnerClasses", "cuchaz/enigma/inputs/innerClasses/*.class") + +def taskBuild(): + ssjb.file.delete(DirBuild) + ssjb.file.mkdir(DirBuild) + buildStandaloneJar(DirBuild) + buildLibJar(DirBuild) + +ssjb.registerTask("getDeps", taskGetDeps) +ssjb.registerTask("buildTestJars", taskBuildTestJars) +ssjb.registerTask("build", taskBuild) +ssjb.run() + diff --git a/conf/about.html b/conf/about.html new file mode 100644 index 00000000..b75c1bf0 --- /dev/null +++ b/conf/about.html @@ -0,0 +1,6 @@ + +

%s

+

A tool for debofuscation of Java code

+

+

Version: %s

+ \ No newline at end of file diff --git a/license.APL2.txt b/license.APL2.txt new file mode 100644 index 00000000..a453e432 --- /dev/null +++ b/license.APL2.txt @@ -0,0 +1,55 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + + +You must cause any modified files to carry prominent notices stating that You changed the files; and + + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/license.GPL3.txt b/license.GPL3.txt new file mode 100644 index 00000000..20d40b6b --- /dev/null +++ b/license.GPL3.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/proguard.conf b/proguard.conf new file mode 100644 index 00000000..e1f04aee --- /dev/null +++ b/proguard.conf @@ -0,0 +1,7 @@ +-libraryjars /lib/rt.jar +-overloadaggressively +-repackageclasses +-allowaccessmodification +-dontoptimize +-dontshrink +-keep class cuchaz.enigma.inputs.Keep \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 00000000..3844f54e --- /dev/null +++ b/readme.txt @@ -0,0 +1,28 @@ + +Enigma v0.6 beta +A tool for deobfuscation of Java bytecode + +Copyright Jeff Martin, 2014 + + +LICENSE + +Enigma is distributed under the GNU General Public license version 3 + +Enigma includes a modified version of Procyon which is distributed under the Apache license version 2. Procyon is copyrighted by Mike Strobel, 2013 + +Enigma includes unmodified versions of the following libraries which are also released under the Apache license version 2. + Guava + Javassist + JSyntaxPane + +Copies of the GNU General Public license verion 3 and the Apache license v2 have been included in this distribution. + + +USING ENIGMA + +Launch the GUI: + java -jar enigma.jar + +Use Enigma on the command line: + java -cp enigma.jar cuchaz.enigma.CommandMain diff --git a/src/cuchaz/enigma/CommandMain.java b/src/cuchaz/enigma/CommandMain.java new file mode 100644 index 00000000..1ec2ad23 --- /dev/null +++ b/src/cuchaz/enigma/CommandMain.java @@ -0,0 +1,136 @@ +package cuchaz.enigma; + +import java.io.File; +import java.io.FileReader; +import java.util.jar.JarFile; + +import cuchaz.enigma.Deobfuscator.ProgressListener; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsReader; + +public class CommandMain { + + public static class ConsoleProgressListener implements ProgressListener { + + private static final int ReportTime = 5000; // 5s + + private int m_totalWork; + private long m_startTime; + private long m_lastReportTime; + + @Override + public void init(int totalWork, String title) { + m_totalWork = totalWork; + m_startTime = System.currentTimeMillis(); + m_lastReportTime = m_startTime; + System.out.println(title); + } + + @Override + public void onProgress(int numDone, String message) { + + long now = System.currentTimeMillis(); + boolean isLastUpdate = numDone == m_totalWork; + boolean shouldReport = isLastUpdate || now - m_lastReportTime > ReportTime; + + if (shouldReport) { + int percent = numDone*100/m_totalWork; + System.out.println(String.format("\tProgress: %3d%%", percent)); + m_lastReportTime = now; + } + if (isLastUpdate) { + double elapsedSeconds = (now - m_startTime)/1000; + System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds)); + } + } + } + + public static void main(String[] args) + throws Exception { + + try { + + // process the command + String command = getArg(args, 0, "command"); + if (command.equalsIgnoreCase("deobfuscate")) { + deobfuscate(args); + } else if(command.equalsIgnoreCase("decompile")) { + decompile(args); + } else { + throw new IllegalArgumentException("Command not recognized: " + command); + } + } catch (IllegalArgumentException ex) { + System.out.println(ex.getMessage()); + printHelp(); + } + } + + private static void printHelp() { + System.out.println(String.format("%s - %s", Constants.Name, Constants.Version)); + System.out.println("Usage:"); + System.out.println("\tjava -cp enigma.jar cuchaz.enigma.CommandMain "); + System.out.println("\twhere is one of:"); + System.out.println("\t\tdeobfuscate "); + System.out.println("\t\tdecompile "); + } + + private static void decompile(String[] args) + throws Exception { + File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); + File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); + File fileJarOut = getWritableFolder(getArg(args, 3, "out folder")); + Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); + deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); + } + + private static void deobfuscate(String[] args) + throws Exception { + File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); + File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); + File fileJarOut = getWritableFile(getArg(args, 3, "out jar")); + Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); + deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); + } + + private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) + throws Exception { + System.out.println("Reading mappings..."); + Mappings mappings = new MappingsReader().read(new FileReader(fileMappings)); + System.out.println("Reading jar..."); + Deobfuscator deobfuscator = new Deobfuscator(jar); + deobfuscator.setMappings(mappings); + return deobfuscator; + } + + private static String getArg(String[] args, int i, String name) { + if (i >= args.length) { + throw new IllegalArgumentException(name + " is required"); + } + return args[i]; + } + + private static File getWritableFile(String path) { + File file = new File(path).getAbsoluteFile(); + File dir = file.getParentFile(); + if (dir == null || !dir.exists()) { + throw new IllegalArgumentException("Cannot write to folder: " + file); + } + return file; + } + + private static File getWritableFolder(String path) { + File dir = new File(path).getAbsoluteFile(); + if (!dir.exists()) { + throw new IllegalArgumentException("Cannot write to folder: " + dir); + } + return dir; + } + + private static File getReadableFile(String path) { + File file = new File(path).getAbsoluteFile(); + if (!file.exists()) { + throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); + } + return file; + } +} diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java new file mode 100644 index 00000000..a1ba2e98 --- /dev/null +++ b/src/cuchaz/enigma/Constants.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +public class Constants { + public static final String Name = "Enigma"; + public static final String Version = "0.6 beta"; + public static final String Url = "http://www.cuchazinteractive.com/enigma"; + public static final int MiB = 1024 * 1024; // 1 mebibyte + public static final int KiB = 1024; // 1 kebibyte + public static final String NonePackage = "none"; +} diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java new file mode 100644 index 00000000..5f61686b --- /dev/null +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -0,0 +1,539 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; + +import javassist.CtClass; +import javassist.bytecode.Descriptor; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.strobel.assembler.metadata.MetadataSystem; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.decompiler.DecompilerContext; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import com.strobel.decompiler.languages.java.JavaOutputVisitor; +import com.strobel.decompiler.languages.java.ast.AstBuilder; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; +import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarClassIterator; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.SourceIndexVisitor; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.FieldMapping; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsRenamer; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.MethodMapping; +import cuchaz.enigma.mapping.TranslationDirection; +import cuchaz.enigma.mapping.Translator; + +public class Deobfuscator { + + public interface ProgressListener { + void init(int totalWork, String title); + void onProgress(int numDone, String message); + } + + private JarFile m_jar; + private DecompilerSettings m_settings; + private JarIndex m_jarIndex; + private Mappings m_mappings; + private MappingsRenamer m_renamer; + private Map m_translatorCache; + + public Deobfuscator(JarFile jar) throws IOException { + m_jar = jar; + + // build the jar index + m_jarIndex = new JarIndex(); + m_jarIndex.indexJar(m_jar, true); + + // config the decompiler + m_settings = DecompilerSettings.javaDefaults(); + m_settings.setMergeVariables(true); + m_settings.setForceExplicitImports(true); + m_settings.setForceExplicitTypeArguments(true); + // DEBUG + //m_settings.setShowSyntheticMembers(true); + + // init defaults + m_translatorCache = Maps.newTreeMap(); + + // init mappings + setMappings(new Mappings()); + } + + public String getJarName() { + return m_jar.getName(); + } + + public JarIndex getJarIndex() { + return m_jarIndex; + } + + public Mappings getMappings() { + return m_mappings; + } + + public void setMappings(Mappings val) { + if (val == null) { + val = new Mappings(); + } + + // pass 1: look for any classes that got moved to inner classes + Map renames = Maps.newHashMap(); + for (ClassMapping classMapping : val.classes()) { + // make sure we strip the packages off of obfuscated inner classes + String innerClassName = new ClassEntry(classMapping.getObfName()).getSimpleName(); + String outerClassName = m_jarIndex.getOuterClass(innerClassName); + if (outerClassName != null) { + // build the composite class name + String newName = outerClassName + "$" + innerClassName; + + // add a rename + renames.put(classMapping.getObfName(), newName); + + System.out.println(String.format("Converted class mapping %s to %s", classMapping.getObfName(), newName)); + } + } + for (Map.Entry entry : renames.entrySet()) { + val.renameObfClass(entry.getKey(), entry.getValue()); + } + + // pass 2: look for fields/methods that are actually declared in superclasses + MappingsRenamer renamer = new MappingsRenamer(m_jarIndex, val); + for (ClassMapping classMapping : Lists.newArrayList(val.classes())) { + ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfName()); + + // fields + for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { + FieldEntry fieldEntry = new FieldEntry(obfClassEntry, fieldMapping.getObfName()); + ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(fieldEntry); + if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(fieldEntry.getClassEntry())) { + boolean wasMoved = renamer.moveFieldToObfClass(classMapping, fieldMapping, resolvedObfClassEntry); + if (wasMoved) { + System.out.println(String.format("Moved field %s to class %s", fieldEntry, resolvedObfClassEntry)); + } else { + System.err.println(String.format("WARNING: Would move field %s to class %s but the field was already there. Dropping instead.", fieldEntry, resolvedObfClassEntry)); + } + } + } + + // methods + for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { + // skip constructors + if (methodMapping.isConstructor()) { + continue; + } + + MethodEntry methodEntry = new MethodEntry( + obfClassEntry, + methodMapping.getObfName(), + methodMapping.getObfSignature() + ); + ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(methodEntry); + if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(methodEntry.getClassEntry())) { + boolean wasMoved = renamer.moveMethodToObfClass(classMapping, methodMapping, resolvedObfClassEntry); + if (wasMoved) { + System.out.println(String.format("Moved method %s to class %s", methodEntry, resolvedObfClassEntry)); + } else { + System.err.println(String.format("WARNING: Would move method %s to class %s but the method was already there. Dropping instead.", methodEntry, resolvedObfClassEntry)); + } + } + } + + // TODO: recurse to inner classes? + } + + // drop mappings that don't match the jar + List unknownClasses = Lists.newArrayList(); + for (ClassMapping classMapping : val.classes()) { + checkClassMapping(unknownClasses, classMapping); + } + if (!unknownClasses.isEmpty()) { + throw new Error("Unable to find classes in jar: " + unknownClasses); + } + + m_mappings = val; + m_renamer = renamer; + m_translatorCache.clear(); + } + + private void checkClassMapping(List unknownClasses, ClassMapping classMapping) { + // check the class + ClassEntry classEntry = new ClassEntry(classMapping.getObfName()); + String outerClassName = m_jarIndex.getOuterClass(classEntry.getSimpleName()); + if (outerClassName != null) { + classEntry = new ClassEntry(outerClassName + "$" + classMapping.getObfName()); + } + if (!m_jarIndex.getObfClassEntries().contains(classEntry)) { + unknownClasses.add(classEntry); + } + + // check the fields + for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { + FieldEntry fieldEntry = new FieldEntry(classEntry, fieldMapping.getObfName()); + if (!m_jarIndex.containsObfField(fieldEntry)) { + System.err.println("WARNING: unable to find field " + fieldEntry + ". dropping mapping."); + classMapping.removeFieldMapping(fieldMapping); + } + } + + // check methods + for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { + BehaviorEntry obfBehaviorEntry = BehaviorEntryFactory.createObf(classEntry, methodMapping); + if (!m_jarIndex.containsObfBehavior(obfBehaviorEntry)) { + System.err.println("WARNING: unable to find behavior " + obfBehaviorEntry + ". dropping mapping."); + classMapping.removeMethodMapping(methodMapping); + } + } + + // check inner classes + for (ClassMapping innerClassMapping : classMapping.innerClasses()) { + checkClassMapping(unknownClasses, innerClassMapping); + } + } + + public Translator getTranslator(TranslationDirection direction) { + Translator translator = m_translatorCache.get(direction); + if (translator == null) { + translator = m_mappings.getTranslator(direction, m_jarIndex.getTranslationIndex()); + m_translatorCache.put(direction, translator); + } + return translator; + } + + public void getSeparatedClasses(List obfClasses, List deobfClasses) { + for (ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries()) { + // skip inner classes + if (obfClassEntry.isInnerClass()) { + continue; + } + + // separate the classes + ClassEntry deobfClassEntry = deobfuscateEntry(obfClassEntry); + if (!deobfClassEntry.equals(obfClassEntry)) { + // if the class has a mapping, clearly it's deobfuscated + deobfClasses.add(deobfClassEntry); + } else if (!obfClassEntry.getPackageName().equals(Constants.NonePackage)) { + // also call it deobufscated if it's not in the none package + deobfClasses.add(obfClassEntry); + } else { + // otherwise, assume it's still obfuscated + obfClasses.add(obfClassEntry); + } + } + } + + public CompilationUnit getSourceTree(String obfClassName) { + // is this class deobfuscated? + // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out + // the decompiler only sees the deobfuscated class, so we need to load it by the deobfuscated name + String lookupClassName = obfClassName; + ClassMapping classMapping = m_mappings.getClassByObf(obfClassName); + if (classMapping != null && classMapping.getDeobfName() != null) { + lookupClassName = classMapping.getDeobfName(); + } + + // is this class even in the jar? + if (!m_jarIndex.containsObfClass(new ClassEntry(obfClassName))) { + return null; + } + + // set the type loader + m_settings.setTypeLoader(new TranslatingTypeLoader( + m_jar, + m_jarIndex, + getTranslator(TranslationDirection.Obfuscating), + getTranslator(TranslationDirection.Deobfuscating) + )); + + // decompile it! + TypeDefinition resolvedType = new MetadataSystem(m_settings.getTypeLoader()).lookupType(lookupClassName).resolve(); + DecompilerContext context = new DecompilerContext(); + context.setCurrentType(resolvedType); + context.setSettings(m_settings); + AstBuilder builder = new AstBuilder(context); + builder.addType(resolvedType); + builder.runTransformations(null); + return builder.getCompilationUnit(); + } + + public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { + // build the source index + SourceIndex index = new SourceIndex(source); + sourceTree.acceptVisitor(new SourceIndexVisitor(), index); + + // DEBUG + // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); + + // resolve all the classes in the source references + for (Token token : index.referenceTokens()) { + EntryReference deobfReference = index.getDeobfReference(token); + + // get the obfuscated entry + Entry obfEntry = obfuscateEntry(deobfReference.entry); + + // try to resolve the class + ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(obfEntry); + if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { + // change the class of the entry + obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); + + // save the new deobfuscated reference + deobfReference.entry = deobfuscateEntry(obfEntry); + index.replaceDeobfReference(token, deobfReference); + } + + // DEBUG + // System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); + } + + return index; + } + + public String getSource(CompilationUnit sourceTree) { + // render the AST into source + StringWriter buf = new StringWriter(); + sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); + sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), m_settings), null); + return buf.toString(); + } + + public void writeSources(File dirOut, ProgressListener progress) throws IOException { + // get the classes to decompile + Set classEntries = Sets.newHashSet(); + for (ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries()) { + // skip inner classes + if (obfClassEntry.isInnerClass()) { + continue; + } + + classEntries.add(obfClassEntry); + } + + if (progress != null) { + progress.init(classEntries.size(), "Decompiling classes..."); + } + + // DEOBFUSCATE ALL THE THINGS!! @_@ + int i = 0; + for (ClassEntry obfClassEntry : classEntries) { + ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry)); + if (progress != null) { + progress.onProgress(i++, deobfClassEntry.toString()); + } + + try { + // get the source + String source = getSource(getSourceTree(obfClassEntry.getName())); + + // write the file + File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); + file.getParentFile().mkdirs(); + try (FileWriter out = new FileWriter(file)) { + out.write(source); + } + } catch (Throwable t) { + throw new Error("Unable to deobfuscate class " + deobfClassEntry.toString() + " (" + obfClassEntry.toString() + ")", t); + } + } + if (progress != null) { + progress.onProgress(i, "Done!"); + } + } + + public void writeJar(File out, ProgressListener progress) { + try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { + if (progress != null) { + progress.init(JarClassIterator.getClassEntries(m_jar).size(), "Translating classes..."); + } + + // prep the loader + TranslatingTypeLoader loader = new TranslatingTypeLoader( + m_jar, + m_jarIndex, + getTranslator(TranslationDirection.Obfuscating), + getTranslator(TranslationDirection.Deobfuscating) + ); + + int i = 0; + for (CtClass c : JarClassIterator.classes(m_jar)) { + if (progress != null) { + progress.onProgress(i++, c.getName()); + } + + try { + c = loader.transformClass(c); + outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); + outJar.write(c.toBytecode()); + outJar.closeEntry(); + } catch (Throwable t) { + throw new Error("Unable to deobfuscate class " + c.getName(), t); + } + } + if (progress != null) { + progress.onProgress(i, "Done!"); + } + + outJar.close(); + } catch (IOException ex) { + throw new Error("Unable to write to Jar file!"); + } + } + + public T obfuscateEntry(T deobfEntry) { + if (deobfEntry == null) { + return null; + } + return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry); + } + + public T deobfuscateEntry(T obfEntry) { + if (obfEntry == null) { + return null; + } + return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry); + } + + public EntryReference obfuscateReference(EntryReference deobfReference) { + if (deobfReference == null) { + return null; + } + return new EntryReference( + obfuscateEntry(deobfReference.entry), + obfuscateEntry(deobfReference.context), + deobfReference + ); + } + + public EntryReference deobfuscateReference(EntryReference obfReference) { + if (obfReference == null) { + return null; + } + return new EntryReference( + deobfuscateEntry(obfReference.entry), + deobfuscateEntry(obfReference.context), + obfReference + ); + } + + public boolean isObfuscatedIdentifier(Entry obfEntry) { + return m_jarIndex.containsObfEntry(obfEntry); + } + + public boolean isRenameable(EntryReference obfReference) { + return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry()); + } + + // NOTE: these methods are a bit messy... oh well + + public boolean hasDeobfuscatedName(Entry obfEntry) { + Translator translator = getTranslator(TranslationDirection.Deobfuscating); + if (obfEntry instanceof ClassEntry) { + return translator.translate((ClassEntry)obfEntry) != null; + } else if (obfEntry instanceof FieldEntry) { + return translator.translate((FieldEntry)obfEntry) != null; + } else if (obfEntry instanceof MethodEntry) { + return translator.translate((MethodEntry)obfEntry) != null; + } else if (obfEntry instanceof ConstructorEntry) { + // constructors have no names + return false; + } else if (obfEntry instanceof ArgumentEntry) { + return translator.translate((ArgumentEntry)obfEntry) != null; + } else { + throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); + } + } + + public void rename(Entry obfEntry, String newName) { + if (obfEntry instanceof ClassEntry) { + m_renamer.setClassName((ClassEntry)obfEntry, Descriptor.toJvmName(newName)); + } else if (obfEntry instanceof FieldEntry) { + m_renamer.setFieldName((FieldEntry)obfEntry, newName); + } else if (obfEntry instanceof MethodEntry) { + m_renamer.setMethodTreeName((MethodEntry)obfEntry, newName); + } else if (obfEntry instanceof ConstructorEntry) { + throw new IllegalArgumentException("Cannot rename constructors"); + } else if (obfEntry instanceof ArgumentEntry) { + m_renamer.setArgumentName((ArgumentEntry)obfEntry, newName); + } else { + throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); + } + + // clear caches + m_translatorCache.clear(); + } + + public void removeMapping(Entry obfEntry) { + if (obfEntry instanceof ClassEntry) { + m_renamer.removeClassMapping((ClassEntry)obfEntry); + } else if (obfEntry instanceof FieldEntry) { + m_renamer.removeFieldMapping((FieldEntry)obfEntry); + } else if (obfEntry instanceof MethodEntry) { + m_renamer.removeMethodTreeMapping((MethodEntry)obfEntry); + } else if (obfEntry instanceof ConstructorEntry) { + throw new IllegalArgumentException("Cannot rename constructors"); + } else if (obfEntry instanceof ArgumentEntry) { + m_renamer.removeArgumentMapping((ArgumentEntry)obfEntry); + } else { + throw new Error("Unknown entry type: " + obfEntry); + } + + // clear caches + m_translatorCache.clear(); + } + + public void markAsDeobfuscated(Entry obfEntry) { + if (obfEntry instanceof ClassEntry) { + m_renamer.markClassAsDeobfuscated((ClassEntry)obfEntry); + } else if (obfEntry instanceof FieldEntry) { + m_renamer.markFieldAsDeobfuscated((FieldEntry)obfEntry); + } else if (obfEntry instanceof MethodEntry) { + m_renamer.markMethodTreeAsDeobfuscated((MethodEntry)obfEntry); + } else if (obfEntry instanceof ConstructorEntry) { + throw new IllegalArgumentException("Cannot rename constructors"); + } else if (obfEntry instanceof ArgumentEntry) { + m_renamer.markArgumentAsDeobfuscated((ArgumentEntry)obfEntry); + } else { + throw new Error("Unknown entry type: " + obfEntry); + } + + // clear caches + m_translatorCache.clear(); + } +} diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java new file mode 100644 index 00000000..acae94b1 --- /dev/null +++ b/src/cuchaz/enigma/Main.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.File; +import java.util.jar.JarFile; + +import cuchaz.enigma.gui.Gui; + +public class Main { + + public static void main(String[] args) throws Exception { + Gui gui = new Gui(); + + // parse command-line args + if (args.length >= 1) { + gui.getController().openJar(new JarFile(getFile(args[0]))); + } + if (args.length >= 2) { + gui.getController().openMappings(getFile(args[1])); + } + + // DEBUG + //gui.getController().openDeclaration(new ClassEntry("none/bxq")); + } + + private static File getFile(String path) { + // expand ~ to the home dir + if (path.startsWith("~")) { + // get the home dir + File dirHome = new File(System.getProperty("user.home")); + + // is the path just ~/ or is it ~user/ ? + if (path.startsWith("~/")) { + return new File(dirHome, path.substring(2)); + } else { + return new File(dirHome.getParentFile(), path.substring(1)); + } + } + + return new File(path); + } +} diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java new file mode 100644 index 00000000..9287999b --- /dev/null +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import javassist.ByteArrayClassPath; +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.Descriptor; + +import com.google.common.collect.Maps; +import com.strobel.assembler.metadata.Buffer; +import com.strobel.assembler.metadata.ClasspathTypeLoader; +import com.strobel.assembler.metadata.ITypeLoader; + +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.bytecode.ClassRenamer; +import cuchaz.enigma.bytecode.ClassTranslator; +import cuchaz.enigma.bytecode.InnerClassWriter; +import cuchaz.enigma.bytecode.MethodParameterWriter; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Translator; + +public class TranslatingTypeLoader implements ITypeLoader { + + private JarFile m_jar; + private JarIndex m_jarIndex; + private Translator m_obfuscatingTranslator; + private Translator m_deobfuscatingTranslator; + private Map m_cache; + private ClasspathTypeLoader m_defaultTypeLoader; + + public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex) { + this(jar, jarIndex, new Translator(), new Translator()); + } + + public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) { + m_jar = jar; + m_jarIndex = jarIndex; + m_obfuscatingTranslator = obfuscatingTranslator; + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_cache = Maps.newHashMap(); + m_defaultTypeLoader = new ClasspathTypeLoader(); + } + + public void clearCache() { + m_cache.clear(); + } + + @Override + public boolean tryLoadType(String deobfClassName, Buffer out) { + // check the cache + byte[] data; + if (m_cache.containsKey(deobfClassName)) { + data = m_cache.get(deobfClassName); + } else { + data = loadType(deobfClassName); + m_cache.put(deobfClassName, data); + } + + if (data == null) { + // chain to default type loader + return m_defaultTypeLoader.tryLoadType(deobfClassName, out); + } + + // send the class to the decompiler + out.reset(data.length); + System.arraycopy(data, 0, out.array(), out.position(), data.length); + out.position(0); + return true; + } + + public CtClass loadClass(String deobfClassName) { + byte[] data = loadType(deobfClassName); + if (data == null) { + return null; + } + + // return a javassist handle for the class + String javaClassFileName = Descriptor.toJavaName(deobfClassName); + ClassPool classPool = new ClassPool(); + classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, data)); + try { + return classPool.get(javaClassFileName); + } catch (NotFoundException ex) { + throw new Error(ex); + } + } + + private byte[] loadType(String deobfClassName) { + ClassEntry deobfClassEntry = new ClassEntry(deobfClassName); + ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(deobfClassEntry); + + // is this an inner class referenced directly? + String obfOuterClassName = m_jarIndex.getOuterClass(obfClassEntry.getSimpleName()); + if (obfOuterClassName != null) { + // this class doesn't really exist. Reference it by outer$inner instead + System.err.println(String.format("WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassName)); + return null; + } + + /* DEBUG + if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) { + System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); + } + */ + + // get the jar entry + String classFileName; + if (obfClassEntry.isInnerClass()) { + // use just the inner class name for inner classes + classFileName = obfClassEntry.getInnerClassName(); + } else if (obfClassEntry.getPackageName().equals(Constants.NonePackage)) { + // use the outer class simple name for classes in the none package + classFileName = obfClassEntry.getSimpleName(); + } else { + // otherwise, just use the class name (ie for classes in packages) + classFileName = obfClassEntry.getName(); + } + + JarEntry entry = m_jar.getJarEntry(classFileName + ".class"); + if (entry == null) { + return null; + } + + try { + // read the class file into a buffer + ByteArrayOutputStream data = new ByteArrayOutputStream(); + byte[] buf = new byte[1024 * 1024]; // 1 KiB + InputStream in = m_jar.getInputStream(entry); + while (true) { + int bytesRead = in.read(buf); + if (bytesRead <= 0) { + break; + } + data.write(buf, 0, bytesRead); + } + data.close(); + in.close(); + buf = data.toByteArray(); + + // load the javassist handle to the raw class + String javaClassFileName = Descriptor.toJavaName(classFileName); + ClassPool classPool = new ClassPool(); + classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, buf)); + CtClass c = classPool.get(javaClassFileName); + + c = transformClass(c); + + // sanity checking + assertClassName(c, deobfClassEntry); + + // DEBUG + Util.writeClass( c ); + + // we have a transformed class! + return c.toBytecode(); + } catch (IOException | NotFoundException | CannotCompileException ex) { + throw new Error(ex); + } + } + + public CtClass transformClass(CtClass c) throws IOException, NotFoundException, CannotCompileException { + // we moved a lot of classes out of the default package into the none package + // make sure all the class references are consistent + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + + // reconstruct inner classes + new InnerClassWriter(m_jarIndex).write(c); + + // re-get the javassist handle since we changed class names + ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + String javaClassReconstructedName = Descriptor.toJavaName(obfClassEntry.getName()); + ClassPool classPool = new ClassPool(); + classPool.insertClassPath(new ByteArrayClassPath(javaClassReconstructedName, c.toBytecode())); + c = classPool.get(javaClassReconstructedName); + + // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong) + assertClassName(c, obfClassEntry); + + // do all kinds of deobfuscating transformations on the class + new MethodParameterWriter(m_deobfuscatingTranslator).writeMethodArguments(c); + new ClassTranslator(m_deobfuscatingTranslator).translate(c); + + return c; + } + + private void assertClassName(CtClass c, ClassEntry obfClassEntry) { + String name1 = Descriptor.toJvmName(c.getName()); + assert (name1.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name1); + + String name2 = Descriptor.toJvmName(c.getClassFile().getName()); + assert (name2.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name2); + } +} diff --git a/src/cuchaz/enigma/Util.java b/src/cuchaz/enigma/Util.java new file mode 100644 index 00000000..7f04bda0 --- /dev/null +++ b/src/cuchaz/enigma/Util.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.awt.Desktop; +import java.io.Closeable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.jar.JarFile; + +import javassist.CannotCompileException; +import javassist.CtClass; +import javassist.bytecode.Descriptor; + +import com.google.common.io.CharStreams; + +public class Util { + + public static int combineHashesOrdered(Object... objs) { + return combineHashesOrdered(Arrays.asList(objs)); + } + + public static int combineHashesOrdered(Iterable objs) { + final int prime = 67; + int result = 1; + for (Object obj : objs) { + result *= prime; + if (obj != null) { + result += obj.hashCode(); + } + } + return result; + } + + public static void closeQuietly(Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException ex) { + // just ignore any further exceptions + } + } + } + + public static void closeQuietly(JarFile jarFile) { + // silly library should implement Closeable... + if (jarFile != null) { + try { + jarFile.close(); + } catch (IOException ex) { + // just ignore any further exceptions + } + } + } + + public static String readStreamToString(InputStream in) throws IOException { + return CharStreams.toString(new InputStreamReader(in, "UTF-8")); + } + + public static String readResourceToString(String path) throws IOException { + InputStream in = Util.class.getResourceAsStream(path); + if (in == null) { + throw new IllegalArgumentException("Resource not found! " + path); + } + return readStreamToString(in); + } + + public static void openUrl(String url) { + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + try { + desktop.browse(new URI(url)); + } catch (IOException ex) { + throw new Error(ex); + } catch (URISyntaxException ex) { + throw new IllegalArgumentException(ex); + } + } + } + + public static void writeClass(CtClass c) { + String name = Descriptor.toJavaName(c.getName()); + File file = new File(name + ".class"); + try (FileOutputStream out = new FileOutputStream(file)) { + out.write(c.toBytecode()); + } catch (IOException | CannotCompileException ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/analysis/Access.java b/src/cuchaz/enigma/analysis/Access.java new file mode 100644 index 00000000..8d3409ac --- /dev/null +++ b/src/cuchaz/enigma/analysis/Access.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.lang.reflect.Modifier; + +import javassist.CtBehavior; +import javassist.CtField; + +public enum Access { + + Public, + Protected, + Private; + + public static Access get(CtBehavior behavior) { + return get(behavior.getModifiers()); + } + + public static Access get(CtField field) { + return get(field.getModifiers()); + } + + public static Access get(int modifiers) { + if (Modifier.isPublic(modifiers)) { + return Public; + } else if (Modifier.isProtected(modifiers)) { + return Protected; + } else if (Modifier.isPrivate(modifiers)) { + return Private; + } + // assume public by default + return Public; + } +} diff --git a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java new file mode 100644 index 00000000..9adac5e9 --- /dev/null +++ b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.Set; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; + +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.Translator; + +public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { + + private static final long serialVersionUID = -3658163700783307520L; + + private Translator m_deobfuscatingTranslator; + private BehaviorEntry m_entry; + private EntryReference m_reference; + private Access m_access; + + public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + m_reference = null; + } + + public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference reference, Access access) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = reference.entry; + m_reference = reference; + m_access = access; + } + + @Override + public BehaviorEntry getEntry() { + return m_entry; + } + + @Override + public EntryReference getReference() { + return m_reference; + } + + @Override + public String toString() { + if (m_reference != null) { + return String.format("%s (%s)", m_deobfuscatingTranslator.translateEntry(m_reference.context), m_access); + } + return m_deobfuscatingTranslator.translateEntry(m_entry).toString(); + } + + public void load(JarIndex index, boolean recurse) { + // get all the child nodes + for (EntryReference reference : index.getBehaviorReferences(m_entry)) { + add(new BehaviorReferenceTreeNode(m_deobfuscatingTranslator, reference, index.getAccess(m_entry))); + } + + if (recurse && children != null) { + for (Object child : children) { + if (child instanceof BehaviorReferenceTreeNode) { + BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode)child; + + // don't recurse into ancestor + Set ancestors = Sets.newHashSet(); + TreeNode n = (TreeNode)node; + while (n.getParent() != null) { + n = n.getParent(); + if (n instanceof BehaviorReferenceTreeNode) { + ancestors.add( ((BehaviorReferenceTreeNode)n).getEntry()); + } + } + if (ancestors.contains(node.getEntry())) { + continue; + } + + node.load(index, true); + } + } + } + } +} diff --git a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java new file mode 100644 index 00000000..49aac5f0 --- /dev/null +++ b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 3112703459157851912L; + + private Translator m_deobfuscatingTranslator; + private ClassEntry m_entry; + + public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + } + + public ClassEntry getClassEntry() { + return m_entry; + } + + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_entry.getClassName()); + } + + @Override + public String toString() { + String className = getDeobfClassName(); + if (className == null) { + className = m_entry.getClassName(); + } + return className; + } + + public void load(JarIndex index) { + // get all method implementations + List nodes = Lists.newArrayList(); + for (String implementingClassName : index.getImplementingClasses(m_entry.getClassName())) { + nodes.add(new ClassImplementationsTreeNode(m_deobfuscatingTranslator, new ClassEntry(implementingClassName))); + } + + // add them to this node + for (ClassImplementationsTreeNode node : nodes) { + this.add(node); + } + } + + public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { + // is this the node? + if (node.m_entry.equals(entry)) { + return node; + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode)node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } +} diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java new file mode 100644 index 00000000..3eaa3912 --- /dev/null +++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Translator; + +public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 4432367405826178490L; + + private Translator m_deobfuscatingTranslator; + private String m_obfClassName; + + public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_obfClassName = obfClassName; + } + + public String getObfClassName() { + return m_obfClassName; + } + + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_obfClassName); + } + + @Override + public String toString() { + String deobfClassName = getDeobfClassName(); + if (deobfClassName != null) { + return deobfClassName; + } + return m_obfClassName; + } + + public void load(TranslationIndex ancestries, boolean recurse) { + // get all the child nodes + List nodes = Lists.newArrayList(); + for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(m_obfClassName))) { + nodes.add(new ClassInheritanceTreeNode(m_deobfuscatingTranslator, subclassEntry.getName())); + } + + // add them to this node + for (ClassInheritanceTreeNode node : nodes) { + this.add(node); + } + + if (recurse) { + for (ClassInheritanceTreeNode node : nodes) { + node.load(ancestries, true); + } + } + } + + public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { + // is this the node? + if (node.getObfClassName().equals(entry.getName())) { + return node; + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode)node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } +} diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java new file mode 100644 index 00000000..bb611df5 --- /dev/null +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.Arrays; +import java.util.List; + +import cuchaz.enigma.Util; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; + +public class EntryReference { + + private static final List ConstructorNonNames = Arrays.asList("this", "super", "static"); + public E entry; + public C context; + + private boolean m_isNamed; + + public EntryReference(E entry, String sourceName) { + this(entry, sourceName, null); + } + + public EntryReference(E entry, String sourceName, C context) { + if (entry == null) { + throw new IllegalArgumentException("Entry cannot be null!"); + } + + this.entry = entry; + this.context = context; + + m_isNamed = sourceName != null && sourceName.length() > 0; + if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) { + m_isNamed = false; + } + } + + public EntryReference(E entry, C context, EntryReference other) { + this.entry = entry; + this.context = context; + m_isNamed = other.m_isNamed; + } + + public ClassEntry getLocationClassEntry() { + if (context != null) { + return context.getClassEntry(); + } + return entry.getClassEntry(); + } + + public boolean isNamed() { + return m_isNamed; + } + + public Entry getNameableEntry() { + if (entry instanceof ConstructorEntry) { + // renaming a constructor really means renaming the class + return entry.getClassEntry(); + } + return entry; + } + + public String getNamableName() { + if (getNameableEntry() instanceof ClassEntry) { + ClassEntry classEntry = (ClassEntry)getNameableEntry(); + if (classEntry.isInnerClass()) { + // make sure we only rename the inner class name + return classEntry.getInnerClassName(); + } + } + + return getNameableEntry().getName(); + } + + @Override + public int hashCode() { + if (context != null) { + return Util.combineHashesOrdered(entry.hashCode(), context.hashCode()); + } + return entry.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof EntryReference) { + return equals((EntryReference)other); + } + return false; + } + + public boolean equals(EntryReference other) { + // check entry first + boolean isEntrySame = entry.equals(other.entry); + if (!isEntrySame) { + return false; + } + + // check caller + if (context == null && other.context == null) { + return true; + } else if (context != null && other.context != null) { + return context.equals(other.context); + } + return false; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(entry); + if (context != null) { + buf.append(" called from "); + buf.append(context); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java new file mode 100644 index 00000000..b54489cd --- /dev/null +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.AbstractMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class EntryRenamer { + + public static void renameClassesInSet(Map renames, Set set) { + List entries = Lists.newArrayList(); + for (T val : set) { + entries.add(renameClassesInThing(renames, val)); + } + set.clear(); + set.addAll(entries); + } + + public static void renameClassesInMap(Map renames, Map map) { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for (Map.Entry entry : map.entrySet()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameClassesInThing(renames, entry.getKey()), + renameClassesInThing(renames, entry.getValue()) + )); + } + map.clear(); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); + } + } + + public static void renameClassesInMultimap(Map renames, Multimap map) { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for (Map.Entry entry : map.entries()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameClassesInThing(renames, entry.getKey()), + renameClassesInThing(renames, entry.getValue()) + )); + } + map.clear(); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); + } + } + + public static void renameMethodsInMultimap(Map renames, Multimap map) { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for (Map.Entry entry : map.entries()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameMethodsInThing(renames, entry.getKey()), + renameMethodsInThing(renames, entry.getValue()) + )); + } + map.clear(); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); + } + } + + public static void renameMethodsInMap(Map renames, Map map) { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for (Map.Entry entry : map.entrySet()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameMethodsInThing(renames, entry.getKey()), + renameMethodsInThing(renames, entry.getValue()) + )); + } + map.clear(); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); + } + } + + @SuppressWarnings("unchecked") + public static T renameMethodsInThing(Map renames, T thing) { + if (thing instanceof MethodEntry) { + MethodEntry methodEntry = (MethodEntry)thing; + MethodEntry newMethodEntry = renames.get(methodEntry); + if (newMethodEntry != null) { + return (T)new MethodEntry( + methodEntry.getClassEntry(), + newMethodEntry.getName(), + methodEntry.getSignature() + ); + } + return thing; + } else if (thing instanceof ArgumentEntry) { + ArgumentEntry argumentEntry = (ArgumentEntry)thing; + return (T)new ArgumentEntry( + renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()), + argumentEntry.getIndex(), + argumentEntry.getName() + ); + } else if (thing instanceof EntryReference) { + EntryReference reference = (EntryReference)thing; + reference.entry = renameMethodsInThing(renames, reference.entry); + reference.context = renameMethodsInThing(renames, reference.context); + return thing; + } + return thing; + } + + @SuppressWarnings("unchecked") + public static T renameClassesInThing(Map renames, T thing) { + if (thing instanceof String) { + String stringEntry = (String)thing; + if (renames.containsKey(stringEntry)) { + return (T)renames.get(stringEntry); + } + } else if (thing instanceof ClassEntry) { + ClassEntry classEntry = (ClassEntry)thing; + return (T)new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); + } else if (thing instanceof FieldEntry) { + FieldEntry fieldEntry = (FieldEntry)thing; + return (T)new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName()); + } else if (thing instanceof ConstructorEntry) { + ConstructorEntry constructorEntry = (ConstructorEntry)thing; + return (T)new ConstructorEntry( + renameClassesInThing(renames, constructorEntry.getClassEntry()), + constructorEntry.getSignature() + ); + } else if (thing instanceof MethodEntry) { + MethodEntry methodEntry = (MethodEntry)thing; + return (T)new MethodEntry( + renameClassesInThing(renames, methodEntry.getClassEntry()), + methodEntry.getName(), + methodEntry.getSignature() + ); + } else if (thing instanceof ArgumentEntry) { + ArgumentEntry argumentEntry = (ArgumentEntry)thing; + return (T)new ArgumentEntry( + renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), + argumentEntry.getIndex(), + argumentEntry.getName() + ); + } else if (thing instanceof EntryReference) { + EntryReference reference = (EntryReference)thing; + reference.entry = renameClassesInThing(renames, reference.entry); + reference.context = renameClassesInThing(renames, reference.context); + return thing; + } + + return thing; + } +} diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java new file mode 100644 index 00000000..2173eea6 --- /dev/null +++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import javax.swing.tree.DefaultMutableTreeNode; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.Translator; + +public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { + + private static final long serialVersionUID = -7934108091928699835L; + + private Translator m_deobfuscatingTranslator; + private FieldEntry m_entry; + private EntryReference m_reference; + private Access m_access; + + public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + m_reference = null; + } + + private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference reference, Access access) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = reference.entry; + m_reference = reference; + m_access = access; + } + + @Override + public FieldEntry getEntry() { + return m_entry; + } + + @Override + public EntryReference getReference() { + return m_reference; + } + + @Override + public String toString() { + if (m_reference != null) { + return String.format("%s (%s)", m_deobfuscatingTranslator.translateEntry(m_reference.context), m_access); + } + return m_deobfuscatingTranslator.translateEntry(m_entry).toString(); + } + + public void load(JarIndex index, boolean recurse) { + // get all the child nodes + if (m_reference == null) { + for (EntryReference reference : index.getFieldReferences(m_entry)) { + add(new FieldReferenceTreeNode(m_deobfuscatingTranslator, reference, index.getAccess(m_entry))); + } + } else { + for (EntryReference reference : index.getBehaviorReferences(m_reference.context)) { + add(new BehaviorReferenceTreeNode(m_deobfuscatingTranslator, reference, index.getAccess(m_reference.context))); + } + } + + if (recurse && children != null) { + for (Object node : children) { + if (node instanceof BehaviorReferenceTreeNode) { + ((BehaviorReferenceTreeNode)node).load(index, true); + } else if (node instanceof FieldReferenceTreeNode) { + ((FieldReferenceTreeNode)node).load(index, true); + } + } + } + } +} diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java new file mode 100644 index 00000000..72a99122 --- /dev/null +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import javassist.ByteArrayClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.Descriptor; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.mapping.ClassEntry; + +public class JarClassIterator implements Iterator { + + private JarFile m_jar; + private Iterator m_iter; + + public JarClassIterator(JarFile jar) { + m_jar = jar; + + // get the jar entries that correspond to classes + List classEntries = Lists.newArrayList(); + Enumeration entries = m_jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + + // is this a class file? + if (entry.getName().endsWith(".class")) { + classEntries.add(entry); + } + } + m_iter = classEntries.iterator(); + } + + @Override + public boolean hasNext() { + return m_iter.hasNext(); + } + + @Override + public CtClass next() { + JarEntry entry = m_iter.next(); + try { + return getClass(m_jar, entry); + } catch (IOException | NotFoundException ex) { + throw new Error("Unable to load class: " + entry.getName()); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + public static List getClassEntries(JarFile jar) { + List classEntries = Lists.newArrayList(); + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + + // is this a class file? + if (!entry.isDirectory() && entry.getName().endsWith(".class")) { + classEntries.add(getClassEntry(entry)); + } + } + return classEntries; + } + + public static Iterable classes(final JarFile jar) { + return new Iterable() { + @Override + public Iterator iterator() { + return new JarClassIterator(jar); + } + }; + } + + public static CtClass getClass(JarFile jar, ClassEntry classEntry) { + try { + return getClass(jar, new JarEntry(classEntry.getName() + ".class")); + } catch (IOException | NotFoundException ex) { + throw new Error("Unable to load class: " + classEntry.getName()); + } + } + + private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException { + // read the class into a buffer + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buf = new byte[Constants.KiB]; + int totalNumBytesRead = 0; + InputStream in = jar.getInputStream(entry); + while (in.available() > 0) { + int numBytesRead = in.read(buf); + if (numBytesRead < 0) { + break; + } + bos.write(buf, 0, numBytesRead); + + // sanity checking + totalNumBytesRead += numBytesRead; + if (totalNumBytesRead > Constants.MiB) { + throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!"); + } + } + + // get a javassist handle for the class + String className = Descriptor.toJavaName(getClassEntry(entry).getName()); + ClassPool classPool = new ClassPool(); + classPool.appendSystemPath(); + classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray())); + return classPool.get(className); + } + + private static ClassEntry getClassEntry(JarEntry entry) { + return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length())); + } +} diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java new file mode 100644 index 00000000..c96d3bc4 --- /dev/null +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -0,0 +1,828 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarFile; + +import javassist.CannotCompileException; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.CtMethod; +import javassist.NotFoundException; +import javassist.bytecode.AccessFlag; +import javassist.bytecode.Descriptor; +import javassist.bytecode.FieldInfo; +import javassist.expr.ConstructorCall; +import javassist.expr.ExprEditor; +import javassist.expr.FieldAccess; +import javassist.expr.MethodCall; +import javassist.expr.NewExpr; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.bytecode.ClassRenamer; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.SignatureUpdater; +import cuchaz.enigma.mapping.Translator; + +public class JarIndex { + + private Set m_obfClassEntries; + private TranslationIndex m_translationIndex; + private Multimap m_interfaces; + private Map m_access; + private Map m_fieldClasses; + private Multimap m_methodImplementations; + private Multimap> m_behaviorReferences; + private Multimap> m_fieldReferences; + private Multimap m_innerClasses; + private Map m_outerClasses; + private Map m_anonymousClasses; + private Map m_bridgeMethods; + + public JarIndex() { + m_obfClassEntries = Sets.newHashSet(); + m_translationIndex = new TranslationIndex(); + m_interfaces = HashMultimap.create(); + m_access = Maps.newHashMap(); + m_fieldClasses = Maps.newHashMap(); + m_methodImplementations = HashMultimap.create(); + m_behaviorReferences = HashMultimap.create(); + m_fieldReferences = HashMultimap.create(); + m_innerClasses = HashMultimap.create(); + m_outerClasses = Maps.newHashMap(); + m_anonymousClasses = Maps.newHashMap(); + m_bridgeMethods = Maps.newHashMap(); + } + + public void indexJar(JarFile jar, boolean buildInnerClasses) { + // step 1: read the class names + for (ClassEntry classEntry : JarClassIterator.getClassEntries(jar)) { + if (classEntry.isInDefaultPackage()) { + // move out of default package + classEntry = new ClassEntry(Constants.NonePackage + "/" + classEntry.getName()); + } + m_obfClassEntries.add(classEntry); + } + + // step 2: index field/method/constructor access + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = new FieldEntry(classEntry, field.getName()); + m_access.put(fieldEntry, Access.get(field)); + } + for (CtMethod method : c.getDeclaredMethods()) { + MethodEntry methodEntry = new MethodEntry(classEntry, method.getName(), method.getSignature()); + m_access.put(methodEntry, Access.get(method)); + } + for (CtConstructor constructor : c.getDeclaredConstructors()) { + ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, constructor.getSignature()); + m_access.put(constructorEntry, Access.get(constructor)); + } + } + + // step 3: index extends, implements, fields, and methods + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + m_translationIndex.indexClass(c); + String className = Descriptor.toJvmName(c.getName()); + for (String interfaceName : c.getClassFile().getInterfaces()) { + className = Descriptor.toJvmName(className); + interfaceName = Descriptor.toJvmName(interfaceName); + if (className.equals(interfaceName)) { + throw new IllegalArgumentException("Class cannot be its own interface! " + className); + } + m_interfaces.put(className, interfaceName); + } + for (CtField field : c.getDeclaredFields()) { + indexField(field); + } + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + indexBehavior(behavior); + } + } + + // step 4: index field, method, constructor references + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + indexBehaviorReferences(behavior); + } + } + + if (buildInnerClasses) { + // step 5: index inner classes and anonymous classes + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + String outerClassName = findOuterClass(c); + if (outerClassName != null) { + String innerClassName = c.getSimpleName(); + m_innerClasses.put(outerClassName, innerClassName); + boolean innerWasAdded = m_outerClasses.put(innerClassName, outerClassName) == null; + assert (innerWasAdded); + + BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassName); + if (enclosingBehavior != null) { + m_anonymousClasses.put(innerClassName, enclosingBehavior); + + // DEBUG + // System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); + } else { + // DEBUG + // System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); + } + } + } + + // step 6: update other indices with inner class info + Map renames = Maps.newHashMap(); + for (Map.Entry entry : m_outerClasses.entrySet()) { + renames.put(Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey()); + } + EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); + m_translationIndex.renameClasses(renames); + EntryRenamer.renameClassesInMultimap(renames, m_interfaces); + EntryRenamer.renameClassesInMultimap(renames, m_methodImplementations); + EntryRenamer.renameClassesInMultimap(renames, m_behaviorReferences); + EntryRenamer.renameClassesInMultimap(renames, m_fieldReferences); + EntryRenamer.renameClassesInMap(renames, m_bridgeMethods); + EntryRenamer.renameClassesInMap(renames, m_access); + } + + // step 6: update other indices with bridge method info + EntryRenamer.renameMethodsInMultimap(m_bridgeMethods, m_methodImplementations); + EntryRenamer.renameMethodsInMultimap(m_bridgeMethods, m_behaviorReferences); + EntryRenamer.renameMethodsInMultimap(m_bridgeMethods, m_fieldReferences); + EntryRenamer.renameMethodsInMap(m_bridgeMethods, m_access); + } + + private void indexField(CtField field) { + // get the field entry + String className = Descriptor.toJvmName(field.getDeclaringClass().getName()); + FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName()); + + // is the field a class type? + if (field.getSignature().startsWith("L")) { + ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1)); + m_fieldClasses.put(fieldEntry, fieldTypeEntry); + } + } + + private void indexBehavior(CtBehavior behavior) { + // get the behavior entry + final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + if (behaviorEntry instanceof MethodEntry) { + MethodEntry methodEntry = (MethodEntry)behaviorEntry; + + // index implementation + m_methodImplementations.put(behaviorEntry.getClassName(), methodEntry); + + // look for bridge methods + CtMethod bridgedMethod = getBridgedMethod((CtMethod)behavior); + if (bridgedMethod != null) { + MethodEntry bridgedMethodEntry = new MethodEntry( + behaviorEntry.getClassEntry(), + bridgedMethod.getName(), + bridgedMethod.getSignature() + ); + m_bridgeMethods.put(bridgedMethodEntry, methodEntry); + } + } + // looks like we don't care about constructors here + } + + private void indexBehaviorReferences(CtBehavior behavior) { + // index method calls + final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + try { + behavior.instrument(new ExprEditor() { + @Override + public void edit(MethodCall call) { + MethodEntry calledMethodEntry = new MethodEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + call.getMethodName(), + call.getSignature() + ); + ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledMethodEntry); + if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { + calledMethodEntry = new MethodEntry( + resolvedClassEntry, + call.getMethodName(), + call.getSignature() + ); + } + EntryReference reference = new EntryReference( + calledMethodEntry, + call.getMethodName(), + behaviorEntry + ); + m_behaviorReferences.put(calledMethodEntry, reference); + } + + @Override + public void edit(FieldAccess call) { + FieldEntry calledFieldEntry = new FieldEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + call.getFieldName() + ); + ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry); + if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { + calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName()); + } + EntryReference reference = new EntryReference( + calledFieldEntry, + call.getFieldName(), + behaviorEntry + ); + m_fieldReferences.put(calledFieldEntry, reference); + } + + @Override + public void edit(ConstructorCall call) { + ConstructorEntry calledConstructorEntry = new ConstructorEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + call.getSignature() + ); + EntryReference reference = new EntryReference( + calledConstructorEntry, + call.getMethodName(), + behaviorEntry + ); + m_behaviorReferences.put(calledConstructorEntry, reference); + } + + @Override + public void edit(NewExpr call) { + ConstructorEntry calledConstructorEntry = new ConstructorEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + call.getSignature() + ); + EntryReference reference = new EntryReference( + calledConstructorEntry, + call.getClassName(), + behaviorEntry + ); + m_behaviorReferences.put(calledConstructorEntry, reference); + } + }); + } catch (CannotCompileException ex) { + throw new Error(ex); + } + } + + private CtMethod getBridgedMethod(CtMethod method) { + + // bridge methods just call another method, cast it to the return type, and return the result + // let's see if we can detect this scenario + + // skip non-synthetic methods + if ( (method.getModifiers() & AccessFlag.SYNTHETIC) == 0) { + return null; + } + + // get all the called methods + final List methodCalls = Lists.newArrayList(); + try { + method.instrument(new ExprEditor() { + @Override + public void edit(MethodCall call) { + methodCalls.add(call); + } + }); + } catch (CannotCompileException ex) { + // this is stupid... we're not even compiling anything + throw new Error(ex); + } + + // is there just one? + if (methodCalls.size() != 1) { + return null; + } + MethodCall call = methodCalls.get(0); + + try { + // we have a bridge method! + return call.getMethod(); + } catch (NotFoundException ex) { + // can't find the type? not a bridge method + return null; + } + } + + private String findOuterClass(CtClass c) { + + // inner classes: + // have constructors that can (illegally) set synthetic fields + // the outer class is the only class that calls constructors + + // use the synthetic fields to find the synthetic constructors + for (CtConstructor constructor : c.getDeclaredConstructors()) { + Set syntheticFieldTypes = Sets.newHashSet(); + if (!isIllegalConstructor(syntheticFieldTypes, constructor)) { + continue; + } + + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + ConstructorEntry constructorEntry = new ConstructorEntry( + classEntry, + constructor.getMethodInfo().getDescriptor() + ); + + // gather the classes from the illegally-set synthetic fields + Set illegallySetClasses = Sets.newHashSet(); + for (String type : syntheticFieldTypes) { + if (type.startsWith("L")) { + ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1)); + if (isSaneOuterClass(outerClassEntry, classEntry)) { + illegallySetClasses.add(outerClassEntry); + } + } + } + + // who calls this constructor? + Set callerClasses = Sets.newHashSet(); + for (EntryReference reference : getBehaviorReferences(constructorEntry)) { + + // make sure it's not a call to super + if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { + + // is the entry a superclass of the context? + ClassEntry calledClassEntry = reference.entry.getClassEntry(); + ClassEntry superclassEntry = m_translationIndex.getSuperclass(reference.context.getClassEntry()); + if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) { + // it's a super call, skip + continue; + } + } + + if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) { + callerClasses.add(reference.context.getClassEntry()); + } + } + + // do we have an answer yet? + if (callerClasses.isEmpty()) { + if (illegallySetClasses.size() == 1) { + return illegallySetClasses.iterator().next().getName(); + } else { + System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); + } + } else { + if (callerClasses.size() == 1) { + return callerClasses.iterator().next().getName(); + } else { + // multiple callers, do the illegally set classes narrow it down? + Set intersection = Sets.newHashSet(callerClasses); + intersection.retainAll(illegallySetClasses); + if (intersection.size() == 1) { + return intersection.iterator().next().getName(); + } else { + System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); + } + } + } + } + + return null; + } + + private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { + + // clearly this would be silly + if (outerClassEntry.equals(innerClassEntry)) { + return false; + } + + // is the outer class in the jar? + if (!m_obfClassEntries.contains(outerClassEntry)) { + return false; + } + + return true; + } + + @SuppressWarnings("unchecked") + private boolean isIllegalConstructor(Set syntheticFieldTypes, CtConstructor constructor) { + + // illegal constructors only set synthetic member fields, then call super() + String className = constructor.getDeclaringClass().getName(); + + // collect all the field accesses, constructor calls, and method calls + final List illegalFieldWrites = Lists.newArrayList(); + final List constructorCalls = Lists.newArrayList(); + try { + constructor.instrument(new ExprEditor() { + @Override + public void edit(FieldAccess fieldAccess) { + if (fieldAccess.isWriter() && constructorCalls.isEmpty()) { + illegalFieldWrites.add(fieldAccess); + } + } + + @Override + public void edit(ConstructorCall constructorCall) { + constructorCalls.add(constructorCall); + } + }); + } catch (CannotCompileException ex) { + // we're not compiling anything... this is stupid + throw new Error(ex); + } + + // are there any illegal field writes? + if (illegalFieldWrites.isEmpty()) { + return false; + } + + // are all the writes to synthetic fields? + for (FieldAccess fieldWrite : illegalFieldWrites) { + + // all illegal writes have to be to the local class + if (!fieldWrite.getClassName().equals(className)) { + System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName())); + return false; + } + + // find the field + FieldInfo fieldInfo = null; + for (FieldInfo info : (List)constructor.getDeclaringClass().getClassFile().getFields()) { + if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) { + fieldInfo = info; + break; + } + } + if (fieldInfo == null) { + // field is in a superclass or something, can't be a local synthetic member + return false; + } + + // is this field synthetic? + boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; + if (isSynthetic) { + syntheticFieldTypes.add(fieldInfo.getDescriptor()); + } else { + System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName())); + return false; + } + } + + // we passed all the tests! + return true; + } + + private BehaviorEntry isAnonymousClass(CtClass c, String outerClassName) { + + ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + + // anonymous classes: + // can't be abstract + // have only one constructor + // it's called exactly once by the outer class + // the type the instance is assigned to can't be this type + + // is abstract? + if (Modifier.isAbstract(c.getModifiers())) { + return null; + } + + // is there exactly one constructor? + if (c.getDeclaredConstructors().length != 1) { + return null; + } + CtConstructor constructor = c.getDeclaredConstructors()[0]; + + // is this constructor called exactly once? + ConstructorEntry constructorEntry = new ConstructorEntry( + innerClassEntry, + constructor.getMethodInfo().getDescriptor() + ); + Collection> references = getBehaviorReferences(constructorEntry); + if (references.size() != 1) { + return null; + } + + // does the caller use this type? + BehaviorEntry caller = references.iterator().next().context; + for (FieldEntry fieldEntry : getReferencedFields(caller)) { + ClassEntry fieldClass = getFieldClass(fieldEntry); + if (fieldClass != null && fieldClass.equals(innerClassEntry)) { + // caller references this type, so it can't be anonymous + return null; + } + } + for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) { + // get the class types from the signature + for (String className : SignatureUpdater.getClasses(behaviorEntry.getSignature())) { + if (className.equals(innerClassEntry.getName())) { + // caller references this type, so it can't be anonymous + return null; + } + } + } + + return caller; + } + + public Set getObfClassEntries() { + return m_obfClassEntries; + } + + public TranslationIndex getTranslationIndex() { + return m_translationIndex; + } + + public Access getAccess(Entry entry) { + return m_access.get(entry); + } + + public ClassEntry getFieldClass(FieldEntry fieldEntry) { + return m_fieldClasses.get(fieldEntry); + } + + public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { + + // get the root node + List ancestry = Lists.newArrayList(); + ancestry.add(obfClassEntry.getName()); + for (ClassEntry classEntry : m_translationIndex.getAncestry(obfClassEntry)) { + ancestry.add(classEntry.getName()); + } + ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( + deobfuscatingTranslator, + ancestry.get(ancestry.size() - 1) + ); + + // expand all children recursively + rootNode.load(m_translationIndex, true); + + return rootNode; + } + + public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { + + // is this even an interface? + if (isInterface(obfClassEntry.getClassName())) { + ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry); + node.load(this); + return node; + } + return null; + } + + public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { + + // travel to the ancestor implementation + ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); + for (ClassEntry ancestorClassEntry : m_translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { + MethodEntry ancestorMethodEntry = new MethodEntry( + new ClassEntry(ancestorClassEntry), + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + if (containsObfBehavior(ancestorMethodEntry)) { + baseImplementationClassEntry = ancestorClassEntry; + } + } + + // make a root node at the base + MethodEntry methodEntry = new MethodEntry( + baseImplementationClassEntry, + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( + deobfuscatingTranslator, + methodEntry, + containsObfBehavior(methodEntry) + ); + + // expand the full tree + rootNode.load(this, true); + + return rootNode; + } + + public MethodImplementationsTreeNode getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { + + MethodEntry interfaceMethodEntry; + + // is this method on an interface? + if (isInterface(obfMethodEntry.getClassName())) { + interfaceMethodEntry = obfMethodEntry; + } else { + // get the interface class + List methodInterfaces = Lists.newArrayList(); + for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) { + // is this method defined in this interface? + MethodEntry methodInterface = new MethodEntry( + new ClassEntry(interfaceName), + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + if (containsObfBehavior(methodInterface)) { + methodInterfaces.add(methodInterface); + } + } + if (methodInterfaces.isEmpty()) { + return null; + } + if (methodInterfaces.size() > 1) { + throw new Error("Too many interfaces define this method! This is not yet supported by Enigma!"); + } + interfaceMethodEntry = methodInterfaces.get(0); + } + + MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); + rootNode.load(this); + return rootNode; + } + + public Set getRelatedMethodImplementations(MethodEntry obfMethodEntry) { + Set methodEntries = Sets.newHashSet(); + getRelatedMethodImplementations(methodEntries, getMethodInheritance(null, obfMethodEntry)); + return methodEntries; + } + + private void getRelatedMethodImplementations(Set methodEntries, MethodInheritanceTreeNode node) { + MethodEntry methodEntry = node.getMethodEntry(); + if (containsObfBehavior(methodEntry)) { + // collect the entry + methodEntries.add(methodEntry); + } + + // look at interface methods too + MethodImplementationsTreeNode implementations = getMethodImplementations(null, methodEntry); + if (implementations != null) { + getRelatedMethodImplementations(methodEntries, implementations); + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode)node.getChildAt(i)); + } + } + + private void getRelatedMethodImplementations(Set methodEntries, MethodImplementationsTreeNode node) { + MethodEntry methodEntry = node.getMethodEntry(); + if (containsObfBehavior(methodEntry)) { + // collect the entry + methodEntries.add(methodEntry); + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode)node.getChildAt(i)); + } + } + + public Collection> getFieldReferences(FieldEntry fieldEntry) { + return m_fieldReferences.get(fieldEntry); + } + + public Collection getReferencedFields(BehaviorEntry behaviorEntry) { + // linear search is fast enough for now + Set fieldEntries = Sets.newHashSet(); + for (EntryReference reference : m_fieldReferences.values()) { + if (reference.context == behaviorEntry) { + fieldEntries.add(reference.entry); + } + } + return fieldEntries; + } + + public Collection> getBehaviorReferences(BehaviorEntry behaviorEntry) { + return m_behaviorReferences.get(behaviorEntry); + } + + public Collection getReferencedBehaviors(BehaviorEntry behaviorEntry) { + // linear search is fast enough for now + Set behaviorEntries = Sets.newHashSet(); + for (EntryReference reference : m_behaviorReferences.values()) { + if (reference.context == behaviorEntry) { + behaviorEntries.add(reference.entry); + } + } + return behaviorEntries; + } + + public Collection getInnerClasses(String obfOuterClassName) { + return m_innerClasses.get(obfOuterClassName); + } + + public String getOuterClass(String obfInnerClassName) { + // make sure we use the right name + if (new ClassEntry(obfInnerClassName).getPackageName() != null) { + throw new IllegalArgumentException("Don't reference obfuscated inner classes using packages: " + obfInnerClassName); + } + return m_outerClasses.get(obfInnerClassName); + } + + public boolean isAnonymousClass(String obfInnerClassName) { + return m_anonymousClasses.containsKey(obfInnerClassName); + } + + public BehaviorEntry getAnonymousClassCaller(String obfInnerClassName) { + return m_anonymousClasses.get(obfInnerClassName); + } + + public Set getInterfaces(String className) { + Set interfaceNames = new HashSet(); + interfaceNames.addAll(m_interfaces.get(className)); + for (ClassEntry ancestor : m_translationIndex.getAncestry(new ClassEntry(className))) { + interfaceNames.addAll(m_interfaces.get(ancestor.getName())); + } + return interfaceNames; + } + + public Set getImplementingClasses(String targetInterfaceName) { + // linear search is fast enough for now + Set classNames = Sets.newHashSet(); + for (Map.Entry entry : m_interfaces.entries()) { + String className = entry.getKey(); + String interfaceName = entry.getValue(); + if (interfaceName.equals(targetInterfaceName)) { + classNames.add(className); + m_translationIndex.getSubclassNamesRecursively(classNames, new ClassEntry(className)); + } + } + return classNames; + } + + public boolean isInterface(String className) { + return m_interfaces.containsValue(className); + } + + public MethodEntry getBridgeMethod(MethodEntry methodEntry) { + return m_bridgeMethods.get(methodEntry); + } + + public boolean containsObfClass(ClassEntry obfClassEntry) { + return m_obfClassEntries.contains(obfClassEntry); + } + + public boolean containsObfField(FieldEntry obfFieldEntry) { + return m_access.containsKey(obfFieldEntry); + } + + public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { + return m_access.containsKey(obfBehaviorEntry); + } + + public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { + // check the behavior + if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { + return false; + } + + // check the argument + if (obfArgumentEntry.getIndex() >= Descriptor.numOfParameters(obfArgumentEntry.getBehaviorEntry().getSignature())) { + return false; + } + + return true; + } + + public boolean containsObfEntry(Entry obfEntry) { + if (obfEntry instanceof ClassEntry) { + return containsObfClass((ClassEntry)obfEntry); + } else if (obfEntry instanceof FieldEntry) { + return containsObfField((FieldEntry)obfEntry); + } else if (obfEntry instanceof BehaviorEntry) { + return containsObfBehavior((BehaviorEntry)obfEntry); + } else if (obfEntry instanceof ArgumentEntry) { + return containsObfArgument((ArgumentEntry)obfEntry); + } else { + throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); + } + } +} diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java new file mode 100644 index 00000000..10092268 --- /dev/null +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 3781080657461899915L; + + private Translator m_deobfuscatingTranslator; + private MethodEntry m_entry; + + public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { + if (entry == null) { + throw new IllegalArgumentException("entry cannot be null!"); + } + + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + } + + public MethodEntry getMethodEntry() { + return m_entry; + } + + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_entry.getClassName()); + } + + public String getDeobfMethodName() { + return m_deobfuscatingTranslator.translate(m_entry); + } + + @Override + public String toString() { + String className = getDeobfClassName(); + if (className == null) { + className = m_entry.getClassName(); + } + + String methodName = getDeobfMethodName(); + if (methodName == null) { + methodName = m_entry.getName(); + } + return className + "." + methodName + "()"; + } + + public void load(JarIndex index) { + // get all method implementations + List nodes = Lists.newArrayList(); + for (String implementingClassName : index.getImplementingClasses(m_entry.getClassName())) { + MethodEntry methodEntry = new MethodEntry( + new ClassEntry(implementingClassName), + m_entry.getName(), + m_entry.getSignature() + ); + if (index.containsObfBehavior(methodEntry)) { + nodes.add(new MethodImplementationsTreeNode(m_deobfuscatingTranslator, methodEntry)); + } + } + + // add them to this node + for (MethodImplementationsTreeNode node : nodes) { + this.add(node); + } + } + + public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) { + // is this the node? + if (node.getMethodEntry().equals(entry)) { + return node; + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode)node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } +} diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java new file mode 100644 index 00000000..87182204 --- /dev/null +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 1096677030991810007L; + + private Translator m_deobfuscatingTranslator; + private MethodEntry m_entry; + private boolean m_isImplemented; + + public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + m_isImplemented = isImplemented; + } + + public MethodEntry getMethodEntry() { + return m_entry; + } + + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_entry.getClassName()); + } + + public String getDeobfMethodName() { + return m_deobfuscatingTranslator.translate(m_entry); + } + + public boolean isImplemented() { + return m_isImplemented; + } + + @Override + public String toString() { + String className = getDeobfClassName(); + if (className == null) { + className = m_entry.getClassName(); + } + + if (!m_isImplemented) { + return className; + } else { + String methodName = getDeobfMethodName(); + if (methodName == null) { + methodName = m_entry.getName(); + } + return className + "." + methodName + "()"; + } + } + + public void load(JarIndex index, boolean recurse) { + // get all the child nodes + List nodes = Lists.newArrayList(); + for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(m_entry.getClassEntry())) { + MethodEntry methodEntry = new MethodEntry( + subclassEntry, + m_entry.getName(), + m_entry.getSignature() + ); + nodes.add(new MethodInheritanceTreeNode( + m_deobfuscatingTranslator, + methodEntry, + index.containsObfBehavior(methodEntry) + )); + } + + // add them to this node + for (MethodInheritanceTreeNode node : nodes) { + this.add(node); + } + + if (recurse) { + for (MethodInheritanceTreeNode node : nodes) { + node.load(index, true); + } + } + } + + public static MethodInheritanceTreeNode findNode(MethodInheritanceTreeNode node, MethodEntry entry) { + // is this the node? + if (node.getMethodEntry().equals(entry)) { + return node; + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode)node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } +} diff --git a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java new file mode 100644 index 00000000..2b08616a --- /dev/null +++ b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import cuchaz.enigma.mapping.Entry; + +public interface ReferenceTreeNode { + E getEntry(); + EntryReference getReference(); +} diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java new file mode 100644 index 00000000..b43ab614 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.strobel.decompiler.languages.Region; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.Identifier; + +import cuchaz.enigma.mapping.Entry; + +public class SourceIndex { + + private String m_source; + private TreeMap> m_tokenToReference; + private Multimap,Token> m_referenceToTokens; + private Map m_declarationToToken; + private List m_lineOffsets; + + public SourceIndex(String source) { + m_source = source; + m_tokenToReference = Maps.newTreeMap(); + m_referenceToTokens = HashMultimap.create(); + m_declarationToToken = Maps.newHashMap(); + m_lineOffsets = Lists.newArrayList(); + + // count the lines + m_lineOffsets.add(0); + for (int i = 0; i < source.length(); i++) { + if (source.charAt(i) == '\n') { + m_lineOffsets.add(i + 1); + } + } + } + + public String getSource() { + return m_source; + } + + public Token getToken(AstNode node) { + + // get the text of the node + String name = ""; + if (node instanceof Identifier) { + name = ((Identifier)node).getName(); + } + + // get a token for this node's region + Region region = node.getRegion(); + if (region.getBeginLine() == 0 || region.getEndLine() == 0) { + // DEBUG + System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region)); + return null; + } + Token token = new Token( + toPos(region.getBeginLine(), region.getBeginColumn()), + toPos(region.getEndLine(), region.getEndColumn()), + m_source + ); + if (token.start == 0) { + // DEBUG + System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region)); + return null; + } + + // DEBUG + // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); + + // for tokens representing inner classes, make sure we only get the simple name + int pos = name.lastIndexOf('$'); + if (pos >= 0) { + token.end -= pos + 1; + } + + return token; + } + + public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { + Token token = getToken(node); + if (token != null) { + EntryReference deobfReference = new EntryReference(deobfEntry, token.text, deobfContext); + m_tokenToReference.put(token, deobfReference); + m_referenceToTokens.put(deobfReference, token); + } + } + + public void addDeclaration(AstNode node, Entry deobfEntry) { + Token token = getToken(node); + if (token != null) { + EntryReference reference = new EntryReference(deobfEntry, token.text); + m_tokenToReference.put(token, reference); + m_referenceToTokens.put(reference, token); + m_declarationToToken.put(deobfEntry, token); + } + } + + public Token getReferenceToken(int pos) { + Token token = m_tokenToReference.floorKey(new Token(pos, pos, null)); + if (token != null && token.contains(pos)) { + return token; + } + return null; + } + + public Collection getReferenceTokens(EntryReference deobfReference) { + return m_referenceToTokens.get(deobfReference); + } + + public EntryReference getDeobfReference(Token token) { + if (token == null) { + return null; + } + return m_tokenToReference.get(token); + } + + public void replaceDeobfReference(Token token, EntryReference newDeobfReference) { + EntryReference oldDeobfReference = m_tokenToReference.get(token); + m_tokenToReference.put(token, newDeobfReference); + Collection tokens = m_referenceToTokens.get(oldDeobfReference); + m_referenceToTokens.removeAll(oldDeobfReference); + m_referenceToTokens.putAll(newDeobfReference, tokens); + } + + public Iterable referenceTokens() { + return m_tokenToReference.keySet(); + } + + public Iterable declarationTokens() { + return m_declarationToToken.values(); + } + + public Token getDeclarationToken(Entry deobfEntry) { + return m_declarationToToken.get(deobfEntry); + } + + public int getLineNumber(int pos) { + // line number is 1-based + int line = 0; + for (Integer offset : m_lineOffsets) { + if (offset > pos) { + break; + } + line++; + } + return line; + } + + public int getColumnNumber(int pos) { + // column number is 1-based + return pos - m_lineOffsets.get(getLineNumber(pos) - 1) + 1; + } + + private int toPos(int line, int col) { + // line and col are 1-based + return m_lineOffsets.get(line - 1) + col - 1; + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java new file mode 100644 index 00000000..43c17499 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.strobel.assembler.metadata.MemberReference; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.MethodReference; +import com.strobel.assembler.metadata.ParameterDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.decompiler.languages.TextLocation; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { + + private BehaviorEntry m_behaviorEntry; + + public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) { + m_behaviorEntry = behaviorEntry; + } + + @Override + public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + + // get the behavior entry + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + BehaviorEntry behaviorEntry = null; + if (ref instanceof MethodReference) { + MethodReference methodRef = (MethodReference)ref; + if (methodRef.isConstructor()) { + behaviorEntry = new ConstructorEntry(classEntry, ref.getSignature()); + } else if (methodRef.isTypeInitializer()) { + behaviorEntry = new ConstructorEntry(classEntry); + } else { + behaviorEntry = new MethodEntry(classEntry, ref.getName(), ref.getSignature()); + } + } + if (behaviorEntry != null) { + // get the node for the token + AstNode tokenNode = null; + if (node.getTarget() instanceof MemberReferenceExpression) { + tokenNode = ((MemberReferenceExpression)node.getTarget()).getMemberNameToken(); + } else if (node.getTarget() instanceof SuperReferenceExpression) { + tokenNode = node.getTarget(); + } else if (node.getTarget() instanceof ThisReferenceExpression) { + tokenNode = node.getTarget(); + } + if (tokenNode != null) { + index.addReference(tokenNode, behaviorEntry, m_behaviorEntry); + } + } + + return recurse(node, index); + } + + @Override + public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { + // make sure this is actually a field + if (ref.getSignature().indexOf('(') >= 0) { + throw new Error("Expected a field here! got " + ref); + } + + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName()); + index.addReference(node.getMemberNameToken(), fieldEntry, m_behaviorEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitSimpleType(SimpleType node, SourceIndex index) { + TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); + if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { + ClassEntry classEntry = new ClassEntry(ref.getInternalName()); + index.addReference(node.getIdentifierToken(), classEntry, m_behaviorEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { + ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + MethodDefinition methodDef = (MethodDefinition)def.getMethod(); + BehaviorEntry behaviorEntry; + if (methodDef.isConstructor()) { + behaviorEntry = new ConstructorEntry(classEntry, methodDef.getSignature()); + } else { + behaviorEntry = new MethodEntry(classEntry, methodDef.getName(), methodDef.getSignature()); + } + ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); + index.addDeclaration(node.getNameToken(), argumentEntry); + + return recurse(node, index); + } + + @Override + public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName()); + index.addReference(node.getIdentifierToken(), fieldEntry, m_behaviorEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, ref.getSignature()); + if (node.getType() instanceof SimpleType) { + SimpleType simpleTypeNode = (SimpleType)node.getType(); + index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, m_behaviorEntry); + } + } + + return recurse(node, index); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java new file mode 100644 index 00000000..7b902a9d --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.strobel.assembler.metadata.FieldDefinition; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.decompiler.languages.TextLocation; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; + +public class SourceIndexClassVisitor extends SourceIndexVisitor { + + private ClassEntry m_classEntry; + + public SourceIndexClassVisitor(ClassEntry classEntry) { + m_classEntry = classEntry; + } + + @Override + public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { + // is this this class, or a subtype? + TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getInternalName()); + if (!classEntry.equals(m_classEntry)) { + // it's a sub-type, recurse + index.addDeclaration(node.getNameToken(), classEntry); + return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); + } + + return recurse(node, index); + } + + @Override + public Void visitSimpleType(SimpleType node, SourceIndex index) { + TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); + if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { + ClassEntry classEntry = new ClassEntry(ref.getInternalName()); + index.addReference(node.getIdentifierToken(), classEntry, m_classEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { + MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(classEntry, def.getName(), def.getSignature()); + AstNode tokenNode = node.getNameToken(); + if (behaviorEntry instanceof ConstructorEntry) { + ConstructorEntry constructorEntry = (ConstructorEntry)behaviorEntry; + if (constructorEntry.isStatic()) { + tokenNode = node.getModifiers().firstOrNullObject(); + } + } + index.addDeclaration(tokenNode, behaviorEntry); + return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry), index); + } + + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { + MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, def.getSignature()); + index.addDeclaration(node.getNameToken(), constructorEntry); + return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); + } + + @Override + public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { + FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName()); + assert (node.getVariables().size() == 1); + VariableInitializer variable = node.getVariables().firstOrNullObject(); + index.addDeclaration(variable.getNameToken(), fieldEntry); + + return recurse(node, index); + } + + @Override + public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { + // treat enum declarations as field declarations + FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName()); + index.addDeclaration(node.getNameToken(), fieldEntry); + + return recurse(node, index); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java new file mode 100644 index 00000000..0d5bdc02 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -0,0 +1,452 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.decompiler.languages.java.ast.Annotation; +import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; +import com.strobel.decompiler.languages.java.ast.ArraySpecifier; +import com.strobel.decompiler.languages.java.ast.AssertStatement; +import com.strobel.decompiler.languages.java.ast.AssignmentExpression; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.BinaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.BlockStatement; +import com.strobel.decompiler.languages.java.ast.BreakStatement; +import com.strobel.decompiler.languages.java.ast.CaseLabel; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.CatchClause; +import com.strobel.decompiler.languages.java.ast.ClassOfExpression; +import com.strobel.decompiler.languages.java.ast.Comment; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; +import com.strobel.decompiler.languages.java.ast.ComposedType; +import com.strobel.decompiler.languages.java.ast.ConditionalExpression; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.ContinueStatement; +import com.strobel.decompiler.languages.java.ast.DoWhileStatement; +import com.strobel.decompiler.languages.java.ast.EmptyStatement; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.ExpressionStatement; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.ForEachStatement; +import com.strobel.decompiler.languages.java.ast.ForStatement; +import com.strobel.decompiler.languages.java.ast.GotoStatement; +import com.strobel.decompiler.languages.java.ast.IAstVisitor; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.IfElseStatement; +import com.strobel.decompiler.languages.java.ast.ImportDeclaration; +import com.strobel.decompiler.languages.java.ast.IndexerExpression; +import com.strobel.decompiler.languages.java.ast.InstanceInitializer; +import com.strobel.decompiler.languages.java.ast.InstanceOfExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.JavaTokenNode; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.LabelStatement; +import com.strobel.decompiler.languages.java.ast.LabeledStatement; +import com.strobel.decompiler.languages.java.ast.LambdaExpression; +import com.strobel.decompiler.languages.java.ast.LocalTypeDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.MethodGroupExpression; +import com.strobel.decompiler.languages.java.ast.NewLineNode; +import com.strobel.decompiler.languages.java.ast.NullReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.PackageDeclaration; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.ParenthesizedExpression; +import com.strobel.decompiler.languages.java.ast.PrimitiveExpression; +import com.strobel.decompiler.languages.java.ast.ReturnStatement; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.SwitchSection; +import com.strobel.decompiler.languages.java.ast.SwitchStatement; +import com.strobel.decompiler.languages.java.ast.SynchronizedStatement; +import com.strobel.decompiler.languages.java.ast.TextNode; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThrowStatement; +import com.strobel.decompiler.languages.java.ast.TryCatchStatement; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; +import com.strobel.decompiler.languages.java.ast.UnaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; +import com.strobel.decompiler.languages.java.ast.WhileStatement; +import com.strobel.decompiler.languages.java.ast.WildcardType; +import com.strobel.decompiler.patterns.Pattern; + +import cuchaz.enigma.mapping.ClassEntry; + +public class SourceIndexVisitor implements IAstVisitor { + + @Override + public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { + TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getInternalName()); + index.addDeclaration(node.getNameToken(), classEntry); + + return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); + } + + protected Void recurse(AstNode node, SourceIndex index) { + for (final AstNode child : node.getChildren()) { + child.acceptVisitor(this, index); + } + return null; + } + + @Override + public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSimpleType(SimpleType node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitComment(Comment node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitPatternPlaceholder(AstNode node, Pattern pattern, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitTypeReference(TypeReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitJavaTokenNode(JavaTokenNode node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitIdentifier(Identifier node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitNullReferenceExpression(NullReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitThisReferenceExpression(ThisReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSuperReferenceExpression(SuperReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitClassOfExpression(ClassOfExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitBlockStatement(BlockStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitExpressionStatement(ExpressionStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitBreakStatement(BreakStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitContinueStatement(ContinueStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitDoWhileStatement(DoWhileStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitEmptyStatement(EmptyStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitIfElseStatement(IfElseStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitLabelStatement(LabelStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitLabeledStatement(LabeledStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitReturnStatement(ReturnStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSwitchStatement(SwitchStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSwitchSection(SwitchSection node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitCaseLabel(CaseLabel node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitThrowStatement(ThrowStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitCatchClause(CatchClause node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitAnnotation(Annotation node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitNewLine(NewLineNode node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitVariableInitializer(VariableInitializer node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitText(TextNode node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitImportDeclaration(ImportDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitInitializerBlock(InstanceInitializer node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitCompilationUnit(CompilationUnit node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitPackageDeclaration(PackageDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitArraySpecifier(ArraySpecifier node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitComposedType(ComposedType node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitWhileStatement(WhileStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitPrimitiveExpression(PrimitiveExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitCastExpression(CastExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitInstanceOfExpression(InstanceOfExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitIndexerExpression(IndexerExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitConditionalExpression(ConditionalExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitArrayInitializerExpression(ArrayInitializerExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitArrayCreationExpression(ArrayCreationExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitAssignmentExpression(AssignmentExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitForStatement(ForStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitTryCatchStatement(TryCatchStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitGotoStatement(GotoStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitParenthesizedExpression(ParenthesizedExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSynchronizedStatement(SynchronizedStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitWildcardType(WildcardType node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitAssertStatement(AssertStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitLambdaExpression(LambdaExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, SourceIndex index) { + return recurse(node, index); + } +} diff --git a/src/cuchaz/enigma/analysis/Token.java b/src/cuchaz/enigma/analysis/Token.java new file mode 100644 index 00000000..481d2f47 --- /dev/null +++ b/src/cuchaz/enigma/analysis/Token.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +public class Token implements Comparable { + + public int start; + public int end; + public String text; + + public Token(int start, int end) { + this(start, end, null); + } + + public Token(int start, int end, String source) { + this.start = start; + this.end = end; + if (source != null) { + this.text = source.substring(start, end); + } + } + + public boolean contains(int pos) { + return pos >= start && pos <= end; + } + + @Override + public int compareTo(Token other) { + return start - other.start; + } + + @Override + public boolean equals(Object other) { + if (other instanceof Token) { + return equals((Token)other); + } + return false; + } + + public boolean equals(Token other) { + return start == other.start && end == other.end; + } + + @Override + public String toString() { + return String.format("[%d,%d]", start, end); + } +} diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java new file mode 100644 index 00000000..7597c3ae --- /dev/null +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtField; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.Translator; + +public class TranslationIndex implements Serializable { + + private static final long serialVersionUID = 738687982126844179L; + + private Map m_superclasses; + private Multimap m_fieldEntries; + private Multimap m_behaviorEntries; + + public TranslationIndex() { + m_superclasses = Maps.newHashMap(); + m_fieldEntries = HashMultimap.create(); + m_behaviorEntries = HashMultimap.create(); + } + + public TranslationIndex(TranslationIndex other, Translator translator) { + + // translate the superclasses + m_superclasses = Maps.newHashMap(); + for (Map.Entry mapEntry : other.m_superclasses.entrySet()) { + m_superclasses.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + + // translate the fields + m_fieldEntries = HashMultimap.create(); + for (Map.Entry mapEntry : other.m_fieldEntries.entries()) { + m_fieldEntries.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + + m_behaviorEntries = HashMultimap.create(); + for (Map.Entry mapEntry : other.m_behaviorEntries.entries()) { + m_behaviorEntries.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + } + + public void indexClass(CtClass c) { + + ClassEntry classEntry = JavassistUtil.getClassEntry(c); + + // add the superclass + ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c); + if (!isJre(classEntry) && superclassEntry != null && !isJre(superclassEntry)) { + m_superclasses.put(classEntry, superclassEntry); + } + + // add fields + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); + } + + // add behaviors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior); + m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); + } + } + + public void renameClasses(Map renames) { + EntryRenamer.renameClassesInMap(renames, m_superclasses); + EntryRenamer.renameClassesInMultimap(renames, m_fieldEntries); + EntryRenamer.renameClassesInMultimap(renames, m_behaviorEntries); + } + + public ClassEntry getSuperclass(ClassEntry classEntry) { + return m_superclasses.get(classEntry); + } + + public List getAncestry(ClassEntry classEntry) { + List ancestors = Lists.newArrayList(); + while (classEntry != null) { + classEntry = getSuperclass(classEntry); + if (classEntry != null) { + ancestors.add(classEntry); + } + } + return ancestors; + } + + public List getSubclass(ClassEntry classEntry) { + // linear search is fast enough for now + List subclasses = Lists.newArrayList(); + for (Map.Entry entry : m_superclasses.entrySet()) { + ClassEntry subclass = entry.getKey(); + ClassEntry superclass = entry.getValue(); + if (classEntry.equals(superclass)) { + subclasses.add(subclass); + } + } + return subclasses; + } + + public void getSubclassesRecursively(Set out, ClassEntry classEntry) { + for (ClassEntry subclassEntry : getSubclass(classEntry)) { + out.add(subclassEntry); + getSubclassesRecursively(out, subclassEntry); + } + } + + public void getSubclassNamesRecursively(Set out, ClassEntry classEntry) { + for (ClassEntry subclassEntry : getSubclass(classEntry)) { + out.add(subclassEntry.getName()); + getSubclassNamesRecursively(out, subclassEntry); + } + } + + public boolean entryExists(Entry entry) { + if (entry instanceof FieldEntry) { + return fieldExists((FieldEntry)entry); + } else if (entry instanceof BehaviorEntry) { + return behaviorExists((BehaviorEntry)entry); + } else if (entry instanceof ArgumentEntry) { + return behaviorExists(((ArgumentEntry)entry).getBehaviorEntry()); + } + throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); + } + + public boolean fieldExists(FieldEntry fieldEntry) { + return m_fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); + } + + public boolean behaviorExists(BehaviorEntry behaviorEntry) { + return m_behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); + } + + public ClassEntry resolveEntryClass(Entry entry) { + + if (entry instanceof ClassEntry) { + return (ClassEntry)entry; + } + + // this entry could refer to a method on a class where the method is not actually implemented + // travel up the inheritance tree to find the closest implementation + while (!entryExists(entry)) { + + // is there a parent class? + ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); + if (superclassEntry == null) { + // this is probably a method from a class in a library + // we can't trace the implementation up any higher unless we index the library + return null; + } + + // move up to the parent class + entry = entry.cloneToNewClass(superclassEntry); + } + return entry.getClassEntry(); + } + + private boolean isJre(ClassEntry classEntry) { + String packageName = classEntry.getPackageName(); + return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); + } + + public void write(OutputStream out) + throws IOException { + GZIPOutputStream gzipout = new GZIPOutputStream(out); + ObjectOutputStream oout = new ObjectOutputStream(gzipout); + oout.writeObject(m_superclasses); + oout.writeObject(m_fieldEntries); + oout.writeObject(m_behaviorEntries); + gzipout.finish(); + } + + @SuppressWarnings("unchecked") + public void read(InputStream in) + throws IOException { + try { + ObjectInputStream oin = new ObjectInputStream(new GZIPInputStream(in)); + m_superclasses = (HashMap)oin.readObject(); + m_fieldEntries = (HashMultimap)oin.readObject(); + m_behaviorEntries = (HashMultimap)oin.readObject(); + } catch (ClassNotFoundException ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java new file mode 100644 index 00000000..23f80899 --- /dev/null +++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -0,0 +1,512 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; + +import com.strobel.componentmodel.Key; +import com.strobel.decompiler.languages.java.ast.Annotation; +import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; +import com.strobel.decompiler.languages.java.ast.ArraySpecifier; +import com.strobel.decompiler.languages.java.ast.AssertStatement; +import com.strobel.decompiler.languages.java.ast.AssignmentExpression; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.BinaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.BlockStatement; +import com.strobel.decompiler.languages.java.ast.BreakStatement; +import com.strobel.decompiler.languages.java.ast.CaseLabel; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.CatchClause; +import com.strobel.decompiler.languages.java.ast.ClassOfExpression; +import com.strobel.decompiler.languages.java.ast.Comment; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; +import com.strobel.decompiler.languages.java.ast.ComposedType; +import com.strobel.decompiler.languages.java.ast.ConditionalExpression; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.ContinueStatement; +import com.strobel.decompiler.languages.java.ast.DoWhileStatement; +import com.strobel.decompiler.languages.java.ast.EmptyStatement; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.ExpressionStatement; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.ForEachStatement; +import com.strobel.decompiler.languages.java.ast.ForStatement; +import com.strobel.decompiler.languages.java.ast.GotoStatement; +import com.strobel.decompiler.languages.java.ast.IAstVisitor; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.IfElseStatement; +import com.strobel.decompiler.languages.java.ast.ImportDeclaration; +import com.strobel.decompiler.languages.java.ast.IndexerExpression; +import com.strobel.decompiler.languages.java.ast.InstanceInitializer; +import com.strobel.decompiler.languages.java.ast.InstanceOfExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.JavaTokenNode; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.LabelStatement; +import com.strobel.decompiler.languages.java.ast.LabeledStatement; +import com.strobel.decompiler.languages.java.ast.LambdaExpression; +import com.strobel.decompiler.languages.java.ast.LocalTypeDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.MethodGroupExpression; +import com.strobel.decompiler.languages.java.ast.NewLineNode; +import com.strobel.decompiler.languages.java.ast.NullReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.PackageDeclaration; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.ParenthesizedExpression; +import com.strobel.decompiler.languages.java.ast.PrimitiveExpression; +import com.strobel.decompiler.languages.java.ast.ReturnStatement; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.SwitchSection; +import com.strobel.decompiler.languages.java.ast.SwitchStatement; +import com.strobel.decompiler.languages.java.ast.SynchronizedStatement; +import com.strobel.decompiler.languages.java.ast.TextNode; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThrowStatement; +import com.strobel.decompiler.languages.java.ast.TryCatchStatement; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; +import com.strobel.decompiler.languages.java.ast.UnaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; +import com.strobel.decompiler.languages.java.ast.WhileStatement; +import com.strobel.decompiler.languages.java.ast.WildcardType; +import com.strobel.decompiler.patterns.Pattern; + +public class TreeDumpVisitor implements IAstVisitor { + + private File m_file; + private Writer m_out; + + public TreeDumpVisitor(File file) { + m_file = file; + m_out = null; + } + + @Override + public Void visitCompilationUnit(CompilationUnit node, Void ignored) { + try { + m_out = new FileWriter(m_file); + recurse(node, ignored); + m_out.close(); + return null; + } catch (IOException ex) { + throw new Error(ex); + } + } + + private Void recurse(AstNode node, Void ignored) { + // show the tree + try { + m_out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n"); + } catch (IOException ex) { + throw new Error(ex); + } + + // recurse + for (final AstNode child : node.getChildren()) { + child.acceptVisitor(this, ignored); + } + return null; + } + + private String getText(AstNode node) { + if (node instanceof Identifier) { + return "\"" + ((Identifier)node).getName() + "\""; + } + return ""; + } + + private String dumpUserData(AstNode node) { + StringBuilder buf = new StringBuilder(); + for (Key key : Keys.ALL_KEYS) { + Object val = node.getUserData(key); + if (val != null) { + buf.append(String.format(" [%s=%s]", key, val)); + } + } + return buf.toString(); + } + + private String getIndent(AstNode node) { + StringBuilder buf = new StringBuilder(); + int depth = getDepth(node); + for (int i = 0; i < depth; i++) { + buf.append("\t"); + } + return buf.toString(); + } + + private int getDepth(AstNode node) { + int depth = -1; + while (node != null) { + depth++; + node = node.getParent(); + } + return depth; + } + + // OVERRIDES WE DON'T CARE ABOUT + + @Override + public Void visitInvocationExpression(InvocationExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitMemberReferenceExpression(MemberReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSimpleType(SimpleType node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitMethodDeclaration(MethodDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitParameterDeclaration(ParameterDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitFieldDeclaration(FieldDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitTypeDeclaration(TypeDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitComment(Comment node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitPatternPlaceholder(AstNode node, Pattern pattern, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitTypeReference(TypeReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitJavaTokenNode(JavaTokenNode node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitIdentifier(Identifier node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitNullReferenceExpression(NullReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitThisReferenceExpression(ThisReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSuperReferenceExpression(SuperReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitClassOfExpression(ClassOfExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitBlockStatement(BlockStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitExpressionStatement(ExpressionStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitBreakStatement(BreakStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitContinueStatement(ContinueStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitDoWhileStatement(DoWhileStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitEmptyStatement(EmptyStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitIfElseStatement(IfElseStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitLabelStatement(LabelStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitLabeledStatement(LabeledStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitReturnStatement(ReturnStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSwitchStatement(SwitchStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSwitchSection(SwitchSection node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitCaseLabel(CaseLabel node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitThrowStatement(ThrowStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitCatchClause(CatchClause node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitAnnotation(Annotation node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitNewLine(NewLineNode node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitVariableDeclaration(VariableDeclarationStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitVariableInitializer(VariableInitializer node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitText(TextNode node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitImportDeclaration(ImportDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitInitializerBlock(InstanceInitializer node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitPackageDeclaration(PackageDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitArraySpecifier(ArraySpecifier node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitComposedType(ComposedType node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitWhileStatement(WhileStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitPrimitiveExpression(PrimitiveExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitCastExpression(CastExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitInstanceOfExpression(InstanceOfExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitIndexerExpression(IndexerExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitIdentifierExpression(IdentifierExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitConditionalExpression(ConditionalExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitArrayInitializerExpression(ArrayInitializerExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitObjectCreationExpression(ObjectCreationExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitArrayCreationExpression(ArrayCreationExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitAssignmentExpression(AssignmentExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitForStatement(ForStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitForEachStatement(ForEachStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitTryCatchStatement(TryCatchStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitGotoStatement(GotoStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitParenthesizedExpression(ParenthesizedExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSynchronizedStatement(SynchronizedStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitWildcardType(WildcardType node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitMethodGroupExpression(MethodGroupExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitAssertStatement(AssertStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitLambdaExpression(LambdaExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, Void ignored) { + return recurse(node, ignored); + } +} diff --git a/src/cuchaz/enigma/bytecode/CheckCastIterator.java b/src/cuchaz/enigma/bytecode/CheckCastIterator.java new file mode 100644 index 00000000..b6efbd49 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/CheckCastIterator.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Iterator; + +import javassist.bytecode.BadBytecode; +import javassist.bytecode.CodeAttribute; +import javassist.bytecode.CodeIterator; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.Opcode; +import cuchaz.enigma.bytecode.CheckCastIterator.CheckCast; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class CheckCastIterator implements Iterator { + + public static class CheckCast { + + public String className; + public MethodEntry prevMethodEntry; + + public CheckCast(String className, MethodEntry prevMethodEntry) { + this.className = className; + this.prevMethodEntry = prevMethodEntry; + } + } + + private ConstPool m_constants; + private CodeAttribute m_attribute; + private CodeIterator m_iter; + private CheckCast m_next; + + public CheckCastIterator(CodeAttribute codeAttribute) throws BadBytecode { + m_constants = codeAttribute.getConstPool(); + m_attribute = codeAttribute; + m_iter = m_attribute.iterator(); + + m_next = getNext(); + } + + @Override + public boolean hasNext() { + return m_next != null; + } + + @Override + public CheckCast next() { + CheckCast out = m_next; + try { + m_next = getNext(); + } catch (BadBytecode ex) { + throw new Error(ex); + } + return out; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + private CheckCast getNext() throws BadBytecode { + int prevPos = 0; + while (m_iter.hasNext()) { + int pos = m_iter.next(); + int opcode = m_iter.byteAt(pos); + switch (opcode) { + case Opcode.CHECKCAST: + + // get the type of this op code (next two bytes are a classinfo index) + MethodEntry prevMethodEntry = getMethodEntry(prevPos); + if (prevMethodEntry != null) { + return new CheckCast(m_constants.getClassInfo(m_iter.s16bitAt(pos + 1)), prevMethodEntry); + } + break; + } + prevPos = pos; + } + return null; + } + + private MethodEntry getMethodEntry(int pos) { + switch (m_iter.byteAt(pos)) { + case Opcode.INVOKEVIRTUAL: + case Opcode.INVOKESTATIC: + case Opcode.INVOKEDYNAMIC: + case Opcode.INVOKESPECIAL: { + int index = m_iter.s16bitAt(pos + 1); + return new MethodEntry( + new ClassEntry(Descriptor.toJvmName(m_constants.getMethodrefClassName(index))), + m_constants.getMethodrefName(index), + m_constants.getMethodrefType(index) + ); + } + + case Opcode.INVOKEINTERFACE: { + int index = m_iter.s16bitAt(pos + 1); + return new MethodEntry( + new ClassEntry(Descriptor.toJvmName(m_constants.getInterfaceMethodrefClassName(index))), + m_constants.getInterfaceMethodrefName(index), + m_constants.getInterfaceMethodrefType(index) + ); + } + } + return null; + } + + public Iterable casts() { + return new Iterable() { + @Override + public Iterator iterator() { + return CheckCastIterator.this; + } + }; + } +} diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java new file mode 100644 index 00000000..a5fea926 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Map; +import java.util.Set; + +import javassist.ClassMap; +import javassist.CtClass; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.InnerClassesAttribute; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ClassEntry; + +public class ClassRenamer { + + public static void renameClasses(CtClass c, Map map) { + + // build the map used by javassist + ClassMap nameMap = new ClassMap(); + for (Map.Entry entry : map.entrySet()) { + nameMap.put(entry.getKey().getName(), entry.getValue().getName()); + } + + c.replaceClassName(nameMap); + + // replace simple names in the InnerClasses attribute too + ConstPool constants = c.getClassFile().getConstPool(); + InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (attr != null) { + for (int i = 0; i < attr.tableLength(); i++) { + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(attr.innerClass(i))); + if (attr.innerNameIndex(i) != 0) { + attr.setInnerNameIndex(i, constants.addUtf8Info(classEntry.getInnerClassName())); + } + + /* DEBUG + System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); + */ + } + } + } + + public static Set getAllClassEntries(final CtClass c) { + + // get the classes that javassist knows about + final Set entries = Sets.newHashSet(); + ClassMap map = new ClassMap() { + @Override + public Object get(Object obj) { + if (obj instanceof String) { + String str = (String)obj; + + // javassist throws a lot of weird things at this map + // I either have to implement my on class scanner, or just try to filter out the weirdness + // I'm opting to filter out the weirdness for now + + // skip anything with generic arguments + if (str.indexOf('<') >= 0 || str.indexOf('>') >= 0 || str.indexOf(';') >= 0) { + return null; + } + + // convert path/to/class.inner to path/to/class$inner + str = str.replace('.', '$'); + + // remember everything else + entries.add(new ClassEntry(str)); + } + return null; + } + + private static final long serialVersionUID = -202160293602070641L; + }; + c.replaceClassName(map); + + return entries; + } + + public static void moveAllClassesOutOfDefaultPackage(CtClass c, String newPackageName) { + Map map = Maps.newHashMap(); + for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { + if (classEntry.isInDefaultPackage()) { + map.put(classEntry, new ClassEntry(newPackageName + "/" + classEntry.getName())); + } + } + ClassRenamer.renameClasses(c, map); + } + + public static void moveAllClassesIntoDefaultPackage(CtClass c, String oldPackageName) { + Map map = Maps.newHashMap(); + for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { + if (classEntry.getPackageName().equals(oldPackageName)) { + map.put(classEntry, new ClassEntry(classEntry.getSimpleName())); + } + } + ClassRenamer.renameClasses(c, map); + } +} diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java new file mode 100644 index 00000000..fb2fb27f --- /dev/null +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Map; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtField; +import javassist.CtMethod; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.SourceFileAttribute; + +import com.google.common.collect.Maps; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class ClassTranslator { + + private Translator m_translator; + + public ClassTranslator(Translator translator) { + m_translator = translator; + } + + public void translate(CtClass c) { + + // NOTE: the order of these translations is very important + + // translate all the field and method references in the code by editing the constant pool + ConstPool constants = c.getClassFile().getConstPool(); + ConstPoolEditor editor = new ConstPoolEditor(constants); + for (int i = 1; i < constants.getSize(); i++) { + switch (constants.getTag(i)) { + + case ConstPool.CONST_Fieldref: { + + // translate the name + FieldEntry entry = new FieldEntry( + new ClassEntry(Descriptor.toJvmName(constants.getFieldrefClassName(i))), + constants.getFieldrefName(i) + ); + FieldEntry translatedEntry = m_translator.translateEntry(entry); + + // translate the type + String type = constants.getFieldrefType(i); + String translatedType = m_translator.translateSignature(type); + + if (!entry.equals(translatedEntry) || !type.equals(translatedType)) { + editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedType); + } + } + break; + + case ConstPool.CONST_Methodref: + case ConstPool.CONST_InterfaceMethodref: { + + // translate the name and type + BehaviorEntry entry = BehaviorEntryFactory.create( + Descriptor.toJvmName(editor.getMemberrefClassname(i)), + editor.getMemberrefName(i), + editor.getMemberrefType(i) + ); + BehaviorEntry translatedEntry = m_translator.translateEntry(entry); + + if (!entry.getName().equals(translatedEntry.getName()) || !entry.getSignature().equals(translatedEntry.getSignature())) { + editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature()); + } + } + break; + } + } + + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + + // translate all the fields + for (CtField field : c.getDeclaredFields()) { + + // translate the name + FieldEntry entry = new FieldEntry(classEntry, field.getName()); + String translatedName = m_translator.translate(entry); + if (translatedName != null) { + field.setName(translatedName); + } + + // translate the type + String translatedType = m_translator.translateSignature(field.getFieldInfo().getDescriptor()); + field.getFieldInfo().setDescriptor(translatedType); + } + + // translate all the methods and constructors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + if (behavior instanceof CtMethod) { + CtMethod method = (CtMethod)behavior; + + // translate the name + MethodEntry entry = new MethodEntry(classEntry, method.getName(), method.getSignature()); + String translatedName = m_translator.translate(entry); + if (translatedName != null) { + method.setName(translatedName); + } + } + + // translate the type + String translatedSignature = m_translator.translateSignature(behavior.getMethodInfo().getDescriptor()); + behavior.getMethodInfo().setDescriptor(translatedSignature); + } + + // translate all the class names referenced in the code + // the above code only changed method/field/reference names and types, but not the class names themselves + Map map = Maps.newHashMap(); + for (ClassEntry obfClassEntry : ClassRenamer.getAllClassEntries(c)) { + ClassEntry deobfClassEntry = m_translator.translateEntry(obfClassEntry); + if (!obfClassEntry.equals(deobfClassEntry)) { + map.put(obfClassEntry, deobfClassEntry); + } + } + ClassRenamer.renameClasses(c, map); + + // translate the source file attribute too + ClassEntry deobfClassEntry = map.get(classEntry); + if (deobfClassEntry != null) { + String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOuterClassName()) + ".java"; + c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java new file mode 100644 index 00000000..2dec3b76 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java @@ -0,0 +1,263 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; + +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor; +import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; +import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; + +public class ConstPoolEditor { + + private static Method m_getItem; + private static Method m_addItem; + private static Method m_addItem0; + private static Field m_items; + private static Field m_cache; + private static Field m_numItems; + private static Field m_objects; + private static Field m_elements; + private static Method m_methodWritePool; + private static Constructor m_constructorPool; + + static { + try { + m_getItem = ConstPool.class.getDeclaredMethod("getItem", int.class); + m_getItem.setAccessible(true); + + m_addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo")); + m_addItem.setAccessible(true); + + m_addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo")); + m_addItem0.setAccessible(true); + + m_items = ConstPool.class.getDeclaredField("items"); + m_items.setAccessible(true); + + m_cache = ConstPool.class.getDeclaredField("itemsCache"); + m_cache.setAccessible(true); + + m_numItems = ConstPool.class.getDeclaredField("numOfItems"); + m_numItems.setAccessible(true); + + m_objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects"); + m_objects.setAccessible(true); + + m_elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements"); + m_elements.setAccessible(true); + + m_methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class); + m_methodWritePool.setAccessible(true); + + m_constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class); + m_constructorPool.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + private ConstPool m_pool; + + public ConstPoolEditor(ConstPool pool) { + m_pool = pool; + } + + public void writePool(DataOutputStream out) { + try { + m_methodWritePool.invoke(m_pool, out); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static ConstPool readPool(DataInputStream in) { + try { + return m_constructorPool.newInstance(in); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public String getMemberrefClassname(int memberrefIndex) { + return Descriptor.toJvmName(m_pool.getClassInfo(m_pool.getMemberClass(memberrefIndex))); + } + + public String getMemberrefName(int memberrefIndex) { + return m_pool.getUtf8Info(m_pool.getNameAndTypeName(m_pool.getMemberNameAndType(memberrefIndex))); + } + + public String getMemberrefType(int memberrefIndex) { + return m_pool.getUtf8Info(m_pool.getNameAndTypeDescriptor(m_pool.getMemberNameAndType(memberrefIndex))); + } + + public ConstInfoAccessor getItem(int index) { + try { + Object entry = m_getItem.invoke(m_pool, index); + if (entry == null) { + return null; + } + return new ConstInfoAccessor(entry); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int addItem(Object item) { + try { + return (Integer)m_addItem.invoke(m_pool, item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int addItemForceNew(Object item) { + try { + return (Integer)m_addItem0.invoke(m_pool, item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + @SuppressWarnings("rawtypes") + public void removeLastItem() { + try { + // remove the item from the cache + HashMap cache = getCache(); + if (cache != null) { + Object item = getItem(m_pool.getSize() - 1); + cache.remove(item); + } + + // remove the actual item + // based off of LongVector.addElement() + Object items = m_items.get(m_pool); + Object[][] objects = (Object[][])m_objects.get(items); + int numElements = (Integer)m_elements.get(items) - 1; + int nth = numElements >> 7; + int offset = numElements & (128 - 1); + objects[nth][offset] = null; + + // decrement the number of items + m_elements.set(items, numElements); + m_numItems.set(m_pool, (Integer)m_numItems.get(m_pool) - 1); + } catch (Exception ex) { + throw new Error(ex); + } + } + + @SuppressWarnings("rawtypes") + public HashMap getCache() { + try { + return (HashMap)m_cache.get(m_pool); + } catch (Exception ex) { + throw new Error(ex); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) { + // NOTE: when changing values, we always need to copy-on-write + try { + // get the memberref item + Object item = getItem(memberrefIndex).getItem(); + + // update the cache + HashMap cache = getCache(); + if (cache != null) { + cache.remove(item); + } + + new MemberRefInfoAccessor(item).setNameAndTypeIndex(m_pool.addNameAndTypeInfo(newName, newType)); + + // update the cache + if (cache != null) { + cache.put(item, item); + } + } catch (Exception ex) { + throw new Error(ex); + } + + // make sure the change worked + assert (newName.equals(getMemberrefName(memberrefIndex))); + assert (newType.equals(getMemberrefType(memberrefIndex))); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void changeClassName(int classNameIndex, String newName) { + // NOTE: when changing values, we always need to copy-on-write + try { + // get the class item + Object item = getItem(classNameIndex).getItem(); + + // update the cache + HashMap cache = getCache(); + if (cache != null) { + cache.remove(item); + } + + // add the new name and repoint the name-and-type to it + new ClassInfoAccessor(item).setNameIndex(m_pool.addUtf8Info(newName)); + + // update the cache + if (cache != null) { + cache.put(item, item); + } + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static ConstPool newConstPool() { + // const pool expects the name of a class to initialize itself + // but we want an empty pool + // so give it a bogus name, and then clear the entries afterwards + ConstPool pool = new ConstPool("a"); + + ConstPoolEditor editor = new ConstPoolEditor(pool); + int size = pool.getSize(); + for (int i = 0; i < size - 1; i++) { + editor.removeLastItem(); + } + + // make sure the pool is actually empty + // although, in this case "empty" means one thing in it + // the JVM spec says index 0 should be reserved + assert (pool.getSize() == 1); + assert (editor.getItem(0) == null); + assert (editor.getItem(1) == null); + assert (editor.getItem(2) == null); + assert (editor.getItem(3) == null); + + // also, clear the cache + editor.getCache().clear(); + + return pool; + } + + public String dump() { + StringBuilder buf = new StringBuilder(); + for (int i = 1; i < m_pool.getSize(); i++) { + buf.append(String.format("%4d", i)); + buf.append(" "); + buf.append(getItem(i).toString()); + buf.append("\n"); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/bytecode/InfoType.java b/src/cuchaz/enigma/bytecode/InfoType.java new file mode 100644 index 00000000..deaf6232 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/InfoType.java @@ -0,0 +1,317 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor; +import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; +import cuchaz.enigma.bytecode.accessors.InvokeDynamicInfoAccessor; +import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; +import cuchaz.enigma.bytecode.accessors.MethodHandleInfoAccessor; +import cuchaz.enigma.bytecode.accessors.MethodTypeInfoAccessor; +import cuchaz.enigma.bytecode.accessors.NameAndTypeInfoAccessor; +import cuchaz.enigma.bytecode.accessors.StringInfoAccessor; + +public enum InfoType { + + Utf8Info( 1, 0 ), + IntegerInfo( 3, 0 ), + FloatInfo( 4, 0 ), + LongInfo( 5, 0 ), + DoubleInfo( 6, 0 ), + ClassInfo( 7, 1 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getNameIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); + accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); + ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); + return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag(); + } + }, + StringInfo( 8, 1 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getStringIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); + accessor.setStringIndex(remapIndex(map, accessor.getStringIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); + ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex()); + return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag(); + } + }, + FieldRefInfo( 9, 2 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getClassIndex()); + gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); + accessor.setClassIndex(remapIndex(map, accessor.getClassIndex())); + accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); + ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex()); + ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); + return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); + } + }, + // same as FieldRefInfo + MethodRefInfo( 10, 2 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + FieldRefInfo.gatherIndexTree(indices, editor, entry); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + FieldRefInfo.remapIndices(map, entry); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + return FieldRefInfo.subIndicesAreValid(entry, pool); + } + }, + // same as FieldRefInfo + InterfaceMethodRefInfo( 11, 2 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + FieldRefInfo.gatherIndexTree(indices, editor, entry); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + FieldRefInfo.remapIndices(map, entry); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + return FieldRefInfo.subIndicesAreValid(entry, pool); + } + }, + NameAndTypeInfo( 12, 1 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getNameIndex()); + gatherIndexTree(indices, editor, accessor.getTypeIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); + accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); + accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); + ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); + ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); + return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); + } + }, + MethodHandleInfo( 15, 3 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getTypeIndex()); + gatherIndexTree(indices, editor, accessor.getMethodRefIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); + accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); + accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); + ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); + ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex()); + return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag(); + } + }, + MethodTypeInfo( 16, 1 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getTypeIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); + accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); + ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); + return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); + } + }, + InvokeDynamicInfo( 18, 2 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getBootstrapIndex()); + gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); + accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex())); + accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); + ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex()); + ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); + return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); + } + }; + + private static Map m_types; + + static { + m_types = Maps.newTreeMap(); + for (InfoType type : values()) { + m_types.put(type.getTag(), type); + } + } + + private int m_tag; + private int m_level; + + private InfoType(int tag, int level) { + m_tag = tag; + m_level = level; + } + + public int getTag() { + return m_tag; + } + + public int getLevel() { + return m_level; + } + + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + // by default, do nothing + } + + public void remapIndices(Map map, ConstInfoAccessor entry) { + // by default, do nothing + } + + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + // by default, everything is good + return true; + } + + public boolean selfIndexIsValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + ConstInfoAccessor entryCheck = pool.getItem(entry.getIndex()); + if (entryCheck == null) { + return false; + } + return entryCheck.getItem().equals(entry.getItem()); + } + + public static InfoType getByTag(int tag) { + return m_types.get(tag); + } + + public static List getByLevel(int level) { + List types = Lists.newArrayList(); + for (InfoType type : values()) { + if (type.getLevel() == level) { + types.add(type); + } + } + return types; + } + + public static List getSortedByLevel() { + List types = Lists.newArrayList(); + types.addAll(getByLevel(0)); + types.addAll(getByLevel(1)); + types.addAll(getByLevel(2)); + types.addAll(getByLevel(3)); + return types; + } + + public static void gatherIndexTree(Collection indices, ConstPoolEditor editor, int index) { + // add own index + indices.add(index); + + // recurse + ConstInfoAccessor entry = editor.getItem(index); + entry.getType().gatherIndexTree(indices, editor, entry); + } + + private static int remapIndex(Map map, int index) { + Integer newIndex = map.get(index); + if (newIndex == null) { + newIndex = index; + } + return newIndex; + } +} diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java new file mode 100644 index 00000000..f52c31aa --- /dev/null +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Collection; + +import javassist.CtClass; +import javassist.bytecode.AccessFlag; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.EnclosingMethodAttribute; +import javassist.bytecode.InnerClassesAttribute; +import cuchaz.enigma.Constants; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; + +public class InnerClassWriter { + + private JarIndex m_jarIndex; + + public InnerClassWriter(JarIndex jarIndex) { + m_jarIndex = jarIndex; + } + + public void write(CtClass c) { + + // is this an inner or outer class? + String obfInnerClassName = new ClassEntry(Descriptor.toJvmName(c.getName())).getSimpleName(); + String obfOuterClassName = m_jarIndex.getOuterClass(obfInnerClassName); + if (obfOuterClassName == null) { + // this is an outer class + obfOuterClassName = Descriptor.toJvmName(c.getName()); + } else { + // this is an inner class, rename it to outer$inner + ClassEntry obfClassEntry = new ClassEntry(obfOuterClassName + "$" + obfInnerClassName); + c.setName(obfClassEntry.getName()); + + BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller(obfInnerClassName); + if (caller != null) { + // write the enclosing method attribute + if (caller.getName().equals("")) { + c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); + } else { + c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature())); + } + } + } + + // write the inner classes if needed + Collection obfInnerClassNames = m_jarIndex.getInnerClasses(obfOuterClassName); + if (obfInnerClassNames != null && !obfInnerClassNames.isEmpty()) { + writeInnerClasses(c, obfOuterClassName, obfInnerClassNames); + } + } + + private void writeInnerClasses(CtClass c, String obfOuterClassName, Collection obfInnerClassNames) { + InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); + c.getClassFile().addAttribute(attr); + for (String obfInnerClassName : obfInnerClassNames) { + // get the new inner class name + ClassEntry obfClassEntry = new ClassEntry(obfOuterClassName + "$" + obfInnerClassName); + + // here's what the JVM spec says about the InnerClasses attribute + // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); + + // update the attribute with this inner class + ConstPool constPool = c.getClassFile().getConstPool(); + int innerClassIndex = constPool.addClassInfo(obfClassEntry.getName()); + int outerClassIndex = 0; + int innerClassSimpleNameIndex = 0; + if (!m_jarIndex.isAnonymousClass(obfInnerClassName)) { + outerClassIndex = constPool.addClassInfo(obfClassEntry.getOuterClassName()); + innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getInnerClassName()); + } + + attr.append(innerClassIndex, outerClassIndex, innerClassSimpleNameIndex, c.getClassFile().getAccessFlags() & ~AccessFlag.SUPER); + + /* DEBUG + System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", + obfClassEntry, + attr.outerClass(attr.tableLength() - 1), + attr.innerClass(attr.tableLength() - 1), + attr.innerName(attr.tableLength() - 1), + Constants.NonePackage + "/" + obfInnerClassName, + obfClassEntry.getName() + )); + */ + + // make sure the outer class references only the new inner class names + c.replaceClassName(Constants.NonePackage + "/" + obfInnerClassName, obfClassEntry.getName()); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java new file mode 100644 index 00000000..5a11cd89 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.ArrayList; +import java.util.List; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtMethod; +import javassist.bytecode.Descriptor; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class MethodParameterWriter { + + private Translator m_translator; + + public MethodParameterWriter(Translator translator) { + m_translator = translator; + } + + public void writeMethodArguments(CtClass c) { + + // Procyon will read method arguments from the "MethodParameters" attribute, so write those + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + int numParams = Descriptor.numOfParameters(behavior.getMethodInfo().getDescriptor()); + if (numParams <= 0) { + continue; + } + + // get the behavior entry + BehaviorEntry behaviorEntry; + if (behavior instanceof CtMethod) { + behaviorEntry = new MethodEntry(classEntry, behavior.getMethodInfo().getName(), behavior.getSignature()); + } else if (behavior instanceof CtConstructor) { + behaviorEntry = new ConstructorEntry(classEntry, behavior.getSignature()); + } else { + throw new Error("Unsupported behavior type: " + behavior.getClass().getName()); + } + + // get the list of parameter names + List names = new ArrayList(numParams); + for (int i = 0; i < numParams; i++) { + names.add(m_translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); + } + + // save the mappings to the class + MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java new file mode 100644 index 00000000..bf959564 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javassist.bytecode.AttributeInfo; +import javassist.bytecode.ConstPool; +import javassist.bytecode.MethodInfo; + +public class MethodParametersAttribute extends AttributeInfo { + + private MethodParametersAttribute(ConstPool pool, List parameterNameIndices) { + super(pool, "MethodParameters", writeStruct(parameterNameIndices)); + } + + public static void updateClass(MethodInfo info, List names) { + // add the names to the class const pool + ConstPool constPool = info.getConstPool(); + List parameterNameIndices = new ArrayList(); + for (String name : names) { + if (name != null) { + parameterNameIndices.add(constPool.addUtf8Info(name)); + } else { + parameterNameIndices.add(0); + } + } + + // add the attribute to the method + info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices)); + } + + private static byte[] writeStruct(List parameterNameIndices) { + // JVM 8 Spec says the struct looks like this: + // http://cr.openjdk.java.net/~mr/se/8/java-se-8-fr-spec-01/java-se-8-jvms-fr-diffs.pdf + // uint8 num_params + // for each param: + // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry + // uint16 access_flags -> don't care, just set to 0 + + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(buf); + + // NOTE: java hates unsigned integers, so we have to be careful here + // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument + // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte + // if the int is out of range, the byte stream won't look the way we want and weird things will happen + final int SIZEOF_UINT8 = 1; + final int SIZEOF_UINT16 = 2; + final int MAX_UINT8 = (1 << 8) - 1; + final int MAX_UINT16 = (1 << 16) - 1; + + try { + assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8); + out.writeByte(parameterNameIndices.size()); + + for (Integer index : parameterNameIndices) { + assert (index >= 0 && index <= MAX_UINT16); + out.writeShort(index); + + // just write 0 for the access flags + out.writeShort(0); + } + + out.close(); + byte[] data = buf.toByteArray(); + assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16)); + return data; + } catch (IOException ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java new file mode 100644 index 00000000..d76f0567 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class ClassInfoAccessor { + + private static Class m_class; + private static Field m_nameIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.ClassInfo"); + m_nameIndex = m_class.getDeclaredField("name"); + m_nameIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public ClassInfoAccessor(Object item) { + m_item = item; + } + + public int getNameIndex() { + try { + return (Integer)m_nameIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setNameIndex(int val) { + try { + m_nameIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java new file mode 100644 index 00000000..d00c1021 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import cuchaz.enigma.bytecode.InfoType; + +public class ConstInfoAccessor { + + private static Class m_class; + private static Field m_index; + private static Method m_getTag; + + static { + try { + m_class = Class.forName("javassist.bytecode.ConstInfo"); + m_index = m_class.getDeclaredField("index"); + m_index.setAccessible(true); + m_getTag = m_class.getMethod("getTag"); + m_getTag.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + private Object m_item; + + public ConstInfoAccessor(Object item) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null!"); + } + m_item = item; + } + + public ConstInfoAccessor(DataInputStream in) throws IOException { + try { + // read the entry + String className = in.readUTF(); + int oldIndex = in.readInt(); + + // NOTE: ConstInfo instances write a type id (a "tag"), but they don't read it back + // so we have to read it here + in.readByte(); + + Constructor constructor = Class.forName(className).getConstructor(DataInputStream.class, int.class); + constructor.setAccessible(true); + m_item = constructor.newInstance(in, oldIndex); + } catch (IOException ex) { + throw ex; + } catch (Exception ex) { + throw new Error(ex); + } + } + + public Object getItem() { + return m_item; + } + + public int getIndex() { + try { + return (Integer)m_index.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setIndex(int val) { + try { + m_index.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int getTag() { + try { + return (Integer)m_getTag.invoke(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public ConstInfoAccessor copy() { + return new ConstInfoAccessor(copyItem()); + } + + public Object copyItem() { + // I don't know of a simpler way to copy one of these silly things... + try { + // serialize the item + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(buf); + write(out); + + // deserialize the item + DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray())); + Object item = new ConstInfoAccessor(in).getItem(); + in.close(); + + return item; + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void write(DataOutputStream out) throws IOException { + try { + out.writeUTF(m_item.getClass().getName()); + out.writeInt(getIndex()); + + Method method = m_item.getClass().getMethod("write", DataOutputStream.class); + method.setAccessible(true); + method.invoke(m_item, out); + } catch (IOException ex) { + throw ex; + } catch (Exception ex) { + throw new Error(ex); + } + } + + @Override + public String toString() { + try { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + PrintWriter out = new PrintWriter(buf); + Method print = m_item.getClass().getMethod("print", PrintWriter.class); + print.setAccessible(true); + print.invoke(m_item, out); + out.close(); + return buf.toString().replace("\n", ""); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public InfoType getType() { + return InfoType.getByTag(getTag()); + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java new file mode 100644 index 00000000..0d780ea6 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class InvokeDynamicInfoAccessor { + + private static Class m_class; + private static Field m_bootstrapIndex; + private static Field m_nameAndTypeIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.InvokeDynamicInfo"); + m_bootstrapIndex = m_class.getDeclaredField("bootstrap"); + m_bootstrapIndex.setAccessible(true); + m_nameAndTypeIndex = m_class.getDeclaredField("nameAndType"); + m_nameAndTypeIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public InvokeDynamicInfoAccessor(Object item) { + m_item = item; + } + + public int getBootstrapIndex() { + try { + return (Integer)m_bootstrapIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setBootstrapIndex(int val) { + try { + m_bootstrapIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int getNameAndTypeIndex() { + try { + return (Integer)m_nameAndTypeIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setNameAndTypeIndex(int val) { + try { + m_nameAndTypeIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java new file mode 100644 index 00000000..9fe945f7 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class MemberRefInfoAccessor { + + private static Class m_class; + private static Field m_classIndex; + private static Field m_nameAndTypeIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.MemberrefInfo"); + m_classIndex = m_class.getDeclaredField("classIndex"); + m_classIndex.setAccessible(true); + m_nameAndTypeIndex = m_class.getDeclaredField("nameAndTypeIndex"); + m_nameAndTypeIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public MemberRefInfoAccessor(Object item) { + m_item = item; + } + + public int getClassIndex() { + try { + return (Integer)m_classIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setClassIndex(int val) { + try { + m_classIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int getNameAndTypeIndex() { + try { + return (Integer)m_nameAndTypeIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setNameAndTypeIndex(int val) { + try { + m_nameAndTypeIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java new file mode 100644 index 00000000..4c95b226 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class MethodHandleInfoAccessor { + + private static Class m_class; + private static Field m_kindIndex; + private static Field m_indexIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.MethodHandleInfo"); + m_kindIndex = m_class.getDeclaredField("refKind"); + m_kindIndex.setAccessible(true); + m_indexIndex = m_class.getDeclaredField("refIndex"); + m_indexIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public MethodHandleInfoAccessor(Object item) { + m_item = item; + } + + public int getTypeIndex() { + try { + return (Integer)m_kindIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setTypeIndex(int val) { + try { + m_kindIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int getMethodRefIndex() { + try { + return (Integer)m_indexIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setMethodRefIndex(int val) { + try { + m_indexIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java new file mode 100644 index 00000000..e1511179 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class MethodTypeInfoAccessor { + + private static Class m_class; + private static Field m_descriptorIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.MethodTypeInfo"); + m_descriptorIndex = m_class.getDeclaredField("descriptor"); + m_descriptorIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public MethodTypeInfoAccessor(Object item) { + m_item = item; + } + + public int getTypeIndex() { + try { + return (Integer)m_descriptorIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setTypeIndex(int val) { + try { + m_descriptorIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java new file mode 100644 index 00000000..6e82f3e9 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class NameAndTypeInfoAccessor { + + private static Class m_class; + private static Field m_nameIndex; + private static Field m_typeIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.NameAndTypeInfo"); + m_nameIndex = m_class.getDeclaredField("memberName"); + m_nameIndex.setAccessible(true); + m_typeIndex = m_class.getDeclaredField("typeDescriptor"); + m_typeIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public NameAndTypeInfoAccessor(Object item) { + m_item = item; + } + + public int getNameIndex() { + try { + return (Integer)m_nameIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setNameIndex(int val) { + try { + m_nameIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int getTypeIndex() { + try { + return (Integer)m_typeIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setTypeIndex(int val) { + try { + m_typeIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java new file mode 100644 index 00000000..6665ffe4 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class StringInfoAccessor { + + private static Class m_class; + private static Field m_stringIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.StringInfo"); + m_stringIndex = m_class.getDeclaredField("string"); + m_stringIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public StringInfoAccessor(Object item) { + m_item = item; + } + + public int getStringIndex() { + try { + return (Integer)m_stringIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setStringIndex(int val) { + try { + m_stringIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java new file mode 100644 index 00000000..2abf60b4 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +public class Utf8InfoAccessor { + + private static Class m_class; + + static { + try { + m_class = Class.forName("javassist.bytecode.Utf8Info"); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } +} diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java new file mode 100644 index 00000000..73404038 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -0,0 +1,411 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +import javassist.CannotCompileException; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.CtMethod; +import javassist.bytecode.BadBytecode; +import javassist.bytecode.CodeIterator; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.Opcode; +import javassist.expr.ConstructorCall; +import javassist.expr.ExprEditor; +import javassist.expr.FieldAccess; +import javassist.expr.MethodCall; +import javassist.expr.NewExpr; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multiset; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.Util; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.bytecode.ConstPoolEditor; +import cuchaz.enigma.bytecode.InfoType; +import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; +import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.SignatureUpdater; +import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; + +public class ClassIdentity { + + private ClassEntry m_classEntry; + private SidedClassNamer m_namer; + private Multiset m_fields; + private Multiset m_methods; + private Multiset m_constructors; + private String m_staticInitializer; + private String m_extends; + private Multiset m_implements; + private Multiset m_implementations; + private Multiset m_references; + + public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) { + m_namer = namer; + + // stuff from the bytecode + + m_classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + m_fields = HashMultiset.create(); + for (CtField field : c.getDeclaredFields()) { + m_fields.add(scrubSignature(field.getSignature())); + } + m_methods = HashMultiset.create(); + for (CtMethod method : c.getDeclaredMethods()) { + m_methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method)); + } + m_constructors = HashMultiset.create(); + for (CtConstructor constructor : c.getDeclaredConstructors()) { + m_constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor)); + } + m_staticInitializer = ""; + if (c.getClassInitializer() != null) { + m_staticInitializer = getBehaviorSignature(c.getClassInitializer()); + } + m_extends = ""; + if (c.getClassFile().getSuperclass() != null) { + m_extends = scrubClassName(c.getClassFile().getSuperclass()); + } + m_implements = HashMultiset.create(); + for (String interfaceName : c.getClassFile().getInterfaces()) { + m_implements.add(scrubClassName(interfaceName)); + } + + // stuff from the jar index + + m_implementations = HashMultiset.create(); + ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, m_classEntry); + if (implementationsNode != null) { + @SuppressWarnings("unchecked") + Enumeration implementations = implementationsNode.children(); + while (implementations.hasMoreElements()) { + ClassImplementationsTreeNode node = implementations.nextElement(); + m_implementations.add(scrubClassName(node.getClassEntry().getName())); + } + } + + m_references = HashMultiset.create(); + if (useReferences) { + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = new FieldEntry(m_classEntry, field.getName()); + for (EntryReference reference : index.getFieldReferences(fieldEntry)) { + addReference(reference); + } + } + for (CtMethod method : c.getDeclaredMethods()) { + MethodEntry methodEntry = new MethodEntry(m_classEntry, method.getName(), method.getSignature()); + for (EntryReference reference : index.getBehaviorReferences(methodEntry)) { + addReference(reference); + } + } + for (CtConstructor constructor : c.getDeclaredConstructors()) { + ConstructorEntry constructorEntry = new ConstructorEntry(m_classEntry, constructor.getSignature()); + for (EntryReference reference : index.getBehaviorReferences(constructorEntry)) { + addReference(reference); + } + } + } + } + + private void addReference(EntryReference reference) { + if (reference.context.getSignature() != null) { + m_references.add(String.format("%s_%s", scrubClassName(reference.context.getClassName()), scrubSignature(reference.context.getSignature()))); + } else { + m_references.add(String.format("%s_", scrubClassName(reference.context.getClassName()))); + } + } + + public ClassEntry getClassEntry() { + return m_classEntry; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("class: "); + buf.append(m_classEntry.getName()); + buf.append(" "); + buf.append(hashCode()); + buf.append("\n"); + for (String field : m_fields) { + buf.append("\tfield "); + buf.append(field); + buf.append("\n"); + } + for (String method : m_methods) { + buf.append("\tmethod "); + buf.append(method); + buf.append("\n"); + } + for (String constructor : m_constructors) { + buf.append("\tconstructor "); + buf.append(constructor); + buf.append("\n"); + } + if (m_staticInitializer.length() > 0) { + buf.append("\tinitializer "); + buf.append(m_staticInitializer); + buf.append("\n"); + } + if (m_extends.length() > 0) { + buf.append("\textends "); + buf.append(m_extends); + buf.append("\n"); + } + for (String interfaceName : m_implements) { + buf.append("\timplements "); + buf.append(interfaceName); + buf.append("\n"); + } + for (String implementation : m_implementations) { + buf.append("\timplemented by "); + buf.append(implementation); + buf.append("\n"); + } + for (String reference : m_references) { + buf.append("\treference "); + buf.append(reference); + buf.append("\n"); + } + return buf.toString(); + } + + private String scrubClassName(String className) { + return scrubSignature("L" + Descriptor.toJvmName(className) + ";"); + } + + private String scrubSignature(String signature) { + return SignatureUpdater.update(signature, new ClassNameUpdater() { + private Map m_classNames = Maps.newHashMap(); + + @Override + public String update(String className) { + // classes not in the none package can be passed through + ClassEntry classEntry = new ClassEntry(className); + if (!classEntry.getPackageName().equals(Constants.NonePackage)) { + return className; + } + + // is this class ourself? + if (className.equals(m_classEntry.getName())) { + return "CSelf"; + } + + // try the namer + if (m_namer != null) { + String newName = m_namer.getName(className); + if (newName != null) { + return newName; + } + } + + // otherwise, use local naming + if (!m_classNames.containsKey(className)) { + m_classNames.put(className, getNewClassName()); + } + return m_classNames.get(className); + } + + private String getNewClassName() { + return String.format("C%03d", m_classNames.size()); + } + }); + } + + private boolean isClassMatchedUniquely(String className) { + return m_namer != null && m_namer.getName(Descriptor.toJvmName(className)) != null; + } + + private String getBehaviorSignature(CtBehavior behavior) { + try { + // does this method have an implementation? + if (behavior.getMethodInfo().getCodeAttribute() == null) { + return "(none)"; + } + + // compute the hash from the opcodes + ConstPool constants = behavior.getMethodInfo().getConstPool(); + final MessageDigest digest = MessageDigest.getInstance("MD5"); + CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); + while (iter.hasNext()) { + int pos = iter.next(); + + // update the hash with the opcode + int opcode = iter.byteAt(pos); + digest.update((byte)opcode); + + switch (opcode) { + case Opcode.LDC: { + int constIndex = iter.byteAt(pos + 1); + updateHashWithConstant(digest, constants, constIndex); + } + break; + + case Opcode.LDC_W: + case Opcode.LDC2_W: { + int constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2); + updateHashWithConstant(digest, constants, constIndex); + } + break; + } + } + + // update hash with method and field accesses + behavior.instrument(new ExprEditor() { + @Override + public void edit(MethodCall call) { + updateHashWithString(digest, scrubClassName(call.getClassName())); + updateHashWithString(digest, scrubSignature(call.getSignature())); + if (isClassMatchedUniquely(call.getClassName())) { + updateHashWithString(digest, call.getMethodName()); + } + } + + @Override + public void edit(FieldAccess access) { + updateHashWithString(digest, scrubClassName(access.getClassName())); + updateHashWithString(digest, scrubSignature(access.getSignature())); + if (isClassMatchedUniquely(access.getClassName())) { + updateHashWithString(digest, access.getFieldName()); + } + } + + @Override + public void edit(ConstructorCall call) { + updateHashWithString(digest, scrubClassName(call.getClassName())); + updateHashWithString(digest, scrubSignature(call.getSignature())); + } + + @Override + public void edit(NewExpr expr) { + updateHashWithString(digest, scrubClassName(expr.getClassName())); + } + }); + + // convert the hash to a hex string + return toHex(digest.digest()); + } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) { + throw new Error(ex); + } + } + + private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) { + ConstPoolEditor editor = new ConstPoolEditor(constants); + ConstInfoAccessor item = editor.getItem(index); + if (item.getType() == InfoType.StringInfo) { + updateHashWithString(digest, constants.getStringInfo(index)); + } + // TODO: other constants + } + + private void updateHashWithString(MessageDigest digest, String val) { + try { + digest.update(val.getBytes("UTF8")); + } catch (UnsupportedEncodingException ex) { + throw new Error(ex); + } + } + + private String toHex(byte[] bytes) { + // function taken from: + // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java + final char[] hexArray = "0123456789ABCDEF".toCharArray(); + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } + + @Override + public boolean equals(Object other) { + if (other instanceof ClassIdentity) { + return equals((ClassIdentity)other); + } + return false; + } + + public boolean equals(ClassIdentity other) { + return m_fields.equals(other.m_fields) + && m_methods.equals(other.m_methods) + && m_constructors.equals(other.m_constructors) + && m_staticInitializer.equals(other.m_staticInitializer) + && m_extends.equals(other.m_extends) + && m_implements.equals(other.m_implements) + && m_implementations.equals(other.m_implementations) + && m_references.equals(other.m_references); + } + + @Override + public int hashCode() { + List objs = Lists.newArrayList(); + objs.addAll(m_fields); + objs.addAll(m_methods); + objs.addAll(m_constructors); + objs.add(m_staticInitializer); + objs.add(m_extends); + objs.addAll(m_implements); + objs.addAll(m_implementations); + objs.addAll(m_references); + return Util.combineHashesOrdered(objs); + } + + public int getMatchScore(ClassIdentity other) { + return getNumMatches(m_fields, other.m_fields) + + getNumMatches(m_methods, other.m_methods) + + getNumMatches(m_constructors, other.m_constructors); + } + + public int getMaxMatchScore() { + return m_fields.size() + m_methods.size() + m_constructors.size(); + } + + public boolean matches(CtClass c) { + // just compare declaration counts + return m_fields.size() == c.getDeclaredFields().length + && m_methods.size() == c.getDeclaredMethods().length + && m_constructors.size() == c.getDeclaredConstructors().length; + } + + private int getNumMatches(Multiset a, Multiset b) { + int numMatches = 0; + for (String val : a) { + if (b.contains(val)) { + numMatches++; + } + } + return numMatches; + } +} diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java new file mode 100644 index 00000000..fc39ed0c --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -0,0 +1,415 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarFile; + +import javassist.CtBehavior; +import javassist.CtClass; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.TranslatingTypeLoader; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.MappingParseException; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.MappingsWriter; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.MethodMapping; + +public class ClassMatcher { + + public static void main(String[] args) throws IOException, MappingParseException { + // TEMP + JarFile sourceJar = new JarFile(new File("input/1.8-pre3.jar")); + JarFile destJar = new JarFile(new File("input/1.8.jar")); + File inMappingsFile = new File("../Enigma Mappings/1.8-pre3.mappings"); + File outMappingsFile = new File("../Enigma Mappings/1.8.mappings"); + + // define a matching to use when the automated system cannot find a match + Map fallbackMatching = Maps.newHashMap(); + fallbackMatching.put("none/ayb", "none/ayf"); + fallbackMatching.put("none/ayd", "none/ayd"); + fallbackMatching.put("none/bgk", "unknown/bgk"); + + // do the conversion + Mappings mappings = new MappingsReader().read(new FileReader(inMappingsFile)); + convertMappings(sourceJar, destJar, mappings, fallbackMatching); + + // write out the converted mappings + FileWriter writer = new FileWriter(outMappingsFile); + new MappingsWriter().write(writer, mappings); + writer.close(); + System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); + } + + private static void convertMappings(JarFile sourceJar, JarFile destJar, Mappings mappings, Map fallbackMatching) { + // index jars + System.out.println("Indexing source jar..."); + JarIndex sourceIndex = new JarIndex(); + sourceIndex.indexJar(sourceJar, false); + System.out.println("Indexing dest jar..."); + JarIndex destIndex = new JarIndex(); + destIndex.indexJar(destJar, false); + TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader(sourceJar, sourceIndex); + TranslatingTypeLoader destLoader = new TranslatingTypeLoader(destJar, destIndex); + + // compute the matching + ClassMatching matching = computeMatching(sourceIndex, sourceLoader, destIndex, destLoader); + Map>> matchingIndex = matching.getIndex(); + + // get all the obf class names used in the mappings + Set usedClassNames = mappings.getAllObfClassNames(); + Set allClassNames = Sets.newHashSet(); + for (ClassEntry classEntry : sourceIndex.getObfClassEntries()) { + allClassNames.add(classEntry.getName()); + } + usedClassNames.retainAll(allClassNames); + System.out.println("Used " + usedClassNames.size() + " classes in the mappings"); + + // probabilistically match the non-uniquely-matched source classes + for (Map.Entry> entry : matchingIndex.values()) { + ClassIdentity sourceClass = entry.getKey(); + List destClasses = entry.getValue(); + + // skip classes that are uniquely matched + if (destClasses.size() == 1) { + continue; + } + + // skip classes that aren't used in the mappings + if (!usedClassNames.contains(sourceClass.getClassEntry().getName())) { + continue; + } + + System.out.println("No exact match for source class " + sourceClass.getClassEntry()); + + // find the closest classes + Multimap scoredMatches = ArrayListMultimap.create(); + for (ClassIdentity c : destClasses) { + scoredMatches.put(sourceClass.getMatchScore(c), c); + } + List scores = new ArrayList(scoredMatches.keySet()); + Collections.sort(scores, Collections.reverseOrder()); + printScoredMatches(sourceClass.getMaxMatchScore(), scores, scoredMatches); + + // does the best match have a non-zero score and the same name? + int bestScore = scores.get(0); + Collection bestMatches = scoredMatches.get(bestScore); + if (bestScore > 0 && bestMatches.size() == 1) { + ClassIdentity bestMatch = bestMatches.iterator().next(); + if (bestMatch.getClassEntry().equals(sourceClass.getClassEntry())) { + // use it + System.out.println("\tAutomatically choosing likely match: " + bestMatch.getClassEntry().getName()); + destClasses.clear(); + destClasses.add(bestMatch); + } + } + } + + // group the matching into unique and non-unique matches + BiMap matchedClassNames = HashBiMap.create(); + Set unmatchedSourceClassNames = Sets.newHashSet(); + for (String className : usedClassNames) { + // is there a match for this class? + Map.Entry> entry = matchingIndex.get(className); + ClassIdentity sourceClass = entry.getKey(); + List matches = entry.getValue(); + + if (matches.size() == 1) { + // unique match! We're good to go! + matchedClassNames.put(sourceClass.getClassEntry().getName(), matches.get(0).getClassEntry().getName()); + } else { + // no match, check the fallback matching + String fallbackMatch = fallbackMatching.get(className); + if (fallbackMatch != null) { + matchedClassNames.put(sourceClass.getClassEntry().getName(), fallbackMatch); + } else { + unmatchedSourceClassNames.add(className); + } + } + } + + // report unmatched classes + if (!unmatchedSourceClassNames.isEmpty()) { + System.err.println("ERROR: there were unmatched classes!"); + for (String className : unmatchedSourceClassNames) { + System.err.println("\t" + className); + } + return; + } + + // get the class name changes from the matched class names + Map classChanges = Maps.newHashMap(); + for (Map.Entry entry : matchedClassNames.entrySet()) { + if (!entry.getKey().equals(entry.getValue())) { + classChanges.put(entry.getKey(), entry.getValue()); + System.out.println(String.format("Class change: %s -> %s", entry.getKey(), entry.getValue())); + /* DEBUG + System.out.println(String.format("\n%s\n%s", + new ClassIdentity(sourceLoader.loadClass(entry.getKey()), null, sourceIndex, false, false), + new ClassIdentity( destLoader.loadClass(entry.getValue()), null, destIndex, false, false) + )); + */ + } + } + + // sort the changes so classes are renamed in the correct order + // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b + LinkedHashMap orderedClassChanges = Maps.newLinkedHashMap(); + int numChangesLeft = classChanges.size(); + while (!classChanges.isEmpty()) { + Iterator> iter = classChanges.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + if (classChanges.get(entry.getValue()) == null) { + orderedClassChanges.put(entry.getKey(), entry.getValue()); + iter.remove(); + } + } + + // did we remove any changes? + if (numChangesLeft - classChanges.size() > 0) { + // keep going + numChangesLeft = classChanges.size(); + } else { + // can't sort anymore. There must be a loop + break; + } + } + if (classChanges.size() > 0) { + throw new Error(String.format("Unable to sort %d/%d class changes!", classChanges.size(), matchedClassNames.size())); + } + + // convert the mappings in the correct class order + for (Map.Entry entry : orderedClassChanges.entrySet()) { + mappings.renameObfClass(entry.getKey(), entry.getValue()); + } + + // check the method matches + System.out.println("Checking methods..."); + for (ClassMapping classMapping : mappings.classes()) { + ClassEntry classEntry = new ClassEntry(classMapping.getObfName()); + for (MethodMapping methodMapping : classMapping.methods()) { + + // skip constructors + if (methodMapping.getObfName().equals("")) { + continue; + } + + MethodEntry methodEntry = new MethodEntry( + classEntry, + methodMapping.getObfName(), + methodMapping.getObfSignature() + ); + if (!destIndex.containsObfBehavior(methodEntry)) { + System.err.println("WARNING: method doesn't match: " + methodEntry); + + // show the available methods + System.err.println("\tAvailable dest methods:"); + CtClass c = destLoader.loadClass(classMapping.getObfName()); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + MethodEntry declaredMethodEntry = new MethodEntry( + new ClassEntry(classMapping.getObfName()), + behavior.getName(), + behavior.getSignature() + ); + System.err.println("\t\t" + declaredMethodEntry); + } + + System.err.println("\tAvailable source methods:"); + c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfName())); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + MethodEntry declaredMethodEntry = new MethodEntry( + new ClassEntry(classMapping.getObfName()), + behavior.getName(), + behavior.getSignature() + ); + System.err.println("\t\t" + declaredMethodEntry); + } + } + } + } + + System.out.println("Done!"); + } + + public static ClassMatching computeMatching(JarIndex sourceIndex, TranslatingTypeLoader sourceLoader, JarIndex destIndex, TranslatingTypeLoader destLoader) { + + System.out.println("Matching classes..."); + + ClassMatching matching = null; + for (boolean useReferences : Arrays.asList(false, true)) { + int numMatches = 0; + do { + SidedClassNamer sourceNamer = null; + SidedClassNamer destNamer = null; + if (matching != null) { + // build a class namer + ClassNamer namer = new ClassNamer(matching.getUniqueMatches()); + sourceNamer = namer.getSourceNamer(); + destNamer = namer.getDestNamer(); + + // note the number of matches + numMatches = matching.getUniqueMatches().size(); + } + + // get the entries left to match + Set sourceClassEntries = Sets.newHashSet(); + Set destClassEntries = Sets.newHashSet(); + if (matching == null) { + sourceClassEntries.addAll(sourceIndex.getObfClassEntries()); + destClassEntries.addAll(destIndex.getObfClassEntries()); + matching = new ClassMatching(); + } else { + for (Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet()) { + for (ClassIdentity c : entry.getKey()) { + sourceClassEntries.add(c.getClassEntry()); + matching.removeSource(c); + } + for (ClassIdentity c : entry.getValue()) { + destClassEntries.add(c.getClassEntry()); + matching.removeDest(c); + } + } + for (ClassIdentity c : matching.getUnmatchedSourceClasses()) { + sourceClassEntries.add(c.getClassEntry()); + matching.removeSource(c); + } + for (ClassIdentity c : matching.getUnmatchedDestClasses()) { + destClassEntries.add(c.getClassEntry()); + matching.removeDest(c); + } + } + + // compute a matching for the classes + for (ClassEntry classEntry : sourceClassEntries) { + CtClass c = sourceLoader.loadClass(classEntry.getName()); + ClassIdentity sourceClass = new ClassIdentity(c, sourceNamer, sourceIndex, useReferences); + matching.addSource(sourceClass); + } + for (ClassEntry classEntry : destClassEntries) { + CtClass c = destLoader.loadClass(classEntry.getName()); + ClassIdentity destClass = new ClassIdentity(c, destNamer, destIndex, useReferences); + matching.matchDestClass(destClass); + } + + // TEMP + System.out.println(matching); + } while (matching.getUniqueMatches().size() - numMatches > 0); + } + + // check the class matches + System.out.println("Checking class matches..."); + ClassNamer namer = new ClassNamer(matching.getUniqueMatches()); + SidedClassNamer sourceNamer = namer.getSourceNamer(); + SidedClassNamer destNamer = namer.getDestNamer(); + for (Map.Entry entry : matching.getUniqueMatches().entrySet()) { + + // check source + ClassIdentity sourceClass = entry.getKey(); + CtClass sourceC = sourceLoader.loadClass(sourceClass.getClassEntry().getName()); + assert (sourceC != null) : "Unable to load source class " + sourceClass.getClassEntry(); + assert (sourceClass.matches(sourceC)) : "Source " + sourceClass + " doesn't match " + new ClassIdentity(sourceC, sourceNamer, sourceIndex, false); + + // check dest + ClassIdentity destClass = entry.getValue(); + CtClass destC = destLoader.loadClass(destClass.getClassEntry().getName()); + assert (destC != null) : "Unable to load dest class " + destClass.getClassEntry(); + assert (destClass.matches(destC)) : "Dest " + destClass + " doesn't match " + new ClassIdentity(destC, destNamer, destIndex, false); + } + + // warn about the ambiguous matchings + List,List>> ambiguousMatches = new ArrayList,List>>(matching.getAmbiguousMatches().entrySet()); + Collections.sort(ambiguousMatches, new Comparator,List>>() { + @Override + public int compare(Map.Entry,List> a, Map.Entry,List> b) { + String aName = a.getKey().get(0).getClassEntry().getName(); + String bName = b.getKey().get(0).getClassEntry().getName(); + return aName.compareTo(bName); + } + }); + for (Map.Entry,List> entry : ambiguousMatches) { + System.out.println("Ambiguous matching:"); + System.out.println("\tSource: " + getClassNames(entry.getKey())); + System.out.println("\tDest: " + getClassNames(entry.getValue())); + } + + /* DEBUG + Map.Entry,List> entry = ambiguousMatches.get( 7 ); + for (ClassIdentity c : entry.getKey()) { + System.out.println(c); + } + for(ClassIdentity c : entry.getKey()) { + System.out.println(decompile(sourceLoader, c.getClassEntry())); + } + */ + + return matching; + } + + private static void printScoredMatches(int maxScore, List scores, Multimap scoredMatches) { + int numScoredMatchesShown = 0; + for (int score : scores) { + for (ClassIdentity scoredMatch : scoredMatches.get(score)) { + System.out.println(String.format("\tScore: %3d %3.0f%% %s", score, 100.0 * score / maxScore, scoredMatch.getClassEntry().getName())); + if (numScoredMatchesShown++ > 10) { + return; + } + } + } + } + + private static List getClassNames(Collection classes) { + List out = Lists.newArrayList(); + for (ClassIdentity c : classes) { + out.add(c.getClassEntry().getName()); + } + Collections.sort(out); + return out; + } + + /* DEBUG + private static String decompile(TranslatingTypeLoader loader, ClassEntry classEntry) { + PlainTextOutput output = new PlainTextOutput(); + DecompilerSettings settings = DecompilerSettings.javaDefaults(); + settings.setForceExplicitImports(true); + settings.setShowSyntheticMembers(true); + settings.setTypeLoader(loader); + Decompiler.decompile(classEntry.getName(), output, settings); + return output.toString(); + } + */ +} diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java new file mode 100644 index 00000000..53b6f7f4 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +public class ClassMatching { + + private Multimap m_sourceClasses; + private Multimap m_matchedDestClasses; + private List m_unmatchedDestClasses; + + public ClassMatching() { + m_sourceClasses = ArrayListMultimap.create(); + m_matchedDestClasses = ArrayListMultimap.create(); + m_unmatchedDestClasses = Lists.newArrayList(); + } + + public void addSource(ClassIdentity c) { + m_sourceClasses.put(c, c); + } + + public void matchDestClass(ClassIdentity destClass) { + Collection matchedSourceClasses = m_sourceClasses.get(destClass); + if (matchedSourceClasses.isEmpty()) { + // no match + m_unmatchedDestClasses.add(destClass); + } else { + // found a match + m_matchedDestClasses.put(destClass, destClass); + + // DEBUG + ClassIdentity sourceClass = matchedSourceClasses.iterator().next(); + assert (sourceClass.hashCode() == destClass.hashCode()); + assert (sourceClass.equals(destClass)); + } + } + + public void removeSource(ClassIdentity sourceClass) { + m_sourceClasses.remove(sourceClass, sourceClass); + } + + public void removeDest(ClassIdentity destClass) { + m_matchedDestClasses.remove(destClass, destClass); + m_unmatchedDestClasses.remove(destClass); + } + + public List getSourceClasses() { + return new ArrayList(m_sourceClasses.values()); + } + + public List getDestClasses() { + List classes = Lists.newArrayList(); + classes.addAll(m_matchedDestClasses.values()); + classes.addAll(m_unmatchedDestClasses); + return classes; + } + + public BiMap getUniqueMatches() { + BiMap uniqueMatches = HashBiMap.create(); + for (ClassIdentity sourceClass : m_sourceClasses.keySet()) { + Collection matchedSourceClasses = m_sourceClasses.get(sourceClass); + Collection matchedDestClasses = m_matchedDestClasses.get(sourceClass); + if (matchedSourceClasses.size() == 1 && matchedDestClasses.size() == 1) { + ClassIdentity matchedSourceClass = matchedSourceClasses.iterator().next(); + ClassIdentity matchedDestClass = matchedDestClasses.iterator().next(); + uniqueMatches.put(matchedSourceClass, matchedDestClass); + } + } + return uniqueMatches; + } + + public BiMap,List> getAmbiguousMatches() { + BiMap,List> ambiguousMatches = HashBiMap.create(); + for (ClassIdentity sourceClass : m_sourceClasses.keySet()) { + Collection matchedSourceClasses = m_sourceClasses.get(sourceClass); + Collection matchedDestClasses = m_matchedDestClasses.get(sourceClass); + if (matchedSourceClasses.size() > 1 && matchedDestClasses.size() > 1) { + ambiguousMatches.put( + new ArrayList(matchedSourceClasses), + new ArrayList(matchedDestClasses) + ); + } + } + return ambiguousMatches; + } + + public int getNumAmbiguousSourceMatches() { + int num = 0; + for (Map.Entry,List> entry : getAmbiguousMatches().entrySet()) { + num += entry.getKey().size(); + } + return num; + } + + public int getNumAmbiguousDestMatches() { + int num = 0; + for (Map.Entry,List> entry : getAmbiguousMatches().entrySet()) { + num += entry.getValue().size(); + } + return num; + } + + public List getUnmatchedSourceClasses() { + List classes = Lists.newArrayList(); + for (ClassIdentity sourceClass : getSourceClasses()) { + if (m_matchedDestClasses.get(sourceClass).isEmpty()) { + classes.add(sourceClass); + } + } + return classes; + } + + public List getUnmatchedDestClasses() { + return new ArrayList(m_unmatchedDestClasses); + } + + public Map>> getIndex() { + Map>> conversion = Maps.newHashMap(); + for (Map.Entry entry : getUniqueMatches().entrySet()) { + conversion.put( + entry.getKey().getClassEntry().getName(), + new AbstractMap.SimpleEntry>(entry.getKey(), Arrays.asList(entry.getValue())) + ); + } + for (Map.Entry,List> entry : getAmbiguousMatches().entrySet()) { + for (ClassIdentity sourceClass : entry.getKey()) { + conversion.put( + sourceClass.getClassEntry().getName(), + new AbstractMap.SimpleEntry>(sourceClass, entry.getValue()) + ); + } + } + for (ClassIdentity sourceClass : getUnmatchedSourceClasses()) { + conversion.put( + sourceClass.getClassEntry().getName(), + new AbstractMap.SimpleEntry>(sourceClass, getUnmatchedDestClasses()) + ); + } + return conversion; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(String.format("%12s%8s%8s\n", "", "Source", "Dest")); + buf.append(String.format("%12s%8d%8d\n", "Classes", getSourceClasses().size(), getDestClasses().size())); + buf.append(String.format("%12s%8d%8d\n", "Unique", getUniqueMatches().size(), getUniqueMatches().size())); + buf.append(String.format("%12s%8d%8d\n", "Ambiguous", getNumAmbiguousSourceMatches(), getNumAmbiguousDestMatches())); + buf.append(String.format("%12s%8d%8d\n", "Unmatched", getUnmatchedSourceClasses().size(), getUnmatchedDestClasses().size())); + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/convert/ClassNamer.java b/src/cuchaz/enigma/convert/ClassNamer.java new file mode 100644 index 00000000..1b6e81c8 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassNamer.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.util.Map; + +import com.google.common.collect.BiMap; +import com.google.common.collect.Maps; + +public class ClassNamer { + + public interface SidedClassNamer { + String getName(String name); + } + + private Map m_sourceNames; + private Map m_destNames; + + public ClassNamer(BiMap mappings) { + // convert the identity mappings to name maps + m_sourceNames = Maps.newHashMap(); + m_destNames = Maps.newHashMap(); + int i = 0; + for (Map.Entry entry : mappings.entrySet()) { + String name = String.format("M%04d", i++); + m_sourceNames.put(entry.getKey().getClassEntry().getName(), name); + m_destNames.put(entry.getValue().getClassEntry().getName(), name); + } + } + + public String getSourceName(String name) { + return m_sourceNames.get(name); + } + + public String getDestName(String name) { + return m_destNames.get(name); + } + + public SidedClassNamer getSourceNamer() { + return new SidedClassNamer() { + @Override + public String getName(String name) { + return getSourceName(name); + } + }; + } + + public SidedClassNamer getDestNamer() { + return new SidedClassNamer() { + @Override + public String getName(String name) { + return getDestName(name); + } + }; + } +} diff --git a/src/cuchaz/enigma/gui/AboutDialog.java b/src/cuchaz/enigma/gui/AboutDialog.java new file mode 100644 index 00000000..2476b564 --- /dev/null +++ b/src/cuchaz/enigma/gui/AboutDialog.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.WindowConstants; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.Util; + +public class AboutDialog { + + public static void show(JFrame parent) { + // init frame + final JFrame frame = new JFrame(Constants.Name + " - About"); + final Container pane = frame.getContentPane(); + pane.setLayout(new FlowLayout()); + + // load the content + try { + String html = Util.readResourceToString("/about.html"); + html = String.format(html, Constants.Name, Constants.Version); + JLabel label = new JLabel(html); + label.setHorizontalAlignment(JLabel.CENTER); + pane.add(label); + } catch (IOException ex) { + throw new Error(ex); + } + + // show the link + String html = "%s"; + html = String.format(html, Constants.Url, Constants.Url); + JButton link = new JButton(html); + link.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + Util.openUrl(Constants.Url); + } + }); + link.setBorderPainted(false); + link.setOpaque(false); + link.setBackground(Color.WHITE); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.setFocusable(false); + JPanel linkPanel = new JPanel(); + linkPanel.add(link); + pane.add(linkPanel); + + // show ok button + JButton okButton = new JButton("Ok"); + pane.add(okButton); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + frame.dispose(); + } + }); + + // show the frame + pane.doLayout(); + frame.setSize(400, 220); + frame.setResizable(false); + frame.setLocationRelativeTo(parent); + frame.setVisible(true); + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + } +} diff --git a/src/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/cuchaz/enigma/gui/BoxHighlightPainter.java new file mode 100644 index 00000000..db7c85b4 --- /dev/null +++ b/src/cuchaz/enigma/gui/BoxHighlightPainter.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; + +public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter { + + private Color m_fillColor; + private Color m_borderColor; + + protected BoxHighlightPainter(Color fillColor, Color borderColor) { + m_fillColor = fillColor; + m_borderColor = borderColor; + } + + @Override + public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { + Rectangle bounds = getBounds(text, start, end); + + // fill the area + if (m_fillColor != null) { + g.setColor(m_fillColor); + g.fillRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); + } + + // draw a box around the area + g.setColor(m_borderColor); + g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); + } + + protected static Rectangle getBounds(JTextComponent text, int start, int end) { + try { + // determine the bounds of the text + Rectangle bounds = text.modelToView(start).union(text.modelToView(end)); + + // adjust the box so it looks nice + bounds.x -= 2; + bounds.width += 2; + bounds.y += 1; + bounds.height -= 2; + + return bounds; + } catch (BadLocationException ex) { + // don't care... just return something + return new Rectangle(0, 0, 0, 0); + } + } +} diff --git a/src/cuchaz/enigma/gui/BrowserCaret.java b/src/cuchaz/enigma/gui/BrowserCaret.java new file mode 100644 index 00000000..acee4833 --- /dev/null +++ b/src/cuchaz/enigma/gui/BrowserCaret.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Graphics; +import java.awt.Shape; + +import javax.swing.text.DefaultCaret; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; + +public class BrowserCaret extends DefaultCaret { + + private static final long serialVersionUID = 1158977422507969940L; + + private static final Highlighter.HighlightPainter m_selectionPainter = new Highlighter.HighlightPainter() { + @Override + public void paint(Graphics g, int p0, int p1, Shape bounds, JTextComponent c) { + // don't paint anything + } + }; + + @Override + public boolean isSelectionVisible() { + return false; + } + + @Override + public boolean isVisible() { + return true; + } + + @Override + public Highlighter.HighlightPainter getSelectionPainter() { + return m_selectionPainter; + } +} diff --git a/src/cuchaz/enigma/gui/ClassListCellRenderer.java b/src/cuchaz/enigma/gui/ClassListCellRenderer.java new file mode 100644 index 00000000..d0f01e6a --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassListCellRenderer.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Component; + +import javassist.bytecode.Descriptor; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +public class ClassListCellRenderer implements ListCellRenderer { + + private DefaultListCellRenderer m_defaultRenderer; + + public ClassListCellRenderer() { + m_defaultRenderer = new DefaultListCellRenderer(); + } + + @Override + public Component getListCellRendererComponent(JList list, String className, int index, boolean isSelected, boolean hasFocus) { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent(list, className, index, isSelected, hasFocus); + label.setText(Descriptor.toJavaName(className)); + return label; + } +} diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java new file mode 100644 index 00000000..654bfbed --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassSelector.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +import cuchaz.enigma.mapping.ClassEntry; + +public class ClassSelector extends JTree { + + private static final long serialVersionUID = -7632046902384775977L; + + public interface ClassSelectionListener { + void onSelectClass(ClassEntry classEntry); + } + + public static Comparator ObfuscatedClassEntryComparator; + public static Comparator DeobfuscatedClassEntryComparator; + + static { + ObfuscatedClassEntryComparator = new Comparator() { + @Override + public int compare(ClassEntry a, ClassEntry b) { + if (a.getName().length() != b.getName().length()) { + return a.getName().length() - b.getName().length(); + } + return a.getName().compareTo(b.getName()); + } + }; + + DeobfuscatedClassEntryComparator = new Comparator() { + @Override + public int compare(ClassEntry a, ClassEntry b) { + return a.getName().compareTo(b.getName()); + } + }; + } + + private ClassSelectionListener m_listener; + private Comparator m_comparator; + + public ClassSelector(Comparator comparator) { + m_comparator = comparator; + + // configure the tree control + setRootVisible(false); + setShowsRootHandles(false); + setModel(null); + + // hook events + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (m_listener != null && event.getClickCount() == 2) { + // get the selected node + TreePath path = getSelectionPath(); + if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { + ClassSelectorClassNode node = (ClassSelectorClassNode)path.getLastPathComponent(); + m_listener.onSelectClass(node.getClassEntry()); + } + } + } + }); + + // init defaults + m_listener = null; + } + + public void setListener(ClassSelectionListener val) { + m_listener = val; + } + + public void setClasses(Collection classEntries) { + if (classEntries == null) { + setModel(null); + return; + } + + // build the package names + Map packages = Maps.newHashMap(); + for (ClassEntry classEntry : classEntries) { + packages.put(classEntry.getPackageName(), null); + } + + // sort the packages + List sortedPackageNames = Lists.newArrayList(packages.keySet()); + Collections.sort(sortedPackageNames, new Comparator() { + @Override + public int compare(String a, String b) { + // I can never keep this rule straight when writing these damn things... + // a < b => -1, a == b => 0, a > b => +1 + + String[] aparts = a.split("/"); + String[] bparts = b.split("/"); + for (int i = 0; true; i++) { + if (i >= aparts.length) { + return -1; + } else if (i >= bparts.length) { + return 1; + } + + int result = aparts[i].compareTo(bparts[i]); + if (result != 0) { + return result; + } + } + } + }); + + // create the root node and the package nodes + DefaultMutableTreeNode root = new DefaultMutableTreeNode(); + for (String packageName : sortedPackageNames) { + ClassSelectorPackageNode node = new ClassSelectorPackageNode(packageName); + packages.put(packageName, node); + root.add(node); + } + + // put the classes into packages + Multimap packagedClassEntries = ArrayListMultimap.create(); + for (ClassEntry classEntry : classEntries) { + packagedClassEntries.put(classEntry.getPackageName(), classEntry); + } + + // build the class nodes + for (String packageName : packagedClassEntries.keySet()) { + // sort the class entries + List classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); + Collections.sort(classEntriesInPackage, m_comparator); + + // create the nodes in order + for (ClassEntry classEntry : classEntriesInPackage) { + ClassSelectorPackageNode node = packages.get(packageName); + node.add(new ClassSelectorClassNode(classEntry)); + } + } + + // finally, update the tree control + setModel(new DefaultTreeModel(root)); + } +} diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java new file mode 100644 index 00000000..66e931b4 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import javax.swing.tree.DefaultMutableTreeNode; + +import cuchaz.enigma.mapping.ClassEntry; + +public class ClassSelectorClassNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = -8956754339813257380L; + + private ClassEntry m_classEntry; + + public ClassSelectorClassNode(ClassEntry classEntry) { + m_classEntry = classEntry; + } + + public ClassEntry getClassEntry() { + return m_classEntry; + } + + @Override + public String toString() { + return m_classEntry.getSimpleName(); + } +} diff --git a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java new file mode 100644 index 00000000..451d3809 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import javax.swing.tree.DefaultMutableTreeNode; + +public class ClassSelectorPackageNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = -3730868701219548043L; + + private String m_packageName; + + public ClassSelectorPackageNode(String packageName) { + m_packageName = packageName; + } + + public String getPackageName() { + return m_packageName; + } + + @Override + public String toString() { + return m_packageName; + } +} diff --git a/src/cuchaz/enigma/gui/CrashDialog.java b/src/cuchaz/enigma/gui/CrashDialog.java new file mode 100644 index 00000000..360091ab --- /dev/null +++ b/src/cuchaz/enigma/gui/CrashDialog.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.WindowConstants; + +import cuchaz.enigma.Constants; + +public class CrashDialog { + + private static CrashDialog m_instance = null; + + private JFrame m_frame; + private JTextArea m_text; + + private CrashDialog(JFrame parent) { + // init frame + m_frame = new JFrame(Constants.Name + " - Crash Report"); + final Container pane = m_frame.getContentPane(); + pane.setLayout(new BorderLayout()); + + JLabel label = new JLabel(Constants.Name + " has crashed! =("); + label.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + pane.add(label, BorderLayout.NORTH); + + // report panel + m_text = new JTextArea(); + m_text.setTabSize(2); + pane.add(new JScrollPane(m_text), BorderLayout.CENTER); + + // buttons panel + JPanel buttonsPanel = new JPanel(); + FlowLayout buttonsLayout = new FlowLayout(); + buttonsLayout.setAlignment(FlowLayout.RIGHT); + buttonsPanel.setLayout(buttonsLayout); + buttonsPanel.add(GuiTricks.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work."))); + JButton ignoreButton = new JButton("Ignore"); + ignoreButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + // close (hide) the dialog + m_frame.setVisible(false); + } + }); + buttonsPanel.add(ignoreButton); + JButton exitButton = new JButton("Exit"); + exitButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + // exit enigma + System.exit(1); + } + }); + buttonsPanel.add(exitButton); + pane.add(buttonsPanel, BorderLayout.SOUTH); + + // show the frame + m_frame.setSize(600, 400); + m_frame.setLocationRelativeTo(parent); + m_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + } + + public static void init(JFrame parent) { + m_instance = new CrashDialog(parent); + } + + public static void show(Throwable ex) { + // get the error report + StringWriter buf = new StringWriter(); + ex.printStackTrace(new PrintWriter(buf)); + String report = buf.toString(); + + // show it! + m_instance.m_text.setText(report); + m_instance.m_frame.doLayout(); + m_instance.m_frame.setVisible(true); + } +} diff --git a/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java new file mode 100644 index 00000000..26a31639 --- /dev/null +++ b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; + +public class DeobfuscatedHighlightPainter extends BoxHighlightPainter { + + public DeobfuscatedHighlightPainter() { + // green ish + super(new Color(220, 255, 220), new Color(80, 160, 80)); + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java new file mode 100644 index 00000000..e652202c --- /dev/null +++ b/src/cuchaz/enigma/gui/Gui.java @@ -0,0 +1,1164 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridLayout; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.IOException; +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Vector; +import java.util.jar.JarFile; + +import javax.swing.BorderFactory; +import javax.swing.JEditorPane; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.KeyStroke; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.Timer; +import javax.swing.WindowConstants; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import jsyntaxpane.DefaultSyntaxKit; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.FieldReferenceTreeNode; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; +import cuchaz.enigma.analysis.ReferenceTreeNode; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.IllegalNameException; +import cuchaz.enigma.mapping.MappingParseException; +import cuchaz.enigma.mapping.MethodEntry; + +public class Gui { + + private GuiController m_controller; + + // controls + private JFrame m_frame; + private ClassSelector m_obfClasses; + private ClassSelector m_deobfClasses; + private JEditorPane m_editor; + private JPanel m_classesPanel; + private JSplitPane m_splitClasses; + private JPanel m_infoPanel; + private ObfuscatedHighlightPainter m_obfuscatedHighlightPainter; + private DeobfuscatedHighlightPainter m_deobfuscatedHighlightPainter; + private OtherHighlightPainter m_otherHighlightPainter; + private SelectionHighlightPainter m_selectionHighlightPainter; + private JTree m_inheritanceTree; + private JTree m_implementationsTree; + private JTree m_callsTree; + private JList m_tokens; + private JTabbedPane m_tabs; + + // dynamic menu items + private JMenuItem m_closeJarMenu; + private JMenuItem m_openMappingsMenu; + private JMenuItem m_saveMappingsMenu; + private JMenuItem m_saveMappingsAsMenu; + private JMenuItem m_closeMappingsMenu; + private JMenuItem m_renameMenu; + private JMenuItem m_showInheritanceMenu; + private JMenuItem m_openEntryMenu; + private JMenuItem m_openPreviousMenu; + private JMenuItem m_showCallsMenu; + private JMenuItem m_showImplementationsMenu; + private JMenuItem m_toggleMappingMenu; + private JMenuItem m_exportSourceMenu; + private JMenuItem m_exportJarMenu; + + // state + private EntryReference m_reference; + private JFileChooser m_jarFileChooser; + private JFileChooser m_mappingsFileChooser; + private JFileChooser m_exportSourceFileChooser; + private JFileChooser m_exportJarFileChooser; + + public Gui() { + + // init frame + m_frame = new JFrame(Constants.Name); + final Container pane = m_frame.getContentPane(); + pane.setLayout(new BorderLayout()); + + if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) { + // install a global exception handler to the event thread + CrashDialog.init(m_frame); + Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread thread, Throwable ex) { + ex.printStackTrace(System.err); + CrashDialog.show(ex); + } + }); + } + + m_controller = new GuiController(this); + + // init file choosers + m_jarFileChooser = new JFileChooser(); + m_mappingsFileChooser = new JFileChooser(); + m_exportSourceFileChooser = new JFileChooser(); + m_exportSourceFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + m_exportJarFileChooser = new JFileChooser(); + + // init obfuscated classes list + m_obfClasses = new ClassSelector(ClassSelector.ObfuscatedClassEntryComparator); + m_obfClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + navigateTo(classEntry); + } + }); + JScrollPane obfScroller = new JScrollPane(m_obfClasses); + JPanel obfPanel = new JPanel(); + obfPanel.setLayout(new BorderLayout()); + obfPanel.add(new JLabel("Obfuscated Classes"), BorderLayout.NORTH); + obfPanel.add(obfScroller, BorderLayout.CENTER); + + // init deobfuscated classes list + m_deobfClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); + m_deobfClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + navigateTo(classEntry); + } + }); + JScrollPane deobfScroller = new JScrollPane(m_deobfClasses); + JPanel deobfPanel = new JPanel(); + deobfPanel.setLayout(new BorderLayout()); + deobfPanel.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH); + deobfPanel.add(deobfScroller, BorderLayout.CENTER); + + // set up classes panel (don't add the splitter yet) + m_splitClasses = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel); + m_splitClasses.setResizeWeight(0.3); + m_classesPanel = new JPanel(); + m_classesPanel.setLayout(new BorderLayout()); + m_classesPanel.setPreferredSize(new Dimension(250, 0)); + + // init info panel + m_infoPanel = new JPanel(); + m_infoPanel.setLayout(new GridLayout(4, 1, 0, 0)); + m_infoPanel.setPreferredSize(new Dimension(0, 100)); + m_infoPanel.setBorder(BorderFactory.createTitledBorder("Identifier Info")); + clearReference(); + + // init editor + DefaultSyntaxKit.initKit(); + m_obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); + m_deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); + m_otherHighlightPainter = new OtherHighlightPainter(); + m_selectionHighlightPainter = new SelectionHighlightPainter(); + m_editor = new JEditorPane(); + m_editor.setEditable(false); + m_editor.setCaret(new BrowserCaret()); + JScrollPane sourceScroller = new JScrollPane(m_editor); + m_editor.setContentType("text/java"); + m_editor.addCaretListener(new CaretListener() { + @Override + public void caretUpdate(CaretEvent event) { + onCaretMove(event.getDot()); + } + }); + m_editor.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent event) { + switch (event.getKeyCode()) { + case KeyEvent.VK_R: + m_renameMenu.doClick(); + break; + + case KeyEvent.VK_I: + m_showInheritanceMenu.doClick(); + break; + + case KeyEvent.VK_M: + m_showImplementationsMenu.doClick(); + break; + + case KeyEvent.VK_N: + m_openEntryMenu.doClick(); + break; + + case KeyEvent.VK_P: + m_openPreviousMenu.doClick(); + break; + + case KeyEvent.VK_C: + m_showCallsMenu.doClick(); + break; + + case KeyEvent.VK_T: + m_toggleMappingMenu.doClick(); + break; + } + } + }); + + // turn off token highlighting (it's wrong most of the time anyway...) + DefaultSyntaxKit kit = (DefaultSyntaxKit)m_editor.getEditorKit(); + kit.toggleComponent(m_editor, "jsyntaxpane.components.TokenMarker"); + + // init editor popup menu + JPopupMenu popupMenu = new JPopupMenu(); + m_editor.setComponentPopupMenu(popupMenu); + { + JMenuItem menu = new JMenuItem("Rename"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + startRename(); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_renameMenu = menu; + } + { + JMenuItem menu = new JMenuItem("Show Inheritance"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + showInheritance(); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_showInheritanceMenu = menu; + } + { + JMenuItem menu = new JMenuItem("Show Implementations"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + showImplementations(); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_showImplementationsMenu = menu; + } + { + JMenuItem menu = new JMenuItem("Show Calls"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + showCalls(); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_showCallsMenu = menu; + } + { + JMenuItem menu = new JMenuItem("Go to Declaration"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + navigateTo(m_reference.entry); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_openEntryMenu = menu; + } + { + JMenuItem menu = new JMenuItem("Go to previous"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + m_controller.openPreviousReference(); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_openPreviousMenu = menu; + } + { + JMenuItem menu = new JMenuItem("Mark as deobfuscated"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + toggleMapping(); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_toggleMappingMenu = menu; + } + + // init inheritance panel + m_inheritanceTree = new JTree(); + m_inheritanceTree.setModel(null); + m_inheritanceTree.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + // get the selected node + TreePath path = m_inheritanceTree.getSelectionPath(); + if (path == null) { + return; + } + + Object node = path.getLastPathComponent(); + if (node instanceof ClassInheritanceTreeNode) { + ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode)node; + navigateTo(new ClassEntry(classNode.getObfClassName())); + } else if (node instanceof MethodInheritanceTreeNode) { + MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode)node; + if (methodNode.isImplemented()) { + navigateTo(methodNode.getMethodEntry()); + } + } + } + } + }); + JPanel inheritancePanel = new JPanel(); + inheritancePanel.setLayout(new BorderLayout()); + inheritancePanel.add(new JScrollPane(m_inheritanceTree)); + + // init implementations panel + m_implementationsTree = new JTree(); + m_implementationsTree.setModel(null); + m_implementationsTree.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + // get the selected node + TreePath path = m_implementationsTree.getSelectionPath(); + if (path == null) { + return; + } + + Object node = path.getLastPathComponent(); + if (node instanceof ClassImplementationsTreeNode) { + ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode)node; + navigateTo(classNode.getClassEntry()); + } else if (node instanceof MethodImplementationsTreeNode) { + MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode)node; + navigateTo(methodNode.getMethodEntry()); + } + } + } + }); + JPanel implementationsPanel = new JPanel(); + implementationsPanel.setLayout(new BorderLayout()); + implementationsPanel.add(new JScrollPane(m_implementationsTree)); + + // init call panel + m_callsTree = new JTree(); + m_callsTree.setModel(null); + m_callsTree.addMouseListener(new MouseAdapter() { + @SuppressWarnings("unchecked") + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + // get the selected node + TreePath path = m_callsTree.getSelectionPath(); + if (path == null) { + return; + } + + Object node = path.getLastPathComponent(); + if (node instanceof ReferenceTreeNode) { + ReferenceTreeNode referenceNode = ((ReferenceTreeNode)node); + if (referenceNode.getReference() != null) { + navigateTo(referenceNode.getReference()); + } else { + navigateTo(referenceNode.getEntry()); + } + } + } + } + }); + m_tokens = new JList(); + m_tokens.setCellRenderer(new TokenListCellRenderer(m_controller)); + m_tokens.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + m_tokens.setLayoutOrientation(JList.VERTICAL); + m_tokens.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + Token selected = m_tokens.getSelectedValue(); + if (selected != null) { + showToken(selected); + } + } + } + }); + m_tokens.setPreferredSize(new Dimension(0, 200)); + m_tokens.setMinimumSize(new Dimension(0, 200)); + JSplitPane callPanel = new JSplitPane( + JSplitPane.VERTICAL_SPLIT, + true, + new JScrollPane(m_callsTree), + new JScrollPane(m_tokens) + ); + callPanel.setResizeWeight(1); // let the top side take all the slack + callPanel.resetToPreferredSizes(); + + // layout controls + JPanel centerPanel = new JPanel(); + centerPanel.setLayout(new BorderLayout()); + centerPanel.add(m_infoPanel, BorderLayout.NORTH); + centerPanel.add(sourceScroller, BorderLayout.CENTER); + m_tabs = new JTabbedPane(); + m_tabs.setPreferredSize(new Dimension(250, 0)); + m_tabs.addTab("Inheritance", inheritancePanel); + m_tabs.addTab("Implementations", implementationsPanel); + m_tabs.addTab("Call Graph", callPanel); + JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, m_tabs); + splitRight.setResizeWeight(1); // let the left side take all the slack + splitRight.resetToPreferredSizes(); + JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, m_classesPanel, splitRight); + splitCenter.setResizeWeight(0); // let the right side take all the slack + pane.add(splitCenter, BorderLayout.CENTER); + + // init menus + JMenuBar menuBar = new JMenuBar(); + m_frame.setJMenuBar(menuBar); + { + JMenu menu = new JMenu("File"); + menuBar.add(menu); + { + JMenuItem item = new JMenuItem("Open Jar..."); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (m_jarFileChooser.showOpenDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + // load the jar in a separate thread + new Thread() { + @Override + public void run() { + try { + m_controller.openJar(new JarFile(m_jarFileChooser.getSelectedFile())); + } catch (IOException ex) { + throw new Error(ex); + } + } + }.start(); + } + } + }); + } + { + JMenuItem item = new JMenuItem("Close Jar"); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + m_controller.closeJar(); + } + }); + m_closeJarMenu = item; + } + menu.addSeparator(); + { + JMenuItem item = new JMenuItem("Open Mappings..."); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (m_mappingsFileChooser.showOpenDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + try { + m_controller.openMappings(m_mappingsFileChooser.getSelectedFile()); + } catch (IOException ex) { + throw new Error(ex); + } catch (MappingParseException ex) { + JOptionPane.showMessageDialog(m_frame, ex.getMessage()); + } + } + } + }); + m_openMappingsMenu = item; + } + { + JMenuItem item = new JMenuItem("Save Mappings"); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + try { + m_controller.saveMappings(m_mappingsFileChooser.getSelectedFile()); + } catch (IOException ex) { + throw new Error(ex); + } + } + }); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); + m_saveMappingsMenu = item; + } + { + JMenuItem item = new JMenuItem("Save Mappings As..."); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (m_mappingsFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + try { + m_controller.saveMappings(m_mappingsFileChooser.getSelectedFile()); + m_saveMappingsMenu.setEnabled(true); + } catch (IOException ex) { + throw new Error(ex); + } + } + } + }); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); + m_saveMappingsAsMenu = item; + } + { + JMenuItem item = new JMenuItem("Close Mappings"); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + m_controller.closeMappings(); + } + }); + m_closeMappingsMenu = item; + } + menu.addSeparator(); + { + JMenuItem item = new JMenuItem("Export Source..."); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (m_exportSourceFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + m_controller.exportSource(m_exportSourceFileChooser.getSelectedFile()); + } + } + }); + m_exportSourceMenu = item; + } + { + JMenuItem item = new JMenuItem("Export Jar..."); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (m_exportJarFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + m_controller.exportJar(m_exportJarFileChooser.getSelectedFile()); + } + } + }); + m_exportJarMenu = item; + } + menu.addSeparator(); + { + JMenuItem item = new JMenuItem("Exit"); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + close(); + } + }); + } + } + { + JMenu menu = new JMenu("Help"); + menuBar.add(menu); + { + JMenuItem item = new JMenuItem("About"); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + AboutDialog.show(m_frame); + } + }); + } + } + + // init state + onCloseJar(); + + m_frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent event) { + close(); + } + }); + + // show the frame + pane.doLayout(); + m_frame.setSize(1024, 576); + m_frame.setMinimumSize(new Dimension(640, 480)); + m_frame.setVisible(true); + m_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + } + + public JFrame getFrame() { + return m_frame; + } + + public GuiController getController() { + return m_controller; + } + + public void onStartOpenJar() { + m_classesPanel.removeAll(); + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(new JLabel("Loading...")); + m_classesPanel.add(panel); + redraw(); + } + + public void onFinishOpenJar(String jarName) { + // update gui + m_frame.setTitle(Constants.Name + " - " + jarName); + m_classesPanel.removeAll(); + m_classesPanel.add(m_splitClasses); + setSource(null); + + // update menu + m_closeJarMenu.setEnabled(true); + m_openMappingsMenu.setEnabled(true); + m_saveMappingsMenu.setEnabled(false); + m_saveMappingsAsMenu.setEnabled(true); + m_closeMappingsMenu.setEnabled(true); + m_exportSourceMenu.setEnabled(true); + m_exportJarMenu.setEnabled(true); + + redraw(); + } + + public void onCloseJar() { + // update gui + m_frame.setTitle(Constants.Name); + setObfClasses(null); + setDeobfClasses(null); + setSource(null); + m_classesPanel.removeAll(); + + // update menu + m_closeJarMenu.setEnabled(false); + m_openMappingsMenu.setEnabled(false); + m_saveMappingsMenu.setEnabled(false); + m_saveMappingsAsMenu.setEnabled(false); + m_closeMappingsMenu.setEnabled(false); + m_exportSourceMenu.setEnabled(false); + m_exportJarMenu.setEnabled(false); + + redraw(); + } + + public void setObfClasses(Collection obfClasses) { + m_obfClasses.setClasses(obfClasses); + } + + public void setDeobfClasses(Collection deobfClasses) { + m_deobfClasses.setClasses(deobfClasses); + } + + public void setMappingsFile(File file) { + m_mappingsFileChooser.setSelectedFile(file); + m_saveMappingsMenu.setEnabled(file != null); + } + + public void setSource(String source) { + m_editor.getHighlighter().removeAllHighlights(); + m_editor.setText(source); + } + + public void showToken(final Token token) { + if (token == null) { + throw new IllegalArgumentException("Token cannot be null!"); + } + + // set the caret position to the token + m_editor.setCaretPosition(token.start); + m_editor.grabFocus(); + + try { + // make sure the token is visible in the scroll window + Rectangle start = m_editor.modelToView(token.start); + Rectangle end = m_editor.modelToView(token.end); + final Rectangle show = start.union(end); + show.grow(start.width * 10, start.height * 6); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + m_editor.scrollRectToVisible(show); + } + }); + } catch (BadLocationException ex) { + throw new Error(ex); + } + + // highlight the token momentarily + final Timer timer = new Timer(200, new ActionListener() { + private int m_counter = 0; + private Object m_highlight = null; + + @Override + public void actionPerformed(ActionEvent event) { + if (m_counter % 2 == 0) { + try { + m_highlight = m_editor.getHighlighter().addHighlight(token.start, token.end, m_selectionHighlightPainter); + } catch (BadLocationException ex) { + // don't care + } + } else if (m_highlight != null) { + m_editor.getHighlighter().removeHighlight(m_highlight); + } + + if (m_counter++ > 6) { + Timer timer = (Timer)event.getSource(); + timer.stop(); + } + } + }); + timer.start(); + + redraw(); + } + + public void showTokens(Collection tokens) { + Vector sortedTokens = new Vector(tokens); + Collections.sort(sortedTokens); + if (sortedTokens.size() > 1) { + // sort the tokens and update the tokens panel + m_tokens.setListData(sortedTokens); + m_tokens.setSelectedIndex(0); + } else { + m_tokens.setListData(new Vector()); + } + + // show the first token + showToken(sortedTokens.get(0)); + } + + public void setHighlightedTokens(Iterable obfuscatedTokens, Iterable deobfuscatedTokens, Iterable otherTokens) { + + // remove any old highlighters + m_editor.getHighlighter().removeAllHighlights(); + + // color things based on the index + if (obfuscatedTokens != null) { + setHighlightedTokens(obfuscatedTokens, m_obfuscatedHighlightPainter); + } + if (deobfuscatedTokens != null) { + setHighlightedTokens(deobfuscatedTokens, m_deobfuscatedHighlightPainter); + } + if (otherTokens != null) { + setHighlightedTokens(otherTokens, m_otherHighlightPainter); + } + + redraw(); + } + + private void setHighlightedTokens(Iterable tokens, Highlighter.HighlightPainter painter) { + for (Token token : tokens) { + try { + m_editor.getHighlighter().addHighlight(token.start, token.end, painter); + } catch (BadLocationException ex) { + throw new IllegalArgumentException(ex); + } + } + } + + private void clearReference() { + m_infoPanel.removeAll(); + JLabel label = new JLabel("No identifier selected"); + GuiTricks.unboldLabel(label); + label.setHorizontalAlignment(JLabel.CENTER); + m_infoPanel.add(label); + + redraw(); + } + + private void showReference(EntryReference reference) { + if (reference == null) { + clearReference(); + return; + } + + m_reference = reference; + + m_infoPanel.removeAll(); + if (reference.entry instanceof ClassEntry) { + showClassEntry((ClassEntry)m_reference.entry); + } else if (m_reference.entry instanceof FieldEntry) { + showFieldEntry((FieldEntry)m_reference.entry); + } else if (m_reference.entry instanceof MethodEntry) { + showMethodEntry((MethodEntry)m_reference.entry); + } else if (m_reference.entry instanceof ConstructorEntry) { + showConstructorEntry((ConstructorEntry)m_reference.entry); + } else if (m_reference.entry instanceof ArgumentEntry) { + showArgumentEntry((ArgumentEntry)m_reference.entry); + } else { + throw new Error("Unknown entry type: " + m_reference.entry.getClass().getName()); + } + + redraw(); + } + + private void showClassEntry(ClassEntry entry) { + addNameValue(m_infoPanel, "Class", entry.getName()); + } + + private void showFieldEntry(FieldEntry entry) { + addNameValue(m_infoPanel, "Field", entry.getName()); + addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); + } + + private void showMethodEntry(MethodEntry entry) { + addNameValue(m_infoPanel, "Method", entry.getName()); + addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); + addNameValue(m_infoPanel, "Signature", entry.getSignature()); + } + + private void showConstructorEntry(ConstructorEntry entry) { + addNameValue(m_infoPanel, "Constructor", entry.getClassEntry().getName()); + addNameValue(m_infoPanel, "Signature", entry.getSignature()); + } + + private void showArgumentEntry(ArgumentEntry entry) { + addNameValue(m_infoPanel, "Argument", entry.getName()); + addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); + addNameValue(m_infoPanel, "Method", entry.getBehaviorEntry().getName()); + addNameValue(m_infoPanel, "Index", Integer.toString(entry.getIndex())); + } + + private void addNameValue(JPanel container, String name, String value) { + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); + container.add(panel); + + JLabel label = new JLabel(name + ":", JLabel.RIGHT); + label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); + panel.add(label); + + panel.add(GuiTricks.unboldLabel(new JLabel(value, JLabel.LEFT))); + } + + private void onCaretMove(int pos) { + + Token token = m_controller.getToken(pos); + boolean isToken = token != null; + + m_reference = m_controller.getDeobfReference(token); + boolean isClassEntry = isToken && m_reference.entry instanceof ClassEntry; + boolean isFieldEntry = isToken && m_reference.entry instanceof FieldEntry; + boolean isMethodEntry = isToken && m_reference.entry instanceof MethodEntry; + boolean isConstructorEntry = isToken && m_reference.entry instanceof ConstructorEntry; + boolean isInJar = isToken && m_controller.entryIsInJar(m_reference.entry); + boolean isRenameable = isToken && m_controller.referenceIsRenameable(m_reference); + + if (isToken) { + showReference(m_reference); + } else { + clearReference(); + } + + m_renameMenu.setEnabled(isRenameable && isToken); + m_showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); + m_showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); + m_showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); + m_openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); + m_openPreviousMenu.setEnabled(m_controller.hasPreviousLocation()); + m_toggleMappingMenu.setEnabled(isRenameable && isToken); + + if (isToken && m_controller.entryHasDeobfuscatedName(m_reference.entry)) { + m_toggleMappingMenu.setText("Reset to obfuscated"); + } else { + m_toggleMappingMenu.setText("Mark as deobfuscated"); + } + } + + private void navigateTo(Entry entry) { + if (!m_controller.entryIsInJar(entry)) { + // entry is not in the jar. Ignore it + return; + } + if (m_reference != null) { + m_controller.savePreviousReference(m_reference); + } + m_controller.openDeclaration(entry); + } + + private void navigateTo(EntryReference reference) { + if (!m_controller.entryIsInJar(reference.getLocationClassEntry())) { + // reference is not in the jar. Ignore it + return; + } + if (m_reference != null) { + m_controller.savePreviousReference(m_reference); + } + m_controller.openReference(reference); + } + + private void startRename() { + + // init the text box + final JTextField text = new JTextField(); + text.setText(m_reference.getNamableName()); + text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); + text.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent event) { + switch (event.getKeyCode()) { + case KeyEvent.VK_ENTER: + finishRename(text, true); + break; + + case KeyEvent.VK_ESCAPE: + finishRename(text, false); + break; + } + } + }); + + // find the label with the name and replace it with the text box + JPanel panel = (JPanel)m_infoPanel.getComponent(0); + panel.remove(panel.getComponentCount() - 1); + panel.add(text); + text.grabFocus(); + text.selectAll(); + + redraw(); + } + + private void finishRename(JTextField text, boolean saveName) { + String newName = text.getText(); + if (saveName && newName != null && newName.length() > 0) { + try { + m_controller.rename(m_reference, newName); + } catch (IllegalNameException ex) { + text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); + text.setToolTipText(ex.getReason()); + GuiTricks.showToolTipNow(text); + } + return; + } + + // abort the rename + JPanel panel = (JPanel)m_infoPanel.getComponent(0); + panel.remove(panel.getComponentCount() - 1); + panel.add(GuiTricks.unboldLabel(new JLabel(m_reference.getNamableName(), JLabel.LEFT))); + + m_editor.grabFocus(); + + redraw(); + } + + private void showInheritance() { + + if (m_reference == null) { + return; + } + + m_inheritanceTree.setModel(null); + + if (m_reference.entry instanceof ClassEntry) { + // get the class inheritance + ClassInheritanceTreeNode classNode = m_controller.getClassInheritance((ClassEntry)m_reference.entry); + + // show the tree at the root + TreePath path = getPathToRoot(classNode); + m_inheritanceTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0))); + m_inheritanceTree.expandPath(path); + m_inheritanceTree.setSelectionRow(m_inheritanceTree.getRowForPath(path)); + } else if (m_reference.entry instanceof MethodEntry) { + // get the method inheritance + MethodInheritanceTreeNode classNode = m_controller.getMethodInheritance((MethodEntry)m_reference.entry); + + // show the tree at the root + TreePath path = getPathToRoot(classNode); + m_inheritanceTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0))); + m_inheritanceTree.expandPath(path); + m_inheritanceTree.setSelectionRow(m_inheritanceTree.getRowForPath(path)); + } + + m_tabs.setSelectedIndex(0); + redraw(); + } + + private void showImplementations() { + + if (m_reference == null) { + return; + } + + m_implementationsTree.setModel(null); + + if (m_reference.entry instanceof ClassEntry) { + // get the class implementations + ClassImplementationsTreeNode node = m_controller.getClassImplementations((ClassEntry)m_reference.entry); + if (node != null) { + // show the tree at the root + TreePath path = getPathToRoot(node); + m_implementationsTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0))); + m_implementationsTree.expandPath(path); + m_implementationsTree.setSelectionRow(m_implementationsTree.getRowForPath(path)); + } + } else if (m_reference.entry instanceof MethodEntry) { + // get the method implementations + MethodImplementationsTreeNode node = m_controller.getMethodImplementations((MethodEntry)m_reference.entry); + if (node != null) { + // show the tree at the root + TreePath path = getPathToRoot(node); + m_implementationsTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0))); + m_implementationsTree.expandPath(path); + m_implementationsTree.setSelectionRow(m_implementationsTree.getRowForPath(path)); + } + } + + m_tabs.setSelectedIndex(1); + redraw(); + } + + private void showCalls() { + + if (m_reference == null) { + return; + } + + if (m_reference.entry instanceof ClassEntry) { + // look for calls to the default constructor + // TODO: get a list of all the constructors and find calls to all of them + BehaviorReferenceTreeNode node = m_controller.getMethodReferences(new ConstructorEntry((ClassEntry)m_reference.entry, "()V")); + m_callsTree.setModel(new DefaultTreeModel(node)); + } else if (m_reference.entry instanceof FieldEntry) { + FieldReferenceTreeNode node = m_controller.getFieldReferences((FieldEntry)m_reference.entry); + m_callsTree.setModel(new DefaultTreeModel(node)); + } else if (m_reference.entry instanceof MethodEntry) { + BehaviorReferenceTreeNode node = m_controller.getMethodReferences((MethodEntry)m_reference.entry); + m_callsTree.setModel(new DefaultTreeModel(node)); + } else if (m_reference.entry instanceof ConstructorEntry) { + BehaviorReferenceTreeNode node = m_controller.getMethodReferences((ConstructorEntry)m_reference.entry); + m_callsTree.setModel(new DefaultTreeModel(node)); + } + + m_tabs.setSelectedIndex(2); + redraw(); + } + + private void toggleMapping() { + if (m_controller.entryHasDeobfuscatedName(m_reference.entry)) { + m_controller.removeMapping(m_reference); + } else { + m_controller.markAsDeobfuscated(m_reference); + } + } + + private TreePath getPathToRoot(TreeNode node) { + List nodes = Lists.newArrayList(); + TreeNode n = node; + do { + nodes.add(n); + n = n.getParent(); + } while (n != null); + Collections.reverse(nodes); + return new TreePath(nodes.toArray()); + } + + private void close() { + if (!m_controller.isDirty()) { + // everything is saved, we can exit safely + m_frame.dispose(); + } else { + // ask to save before closing + String[] options = { "Save and exit", "Discard changes", "Cancel" }; + int response = JOptionPane.showOptionDialog(m_frame, "Your mappings have not been saved yet. Do you want to save?", "Save your changes?", JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, null, options, options[2]); + switch (response) { + case JOptionPane.YES_OPTION: // save and exit + if (m_mappingsFileChooser.getSelectedFile() != null || m_mappingsFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + try { + m_controller.saveMappings(m_mappingsFileChooser.getSelectedFile()); + m_frame.dispose(); + } catch (IOException ex) { + throw new Error(ex); + } + } + break; + + case JOptionPane.NO_OPTION: + // don't save, exit + m_frame.dispose(); + break; + + // cancel means do nothing + } + } + } + + private void redraw() { + m_frame.validate(); + m_frame.repaint(); + } +} diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java new file mode 100644 index 00000000..61fea9c0 --- /dev/null +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -0,0 +1,355 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Collection; +import java.util.Deque; +import java.util.List; +import java.util.jar.JarFile; + +import com.google.common.collect.Lists; +import com.google.common.collect.Queues; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; + +import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.Deobfuscator.ProgressListener; +import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.FieldReferenceTreeNode; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.gui.ProgressDialog.ProgressRunnable; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MappingParseException; +import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.MappingsWriter; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.TranslationDirection; + +public class GuiController { + + private Deobfuscator m_deobfuscator; + private Gui m_gui; + private SourceIndex m_index; + private ClassEntry m_currentObfClass; + private boolean m_isDirty; + private Deque> m_referenceStack; + + public GuiController(Gui gui) { + m_gui = gui; + m_deobfuscator = null; + m_index = null; + m_currentObfClass = null; + m_isDirty = false; + m_referenceStack = Queues.newArrayDeque(); + } + + public boolean isDirty() { + return m_isDirty; + } + + public void openJar(final JarFile jar) throws IOException { + m_gui.onStartOpenJar(); + m_deobfuscator = new Deobfuscator(jar); + m_gui.onFinishOpenJar(m_deobfuscator.getJarName()); + refreshClasses(); + } + + public void closeJar() { + m_deobfuscator = null; + m_gui.onCloseJar(); + } + + public void openMappings(File file) throws IOException, MappingParseException { + FileReader in = new FileReader(file); + m_deobfuscator.setMappings(new MappingsReader().read(in)); + in.close(); + m_isDirty = false; + m_gui.setMappingsFile(file); + refreshClasses(); + refreshCurrentClass(); + } + + public void saveMappings(File file) throws IOException { + FileWriter out = new FileWriter(file); + new MappingsWriter().write(out, m_deobfuscator.getMappings()); + out.close(); + m_isDirty = false; + } + + public void closeMappings() { + m_deobfuscator.setMappings(null); + m_gui.setMappingsFile(null); + refreshClasses(); + refreshCurrentClass(); + } + + public void exportSource(final File dirOut) { + ProgressDialog.runInThread(m_gui.getFrame(), new ProgressRunnable() { + @Override + public void run(ProgressListener progress) throws Exception { + m_deobfuscator.writeSources(dirOut, progress); + } + }); + } + + public void exportJar(final File fileOut) { + ProgressDialog.runInThread(m_gui.getFrame(), new ProgressRunnable() { + @Override + public void run(ProgressListener progress) { + m_deobfuscator.writeJar(fileOut, progress); + } + }); + } + + public Token getToken(int pos) { + if (m_index == null) { + return null; + } + return m_index.getReferenceToken(pos); + } + + public EntryReference getDeobfReference(Token token) { + if (m_index == null) { + return null; + } + return m_index.getDeobfReference(token); + } + + public ReadableToken getReadableToken(Token token) { + if (m_index == null) { + return null; + } + return new ReadableToken( + m_index.getLineNumber(token.start), + m_index.getColumnNumber(token.start), + m_index.getColumnNumber(token.end) + ); + } + + public boolean entryHasDeobfuscatedName(Entry deobfEntry) { + return m_deobfuscator.hasDeobfuscatedName(m_deobfuscator.obfuscateEntry(deobfEntry)); + } + + public boolean entryIsInJar(Entry deobfEntry) { + return m_deobfuscator.isObfuscatedIdentifier(m_deobfuscator.obfuscateEntry(deobfEntry)); + } + + public boolean referenceIsRenameable(EntryReference deobfReference) { + return m_deobfuscator.isRenameable(m_deobfuscator.obfuscateReference(deobfReference)); + } + + public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { + ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry(deobfClassEntry); + ClassInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getClassInheritance( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfClassEntry + ); + return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); + } + + public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { + ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry(deobfClassEntry); + return m_deobfuscator.getJarIndex().getClassImplementations( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfClassEntry + ); + } + + public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { + MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry(deobfMethodEntry); + MethodInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodInheritance( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfMethodEntry + ); + return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); + } + + public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { + MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry(deobfMethodEntry); + MethodImplementationsTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodImplementations( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfMethodEntry + ); + if (rootNode == null) { + return null; + } + return MethodImplementationsTreeNode.findNode(rootNode, obfMethodEntry); + } + + public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { + FieldEntry obfFieldEntry = m_deobfuscator.obfuscateEntry(deobfFieldEntry); + FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfFieldEntry + ); + rootNode.load(m_deobfuscator.getJarIndex(), true); + return rootNode; + } + + public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) { + BehaviorEntry obfBehaviorEntry = m_deobfuscator.obfuscateEntry(deobfBehaviorEntry); + BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfBehaviorEntry + ); + rootNode.load(m_deobfuscator.getJarIndex(), true); + return rootNode; + } + + public void rename(EntryReference deobfReference, String newName) { + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + m_deobfuscator.rename(obfReference.getNameableEntry(), newName); + m_isDirty = true; + refreshClasses(); + refreshCurrentClass(obfReference); + } + + public void removeMapping(EntryReference deobfReference) { + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + m_deobfuscator.removeMapping(obfReference.getNameableEntry()); + m_isDirty = true; + refreshClasses(); + refreshCurrentClass(obfReference); + } + + public void markAsDeobfuscated(EntryReference deobfReference) { + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + m_deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); + m_isDirty = true; + refreshClasses(); + refreshCurrentClass(obfReference); + } + + public void openDeclaration(Entry deobfEntry) { + if (deobfEntry == null) { + throw new IllegalArgumentException("Entry cannot be null!"); + } + openReference(new EntryReference(deobfEntry, deobfEntry.getName())); + } + + public void openReference(EntryReference deobfReference) { + if (deobfReference == null) { + throw new IllegalArgumentException("Reference cannot be null!"); + } + + // get the reference target class + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOuterClassEntry(); + if (!m_deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { + throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); + } + if (m_currentObfClass == null || !m_currentObfClass.equals(obfClassEntry)) { + // deobfuscate the class, then navigate to the reference + m_currentObfClass = obfClassEntry; + deobfuscate(m_currentObfClass, obfReference); + } else { + showReference(obfReference); + } + } + + private void showReference(EntryReference obfReference) { + EntryReference deobfReference = m_deobfuscator.deobfuscateReference(obfReference); + Collection tokens = m_index.getReferenceTokens(deobfReference); + if (tokens.isEmpty()) { + // DEBUG + System.err.println(String.format("WARNING: no tokens found for %s in %s", deobfReference, m_currentObfClass)); + } else { + m_gui.showTokens(tokens); + } + } + + public void savePreviousReference(EntryReference deobfReference) { + m_referenceStack.push(m_deobfuscator.obfuscateReference(deobfReference)); + } + + public void openPreviousReference() { + if (hasPreviousLocation()) { + openReference(m_deobfuscator.deobfuscateReference(m_referenceStack.pop())); + } + } + + public boolean hasPreviousLocation() { + return !m_referenceStack.isEmpty(); + } + + private void refreshClasses() { + List obfClasses = Lists.newArrayList(); + List deobfClasses = Lists.newArrayList(); + m_deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); + m_gui.setObfClasses(obfClasses); + m_gui.setDeobfClasses(deobfClasses); + } + + private void refreshCurrentClass() { + refreshCurrentClass(null); + } + + private void refreshCurrentClass(EntryReference obfReference) { + if (m_currentObfClass != null) { + deobfuscate(m_currentObfClass, obfReference); + } + } + + private void deobfuscate(final ClassEntry classEntry, final EntryReference obfReference) { + + m_gui.setSource("(deobfuscating...)"); + + // run the deobfuscator in a separate thread so we don't block the GUI event queue + new Thread() { + @Override + public void run() { + // decompile,deobfuscate the bytecode + CompilationUnit sourceTree = m_deobfuscator.getSourceTree(classEntry.getClassName()); + if (sourceTree == null) { + // decompilation of this class is not supported + m_gui.setSource("Unable to find class: " + classEntry); + return; + } + String source = m_deobfuscator.getSource(sourceTree); + m_index = m_deobfuscator.getSourceIndex(sourceTree, source); + m_gui.setSource(m_index.getSource()); + if (obfReference != null) { + showReference(obfReference); + } + + // set the highlighted tokens + List obfuscatedTokens = Lists.newArrayList(); + List deobfuscatedTokens = Lists.newArrayList(); + List otherTokens = Lists.newArrayList(); + for (Token token : m_index.referenceTokens()) { + EntryReference reference = m_index.getDeobfReference(token); + if (referenceIsRenameable(reference)) { + if (entryHasDeobfuscatedName(reference.getNameableEntry())) { + deobfuscatedTokens.add(token); + } else { + obfuscatedTokens.add(token); + } + } else { + otherTokens.add(token); + } + } + m_gui.setHighlightedTokens(obfuscatedTokens, deobfuscatedTokens, otherTokens); + } + }.start(); + } +} diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java new file mode 100644 index 00000000..df9e2215 --- /dev/null +++ b/src/cuchaz/enigma/gui/GuiTricks.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Font; +import java.awt.event.MouseEvent; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.ToolTipManager; + +public class GuiTricks { + + public static JLabel unboldLabel(JLabel label) { + Font font = label.getFont(); + label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); + return label; + } + + public static void showToolTipNow(JComponent component) { + // HACKHACK: trick the tooltip manager into showing the tooltip right now + ToolTipManager manager = ToolTipManager.sharedInstance(); + int oldDelay = manager.getInitialDelay(); + manager.setInitialDelay(0); + manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); + manager.setInitialDelay(oldDelay); + } +} diff --git a/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java new file mode 100644 index 00000000..177835f4 --- /dev/null +++ b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; + +public class ObfuscatedHighlightPainter extends BoxHighlightPainter { + + public ObfuscatedHighlightPainter() { + // red ish + super(new Color(255, 220, 220), new Color(160, 80, 80)); + } +} diff --git a/src/cuchaz/enigma/gui/OtherHighlightPainter.java b/src/cuchaz/enigma/gui/OtherHighlightPainter.java new file mode 100644 index 00000000..4e9c8709 --- /dev/null +++ b/src/cuchaz/enigma/gui/OtherHighlightPainter.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; + +public class OtherHighlightPainter extends BoxHighlightPainter { + + public OtherHighlightPainter() { + // grey + super(null, new Color(180, 180, 180)); + } +} diff --git a/src/cuchaz/enigma/gui/ProgressDialog.java b/src/cuchaz/enigma/gui/ProgressDialog.java new file mode 100644 index 00000000..b864fdbf --- /dev/null +++ b/src/cuchaz/enigma/gui/ProgressDialog.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; + +import javax.swing.BorderFactory; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.WindowConstants; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.Deobfuscator.ProgressListener; + +public class ProgressDialog implements ProgressListener, AutoCloseable { + + private JFrame m_frame; + private JLabel m_title; + private JLabel m_text; + private JProgressBar m_progress; + + public ProgressDialog(JFrame parent) { + + // init frame + m_frame = new JFrame(Constants.Name + " - Operation in progress"); + final Container pane = m_frame.getContentPane(); + FlowLayout layout = new FlowLayout(); + layout.setAlignment(FlowLayout.LEFT); + pane.setLayout(layout); + + m_title = new JLabel(); + pane.add(m_title); + + // set up the progress bar + JPanel panel = new JPanel(); + pane.add(panel); + panel.setLayout(new BorderLayout()); + m_text = GuiTricks.unboldLabel(new JLabel()); + m_progress = new JProgressBar(); + m_text.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); + panel.add(m_text, BorderLayout.NORTH); + panel.add(m_progress, BorderLayout.CENTER); + panel.setPreferredSize(new Dimension(360, 50)); + + // show the frame + pane.doLayout(); + m_frame.setSize(400, 120); + m_frame.setResizable(false); + m_frame.setLocationRelativeTo(parent); + m_frame.setVisible(true); + m_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + } + + public void close() { + m_frame.dispose(); + } + + @Override + public void init(int totalWork, String title) { + m_title.setText(title); + m_progress.setMinimum(0); + m_progress.setMaximum(totalWork); + m_progress.setValue(0); + } + + @Override + public void onProgress(int numDone, String message) { + m_text.setText(message); + m_progress.setValue(numDone); + + // update the frame + m_frame.validate(); + m_frame.repaint(); + } + + public static interface ProgressRunnable { + void run(ProgressListener listener) throws Exception; + } + + public static void runInThread(final JFrame parent, final ProgressRunnable runnable) { + new Thread() { + @Override + public void run() { + try (ProgressDialog progress = new ProgressDialog(parent)) { + runnable.run(progress); + } catch (Exception ex) { + throw new Error(ex); + } + } + }.start(); + } +} diff --git a/src/cuchaz/enigma/gui/ReadableToken.java b/src/cuchaz/enigma/gui/ReadableToken.java new file mode 100644 index 00000000..66bcbc2a --- /dev/null +++ b/src/cuchaz/enigma/gui/ReadableToken.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +public class ReadableToken { + + public int line; + public int startColumn; + public int endColumn; + + public ReadableToken(int line, int startColumn, int endColumn) { + this.line = line; + this.startColumn = startColumn; + this.endColumn = endColumn; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("line "); + buf.append(line); + buf.append(" columns "); + buf.append(startColumn); + buf.append("-"); + buf.append(endColumn); + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/gui/RenameListener.java b/src/cuchaz/enigma/gui/RenameListener.java new file mode 100644 index 00000000..abeda0ce --- /dev/null +++ b/src/cuchaz/enigma/gui/RenameListener.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import cuchaz.enigma.mapping.Entry; + +public interface RenameListener { + void rename(Entry obfEntry, String newName); +} diff --git a/src/cuchaz/enigma/gui/SelectionHighlightPainter.java b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java new file mode 100644 index 00000000..5e189d2e --- /dev/null +++ b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; + +public class SelectionHighlightPainter implements Highlighter.HighlightPainter { + + @Override + public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { + // draw a thick border + Graphics2D g2d = (Graphics2D)g; + Rectangle bounds = BoxHighlightPainter.getBounds(text, start, end); + g2d.setColor(Color.black); + g2d.setStroke(new BasicStroke(2.0f)); + g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); + } +} diff --git a/src/cuchaz/enigma/gui/TokenListCellRenderer.java b/src/cuchaz/enigma/gui/TokenListCellRenderer.java new file mode 100644 index 00000000..a49be37b --- /dev/null +++ b/src/cuchaz/enigma/gui/TokenListCellRenderer.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Component; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +import cuchaz.enigma.analysis.Token; + +public class TokenListCellRenderer implements ListCellRenderer { + + private GuiController m_controller; + private DefaultListCellRenderer m_defaultRenderer; + + public TokenListCellRenderer(GuiController controller) { + m_controller = controller; + m_defaultRenderer = new DefaultListCellRenderer(); + } + + @Override + public Component getListCellRendererComponent(JList list, Token token, int index, boolean isSelected, boolean hasFocus) { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent(list, token, index, isSelected, hasFocus); + label.setText(m_controller.getReadableToken(token).toString()); + return label; + } +} diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java new file mode 100644 index 00000000..2c15f4e8 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +import cuchaz.enigma.Util; + +public class ArgumentEntry implements Entry, Serializable { + + private static final long serialVersionUID = 4472172468162696006L; + + private BehaviorEntry m_behaviorEntry; + private int m_index; + private String m_name; + + public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) { + if (behaviorEntry == null) { + throw new IllegalArgumentException("Behavior cannot be null!"); + } + if (index < 0) { + throw new IllegalArgumentException("Index must be non-negative!"); + } + if (name == null) { + throw new IllegalArgumentException("Argument name cannot be null!"); + } + + m_behaviorEntry = behaviorEntry; + m_index = index; + m_name = name; + } + + public ArgumentEntry(ArgumentEntry other) { + m_behaviorEntry = (BehaviorEntry)m_behaviorEntry.cloneToNewClass(getClassEntry()); + m_index = other.m_index; + m_name = other.m_name; + } + + public ArgumentEntry(ArgumentEntry other, String newClassName) { + m_behaviorEntry = (BehaviorEntry)other.m_behaviorEntry.cloneToNewClass(new ClassEntry(newClassName)); + m_index = other.m_index; + m_name = other.m_name; + } + + public BehaviorEntry getBehaviorEntry() { + return m_behaviorEntry; + } + + public int getIndex() { + return m_index; + } + + @Override + public String getName() { + return m_name; + } + + @Override + public ClassEntry getClassEntry() { + return m_behaviorEntry.getClassEntry(); + } + + @Override + public String getClassName() { + return m_behaviorEntry.getClassName(); + } + + @Override + public ArgumentEntry cloneToNewClass(ClassEntry classEntry) { + return new ArgumentEntry(this, classEntry.getName()); + } + + public String getMethodName() { + return m_behaviorEntry.getName(); + } + + public String getMethodSignature() { + return m_behaviorEntry.getSignature(); + } + + @Override + public int hashCode() { + return Util.combineHashesOrdered( + m_behaviorEntry, + Integer.valueOf(m_index).hashCode(), + m_name.hashCode() + ); + } + + @Override + public boolean equals(Object other) { + if (other instanceof ArgumentEntry) { + return equals((ArgumentEntry)other); + } + return false; + } + + public boolean equals(ArgumentEntry other) { + return m_behaviorEntry.equals(other.m_behaviorEntry) + && m_index == other.m_index + && m_name.equals(other.m_name); + } + + @Override + public String toString() { + return m_behaviorEntry.toString() + "(" + m_index + ":" + m_name + ")"; + } +} diff --git a/src/cuchaz/enigma/mapping/ArgumentMapping.java b/src/cuchaz/enigma/mapping/ArgumentMapping.java new file mode 100644 index 00000000..f4d8e774 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +public class ArgumentMapping implements Serializable, Comparable { + + private static final long serialVersionUID = 8610742471440861315L; + + private int m_index; + private String m_name; + + // NOTE: this argument order is important for the MethodReader/MethodWriter + public ArgumentMapping(int index, String name) { + m_index = index; + m_name = NameValidator.validateArgumentName(name); + } + + public int getIndex() { + return m_index; + } + + public String getName() { + return m_name; + } + + public void setName(String val) { + m_name = NameValidator.validateArgumentName(val); + } + + @Override + public int compareTo(ArgumentMapping other) { + return Integer.compare(m_index, other.m_index); + } +} diff --git a/src/cuchaz/enigma/mapping/BehaviorEntry.java b/src/cuchaz/enigma/mapping/BehaviorEntry.java new file mode 100644 index 00000000..f4200b8b --- /dev/null +++ b/src/cuchaz/enigma/mapping/BehaviorEntry.java @@ -0,0 +1,15 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public interface BehaviorEntry extends Entry { + String getSignature(); +} diff --git a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java new file mode 100644 index 00000000..386faeb8 --- /dev/null +++ b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import javassist.CtBehavior; +import javassist.CtConstructor; +import javassist.CtMethod; +import javassist.bytecode.Descriptor; + +public class BehaviorEntryFactory { + + public static BehaviorEntry create(String className, String name, String signature) { + return create(new ClassEntry(className), name, signature); + } + + public static BehaviorEntry create(ClassEntry classEntry, String name, String signature) { + if (name.equals("")) { + return new ConstructorEntry(classEntry, signature); + } else if (name.equals("")) { + return new ConstructorEntry(classEntry); + } else { + return new MethodEntry(classEntry, name, signature); + } + } + + public static BehaviorEntry create(CtBehavior behavior) { + String className = Descriptor.toJvmName(behavior.getDeclaringClass().getName()); + if (behavior instanceof CtMethod) { + return create(className, behavior.getName(), behavior.getSignature()); + } else if (behavior instanceof CtConstructor) { + CtConstructor constructor = (CtConstructor)behavior; + if (constructor.isClassInitializer()) { + return create(className, "", null); + } else { + return create(className, "", constructor.getSignature()); + } + } else { + throw new IllegalArgumentException("Unable to create BehaviorEntry from " + behavior); + } + } + + public static BehaviorEntry createObf(ClassEntry classEntry, MethodMapping methodMapping) { + return create(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); + } + + public static BehaviorEntry createDeobf(ClassEntry classEntry, MethodMapping methodMapping) { + return create(classEntry, methodMapping.getDeobfName(), methodMapping.getObfSignature()); + } +} diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java new file mode 100644 index 00000000..cf410012 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +public class ClassEntry implements Entry, Serializable { + + private static final long serialVersionUID = 4235460580973955811L; + + private String m_name; + + public ClassEntry(String className) { + if (className == null) { + throw new IllegalArgumentException("Class name cannot be null!"); + } + if (className.indexOf('.') >= 0) { + throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); + } + + m_name = className; + + if (isInnerClass() && getInnerClassName().indexOf('/') >= 0) { + throw new IllegalArgumentException("Inner class must not have a package: " + className); + } + } + + public ClassEntry(ClassEntry other) { + m_name = other.m_name; + } + + @Override + public String getName() { + return m_name; + } + + @Override + public String getClassName() { + return m_name; + } + + @Override + public ClassEntry getClassEntry() { + return this; + } + + @Override + public ClassEntry cloneToNewClass(ClassEntry classEntry) { + return classEntry; + } + + @Override + public int hashCode() { + return m_name.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof ClassEntry) { + return equals((ClassEntry)other); + } + return false; + } + + public boolean equals(ClassEntry other) { + return m_name.equals(other.m_name); + } + + @Override + public String toString() { + return m_name; + } + + public boolean isInnerClass() { + return m_name.lastIndexOf('$') >= 0; + } + + public String getOuterClassName() { + if (isInnerClass()) { + return m_name.substring(0, m_name.lastIndexOf('$')); + } + return m_name; + } + + public String getInnerClassName() { + if (!isInnerClass()) { + throw new Error("This is not an inner class!"); + } + return m_name.substring(m_name.lastIndexOf('$') + 1); + } + + public ClassEntry getOuterClassEntry() { + return new ClassEntry(getOuterClassName()); + } + + public boolean isInDefaultPackage() { + return m_name.indexOf('/') < 0; + } + + public String getPackageName() { + int pos = m_name.lastIndexOf('/'); + if (pos > 0) { + return m_name.substring(0, pos); + } + return null; + } + + public String getSimpleName() { + int pos = m_name.lastIndexOf('/'); + if (pos > 0) { + return m_name.substring(pos + 1); + } + return m_name; + } +} diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java new file mode 100644 index 00000000..dbb8717f --- /dev/null +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -0,0 +1,405 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Map; + +import com.google.common.collect.Maps; + +public class ClassMapping implements Serializable, Comparable { + + private static final long serialVersionUID = -5148491146902340107L; + + private String m_obfName; + private String m_deobfName; + private Map m_innerClassesByObf; + private Map m_innerClassesByDeobf; + private Map m_fieldsByObf; + private Map m_fieldsByDeobf; + private Map m_methodsByObf; + private Map m_methodsByDeobf; + + public ClassMapping(String obfName) { + this(obfName, null); + } + + public ClassMapping(String obfName, String deobfName) { + m_obfName = obfName; + m_deobfName = NameValidator.validateClassName(deobfName, false); + m_innerClassesByObf = Maps.newHashMap(); + m_innerClassesByDeobf = Maps.newHashMap(); + m_fieldsByObf = Maps.newHashMap(); + m_fieldsByDeobf = Maps.newHashMap(); + m_methodsByObf = Maps.newHashMap(); + m_methodsByDeobf = Maps.newHashMap(); + } + + public String getObfName() { + return m_obfName; + } + + public String getDeobfName() { + return m_deobfName; + } + + public void setDeobfName(String val) { + m_deobfName = NameValidator.validateClassName(val, false); + } + + //// INNER CLASSES //////// + + public Iterable innerClasses() { + assert (m_innerClassesByObf.size() >= m_innerClassesByDeobf.size()); + return m_innerClassesByObf.values(); + } + + public void addInnerClassMapping(ClassMapping classMapping) { + assert (isSimpleClassName(classMapping.getObfName())); + boolean obfWasAdded = m_innerClassesByObf.put(classMapping.getObfName(), classMapping) == null; + assert (obfWasAdded); + if (classMapping.getDeobfName() != null) { + assert (isSimpleClassName(classMapping.getDeobfName())); + boolean deobfWasAdded = m_innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping) == null; + assert (deobfWasAdded); + } + } + + public void removeInnerClassMapping(ClassMapping classMapping) { + boolean obfWasRemoved = m_innerClassesByObf.remove(classMapping.getObfName()) != null; + assert (obfWasRemoved); + if (classMapping.getDeobfName() != null) { + boolean deobfWasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (deobfWasRemoved); + } + } + + public ClassMapping getOrCreateInnerClass(String obfName) { + assert (isSimpleClassName(obfName)); + ClassMapping classMapping = m_innerClassesByObf.get(obfName); + if (classMapping == null) { + classMapping = new ClassMapping(obfName); + boolean wasAdded = m_innerClassesByObf.put(obfName, classMapping) == null; + assert (wasAdded); + } + return classMapping; + } + + public ClassMapping getInnerClassByObf(String obfName) { + assert (isSimpleClassName(obfName)); + return m_innerClassesByObf.get(obfName); + } + + public ClassMapping getInnerClassByDeobf(String deobfName) { + assert (isSimpleClassName(deobfName)); + return m_innerClassesByDeobf.get(deobfName); + } + + public ClassMapping getInnerClassByDeobfThenObf(String name) { + ClassMapping classMapping = getInnerClassByDeobf(name); + if (classMapping == null) { + classMapping = getInnerClassByObf(name); + } + return classMapping; + } + + public String getObfInnerClassName(String deobfName) { + assert (isSimpleClassName(deobfName)); + ClassMapping classMapping = m_innerClassesByDeobf.get(deobfName); + if (classMapping != null) { + return classMapping.getObfName(); + } + return null; + } + + public String getDeobfInnerClassName(String obfName) { + assert (isSimpleClassName(obfName)); + ClassMapping classMapping = m_innerClassesByObf.get(obfName); + if (classMapping != null) { + return classMapping.getDeobfName(); + } + return null; + } + + public void setInnerClassName(String obfName, String deobfName) { + assert (isSimpleClassName(obfName)); + ClassMapping classMapping = getOrCreateInnerClass(obfName); + if (classMapping.getDeobfName() != null) { + boolean wasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (wasRemoved); + } + classMapping.setDeobfName(deobfName); + if (deobfName != null) { + assert (isSimpleClassName(deobfName)); + boolean wasAdded = m_innerClassesByDeobf.put(deobfName, classMapping) == null; + assert (wasAdded); + } + } + + //// FIELDS //////// + + public Iterable fields() { + assert (m_fieldsByObf.size() == m_fieldsByDeobf.size()); + return m_fieldsByObf.values(); + } + + public boolean containsObfField(String obfName) { + return m_fieldsByObf.containsKey(obfName); + } + + public boolean containsDeobfField(String deobfName) { + return m_fieldsByDeobf.containsKey(deobfName); + } + + public void addFieldMapping(FieldMapping fieldMapping) { + if (m_fieldsByObf.containsKey(fieldMapping.getObfName())) { + throw new Error("Already have mapping for " + m_obfName + "." + fieldMapping.getObfName()); + } + if (m_fieldsByDeobf.containsKey(fieldMapping.getDeobfName())) { + throw new Error("Already have mapping for " + m_deobfName + "." + fieldMapping.getDeobfName()); + } + boolean obfWasAdded = m_fieldsByObf.put(fieldMapping.getObfName(), fieldMapping) == null; + assert (obfWasAdded); + boolean deobfWasAdded = m_fieldsByDeobf.put(fieldMapping.getDeobfName(), fieldMapping) == null; + assert (deobfWasAdded); + assert (m_fieldsByObf.size() == m_fieldsByDeobf.size()); + } + + public void removeFieldMapping(FieldMapping fieldMapping) { + boolean obfWasRemoved = m_fieldsByObf.remove(fieldMapping.getObfName()) != null; + assert (obfWasRemoved); + if (fieldMapping.getDeobfName() != null) { + boolean deobfWasRemoved = m_fieldsByDeobf.remove(fieldMapping.getDeobfName()) != null; + assert (deobfWasRemoved); + } + } + + public FieldMapping getFieldByObf(String obfName) { + return m_fieldsByObf.get(obfName); + } + + public FieldMapping getFieldByDeobf(String deobfName) { + return m_fieldsByDeobf.get(deobfName); + } + + public String getObfFieldName(String deobfName) { + FieldMapping fieldMapping = m_fieldsByDeobf.get(deobfName); + if (fieldMapping != null) { + return fieldMapping.getObfName(); + } + return null; + } + + public String getDeobfFieldName(String obfName) { + FieldMapping fieldMapping = m_fieldsByObf.get(obfName); + if (fieldMapping != null) { + return fieldMapping.getDeobfName(); + } + return null; + } + + public void setFieldName(String obfName, String deobfName) { + FieldMapping fieldMapping = m_fieldsByObf.get(obfName); + if (fieldMapping == null) { + fieldMapping = new FieldMapping(obfName, deobfName); + boolean obfWasAdded = m_fieldsByObf.put(obfName, fieldMapping) == null; + assert (obfWasAdded); + } else { + boolean wasRemoved = m_fieldsByDeobf.remove(fieldMapping.getDeobfName()) != null; + assert (wasRemoved); + } + fieldMapping.setDeobfName(deobfName); + if (deobfName != null) { + boolean wasAdded = m_fieldsByDeobf.put(deobfName, fieldMapping) == null; + assert (wasAdded); + } + } + + //// METHODS //////// + + public Iterable methods() { + assert (m_methodsByObf.size() >= m_methodsByDeobf.size()); + return m_methodsByObf.values(); + } + + public boolean containsObfMethod(String obfName, String obfSignature) { + return m_methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); + } + + public boolean containsDeobfMethod(String deobfName, String deobfSignature) { + return m_methodsByDeobf.containsKey(getMethodKey(deobfName, deobfSignature)); + } + + public void addMethodMapping(MethodMapping methodMapping) { + String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); + if (m_methodsByObf.containsKey(obfKey)) { + throw new Error("Already have mapping for " + m_obfName + "." + obfKey); + } + boolean wasAdded = m_methodsByObf.put(obfKey, methodMapping) == null; + assert (wasAdded); + if (methodMapping.getDeobfName() != null) { + String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); + if (m_methodsByDeobf.containsKey(deobfKey)) { + throw new Error("Already have mapping for " + m_deobfName + "." + deobfKey); + } + boolean deobfWasAdded = m_methodsByDeobf.put(deobfKey, methodMapping) == null; + assert (deobfWasAdded); + } + assert (m_methodsByObf.size() >= m_methodsByDeobf.size()); + } + + public void removeMethodMapping(MethodMapping methodMapping) { + boolean obfWasRemoved = m_methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; + assert (obfWasRemoved); + if (methodMapping.getDeobfName() != null) { + boolean deobfWasRemoved = m_methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; + assert (deobfWasRemoved); + } + } + + public MethodMapping getMethodByObf(String obfName, String signature) { + return m_methodsByObf.get(getMethodKey(obfName, signature)); + } + + public MethodMapping getMethodByDeobf(String deobfName, String signature) { + return m_methodsByDeobf.get(getMethodKey(deobfName, signature)); + } + + private String getMethodKey(String name, String signature) { + if (name == null) { + throw new IllegalArgumentException("name cannot be null!"); + } + if (signature == null) { + throw new IllegalArgumentException("signature cannot be null!"); + } + return name + signature; + } + + public void setMethodName(String obfName, String obfSignature, String deobfName) { + MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfName, obfSignature)); + if (methodMapping == null) { + methodMapping = createMethodMapping(obfName, obfSignature); + } else if (methodMapping.getDeobfName() != null) { + boolean wasRemoved = m_methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; + assert (wasRemoved); + } + methodMapping.setDeobfName(deobfName); + if (deobfName != null) { + boolean wasAdded = m_methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; + assert (wasAdded); + } + } + + //// ARGUMENTS //////// + + public void setArgumentName(String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName) { + MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); + if (methodMapping == null) { + methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); + } + methodMapping.setArgumentName(argumentIndex, argumentName); + } + + public void removeArgumentName(String obfMethodName, String obfMethodSignature, int argumentIndex) { + m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); + } + + private MethodMapping createMethodMapping(String obfName, String obfSignature) { + MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); + boolean wasAdded = m_methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; + assert (wasAdded); + return methodMapping; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(m_obfName); + buf.append(" <-> "); + buf.append(m_deobfName); + buf.append("\n"); + buf.append("Fields:\n"); + for (FieldMapping fieldMapping : fields()) { + buf.append("\t"); + buf.append(fieldMapping.getObfName()); + buf.append(" <-> "); + buf.append(fieldMapping.getDeobfName()); + buf.append("\n"); + } + buf.append("Methods:\n"); + for (MethodMapping methodMapping : m_methodsByObf.values()) { + buf.append(methodMapping.toString()); + buf.append("\n"); + } + buf.append("Inner Classes:\n"); + for (ClassMapping classMapping : m_innerClassesByObf.values()) { + buf.append("\t"); + buf.append(classMapping.getObfName()); + buf.append(" <-> "); + buf.append(classMapping.getDeobfName()); + buf.append("\n"); + } + return buf.toString(); + } + + @Override + public int compareTo(ClassMapping other) { + // sort by a, b, c, ... aa, ab, etc + if (m_obfName.length() != other.m_obfName.length()) { + return m_obfName.length() - other.m_obfName.length(); + } + return m_obfName.compareTo(other.m_obfName); + } + + public boolean renameObfClass(String oldObfClassName, String newObfClassName) { + + // rename inner classes + for (ClassMapping innerClassMapping : new ArrayList(m_innerClassesByObf.values())) { + if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) { + boolean wasRemoved = m_innerClassesByObf.remove(oldObfClassName) != null; + assert (wasRemoved); + boolean wasAdded = m_innerClassesByObf.put(newObfClassName, innerClassMapping) == null; + assert (wasAdded); + } + } + + // rename method signatures + for (MethodMapping methodMapping : new ArrayList(m_methodsByObf.values())) { + String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); + if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { + boolean wasRemoved = m_methodsByObf.remove(oldMethodKey) != null; + assert (wasRemoved); + boolean wasAdded = m_methodsByObf.put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; + assert (wasAdded); + } + } + + if (m_obfName.equals(oldObfClassName)) { + // rename this class + m_obfName = newObfClassName; + return true; + } + return false; + } + + public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { + MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); + if (methodMapping != null) { + return methodMapping.containsArgument(name); + } + return false; + } + + public static boolean isSimpleClassName(String name) { + return name.indexOf('/') < 0 && name.indexOf('$') < 0; + } +} diff --git a/src/cuchaz/enigma/mapping/ConstructorEntry.java b/src/cuchaz/enigma/mapping/ConstructorEntry.java new file mode 100644 index 00000000..ea0535f8 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ConstructorEntry.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +import cuchaz.enigma.Util; + +public class ConstructorEntry implements BehaviorEntry, Serializable { + + private static final long serialVersionUID = -868346075317366758L; + + private ClassEntry m_classEntry; + private String m_signature; + + public ConstructorEntry(ClassEntry classEntry) { + this(classEntry, null); + } + + public ConstructorEntry(ClassEntry classEntry, String signature) { + if (classEntry == null) { + throw new IllegalArgumentException("Class cannot be null!"); + } + + m_classEntry = classEntry; + m_signature = signature; + } + + public ConstructorEntry(ConstructorEntry other) { + m_classEntry = new ClassEntry(other.m_classEntry); + m_signature = other.m_signature; + } + + public ConstructorEntry(ConstructorEntry other, String newClassName) { + m_classEntry = new ClassEntry(newClassName); + m_signature = other.m_signature; + } + + @Override + public ClassEntry getClassEntry() { + return m_classEntry; + } + + @Override + public String getName() { + if (isStatic()) { + return ""; + } + return ""; + } + + public boolean isStatic() { + return m_signature == null; + } + + @Override + public String getSignature() { + return m_signature; + } + + @Override + public String getClassName() { + return m_classEntry.getName(); + } + + @Override + public ConstructorEntry cloneToNewClass(ClassEntry classEntry) { + return new ConstructorEntry(this, classEntry.getName()); + } + + @Override + public int hashCode() { + if (isStatic()) { + return Util.combineHashesOrdered(m_classEntry); + } else { + return Util.combineHashesOrdered(m_classEntry, m_signature); + } + } + + @Override + public boolean equals(Object other) { + if (other instanceof ConstructorEntry) { + return equals((ConstructorEntry)other); + } + return false; + } + + public boolean equals(ConstructorEntry other) { + if (isStatic() != other.isStatic()) { + return false; + } + + if (isStatic()) { + return m_classEntry.equals(other.m_classEntry); + } else { + return m_classEntry.equals(other.m_classEntry) && m_signature.equals(other.m_signature); + } + } + + @Override + public String toString() { + if (isStatic()) { + return m_classEntry.getName() + "." + getName(); + } else { + return m_classEntry.getName() + "." + getName() + m_signature; + } + } +} diff --git a/src/cuchaz/enigma/mapping/Entry.java b/src/cuchaz/enigma/mapping/Entry.java new file mode 100644 index 00000000..39e1507d --- /dev/null +++ b/src/cuchaz/enigma/mapping/Entry.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public interface Entry { + String getName(); + String getClassName(); + ClassEntry getClassEntry(); + Entry cloneToNewClass(ClassEntry classEntry); +} diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java new file mode 100644 index 00000000..60411c40 --- /dev/null +++ b/src/cuchaz/enigma/mapping/EntryPair.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public class EntryPair { + + public T obf; + public T deobf; + + public EntryPair(T obf, T deobf) { + this.obf = obf; + this.deobf = deobf; + } +} diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java new file mode 100644 index 00000000..6cc9eb78 --- /dev/null +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +import cuchaz.enigma.Util; + +public class FieldEntry implements Entry, Serializable { + + private static final long serialVersionUID = 3004663582802885451L; + + private ClassEntry m_classEntry; + private String m_name; + + // NOTE: this argument order is important for the MethodReader/MethodWriter + public FieldEntry(ClassEntry classEntry, String name) { + if (classEntry == null) { + throw new IllegalArgumentException("Class cannot be null!"); + } + if (name == null) { + throw new IllegalArgumentException("Field name cannot be null!"); + } + + m_classEntry = classEntry; + m_name = name; + } + + public FieldEntry(FieldEntry other) { + m_classEntry = new ClassEntry(other.m_classEntry); + m_name = other.m_name; + } + + public FieldEntry(FieldEntry other, String newClassName) { + m_classEntry = new ClassEntry(newClassName); + m_name = other.m_name; + } + + @Override + public ClassEntry getClassEntry() { + return m_classEntry; + } + + @Override + public String getName() { + return m_name; + } + + @Override + public String getClassName() { + return m_classEntry.getName(); + } + + @Override + public FieldEntry cloneToNewClass(ClassEntry classEntry) { + return new FieldEntry(this, classEntry.getName()); + } + + @Override + public int hashCode() { + return Util.combineHashesOrdered(m_classEntry, m_name); + } + + @Override + public boolean equals(Object other) { + if (other instanceof FieldEntry) { + return equals((FieldEntry)other); + } + return false; + } + + public boolean equals(FieldEntry other) { + return m_classEntry.equals(other.m_classEntry) && m_name.equals(other.m_name); + } + + @Override + public String toString() { + return m_classEntry.getName() + "." + m_name; + } +} diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java new file mode 100644 index 00000000..5f5c270d --- /dev/null +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +public class FieldMapping implements Serializable, Comparable { + + private static final long serialVersionUID = 8610742471440861315L; + + private String m_obfName; + private String m_deobfName; + + public FieldMapping(String obfName, String deobfName) { + m_obfName = obfName; + m_deobfName = NameValidator.validateFieldName(deobfName); + } + + public String getObfName() { + return m_obfName; + } + + public String getDeobfName() { + return m_deobfName; + } + + public void setDeobfName(String val) { + m_deobfName = NameValidator.validateFieldName(val); + } + + @Override + public int compareTo(FieldMapping other) { + return m_obfName.compareTo(other.m_obfName); + } +} diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java new file mode 100644 index 00000000..aacaf3b0 --- /dev/null +++ b/src/cuchaz/enigma/mapping/IllegalNameException.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public class IllegalNameException extends RuntimeException { + + private static final long serialVersionUID = -2279910052561114323L; + + private String m_name; + private String m_reason; + + public IllegalNameException(String name) { + this(name, null); + } + + public IllegalNameException(String name, String reason) { + m_name = name; + m_reason = reason; + } + + public String getReason() { + return m_reason; + } + + @Override + public String getMessage() { + StringBuilder buf = new StringBuilder(); + buf.append("Illegal name: "); + buf.append(m_name); + if (m_reason != null) { + buf.append(" because "); + buf.append(m_reason); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/mapping/JavassistUtil.java b/src/cuchaz/enigma/mapping/JavassistUtil.java new file mode 100644 index 00000000..b011e0be --- /dev/null +++ b/src/cuchaz/enigma/mapping/JavassistUtil.java @@ -0,0 +1,55 @@ +package cuchaz.enigma.mapping; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.CtMethod; +import javassist.bytecode.Descriptor; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class JavassistUtil { + + public static ClassEntry getClassEntry(CtClass c) { + return new ClassEntry(Descriptor.toJvmName(c.getName())); + } + + public static ClassEntry getSuperclassEntry(CtClass c) { + return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); + } + + public static MethodEntry getMethodEntry(CtMethod method) { + return new MethodEntry( + getClassEntry(method.getDeclaringClass()), + method.getName(), + method.getMethodInfo().getDescriptor() + ); + } + + public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { + return new ConstructorEntry( + getClassEntry(constructor.getDeclaringClass()), + constructor.getMethodInfo().getDescriptor() + ); + } + + public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { + if (behavior instanceof CtMethod) { + return getMethodEntry((CtMethod)behavior); + } else if (behavior instanceof CtConstructor) { + return getConstructorEntry((CtConstructor)behavior); + } + throw new Error("behavior is neither Method nor Constructor!"); + } + + public static FieldEntry getFieldEntry(CtField field) { + return new FieldEntry( + getClassEntry(field.getDeclaringClass()), + field.getName() + ); + } +} diff --git a/src/cuchaz/enigma/mapping/MappingParseException.java b/src/cuchaz/enigma/mapping/MappingParseException.java new file mode 100644 index 00000000..1974c222 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingParseException.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public class MappingParseException extends Exception { + + private static final long serialVersionUID = -5487280332892507236L; + + private int m_line; + private String m_message; + + public MappingParseException(int line, String message) { + m_line = line; + m_message = message; + } + + @Override + public String getMessage() { + return "Line " + m_line + ": " + m_message; + } +} diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java new file mode 100644 index 00000000..cc560a87 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.zip.GZIPInputStream; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import cuchaz.enigma.Util; +import cuchaz.enigma.analysis.TranslationIndex; +import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; + +public class Mappings implements Serializable { + + private static final long serialVersionUID = 4649790259460259026L; + + protected Map m_classesByObf; + protected Map m_classesByDeobf; + + public Mappings() { + m_classesByObf = Maps.newHashMap(); + m_classesByDeobf = Maps.newHashMap(); + } + + public Mappings(Iterable classes) { + this(); + + for (ClassMapping classMapping : classes) { + m_classesByObf.put(classMapping.getObfName(), classMapping); + if (classMapping.getDeobfName() != null) { + m_classesByDeobf.put(classMapping.getDeobfName(), classMapping); + } + } + } + + public static Mappings newFromResource(String resource) throws IOException { + InputStream in = null; + try { + in = Mappings.class.getResourceAsStream(resource); + return newFromStream(in); + } finally { + Util.closeQuietly(in); + } + } + + public Collection classes() { + assert (m_classesByObf.size() >= m_classesByDeobf.size()); + return m_classesByObf.values(); + } + + public void addClassMapping(ClassMapping classMapping) { + if (m_classesByObf.containsKey(classMapping.getObfName())) { + throw new Error("Already have mapping for " + classMapping.getObfName()); + } + boolean obfWasAdded = m_classesByObf.put(classMapping.getObfName(), classMapping) == null; + assert (obfWasAdded); + if (classMapping.getDeobfName() != null) { + if (m_classesByDeobf.containsKey(classMapping.getDeobfName())) { + throw new Error("Already have mapping for " + classMapping.getDeobfName()); + } + boolean deobfWasAdded = m_classesByDeobf.put(classMapping.getDeobfName(), classMapping) == null; + assert (deobfWasAdded); + } + } + + public void removeClassMapping(ClassMapping classMapping) { + boolean obfWasRemoved = m_classesByObf.remove(classMapping.getObfName()) != null; + assert (obfWasRemoved); + if (classMapping.getDeobfName() != null) { + boolean deobfWasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (deobfWasRemoved); + } + } + + public ClassMapping getClassByObf(ClassEntry entry) { + return getClassByObf(entry.getName()); + } + + public ClassMapping getClassByObf(String obfName) { + return m_classesByObf.get(obfName); + } + + public ClassMapping getClassByDeobf(ClassEntry entry) { + return getClassByDeobf(entry.getName()); + } + + public ClassMapping getClassByDeobf(String deobfName) { + return m_classesByDeobf.get(deobfName); + } + + public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { + switch (direction) { + case Deobfuscating: + + return new Translator(direction, m_classesByObf, index); + + case Obfuscating: + + // fill in the missing deobf class entries with obf entries + Map classes = Maps.newHashMap(); + for (ClassMapping classMapping : classes()) { + if (classMapping.getDeobfName() != null) { + classes.put(classMapping.getDeobfName(), classMapping); + } else { + classes.put(classMapping.getObfName(), classMapping); + } + } + + // translate the translation index + // NOTE: this isn't actually recursive + TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); + + return new Translator(direction, classes, deobfIndex); + + default: + throw new Error("Invalid translation direction!"); + } + } + + public static Mappings newFromStream(InputStream in) throws IOException { + try { + return (Mappings)new ObjectInputStream(new GZIPInputStream(in)).readObject(); + } catch (ClassNotFoundException ex) { + throw new Error(ex); + } + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + for (ClassMapping classMapping : m_classesByObf.values()) { + buf.append(classMapping.toString()); + buf.append("\n"); + } + return buf.toString(); + } + + public void renameObfClass(String oldObfName, String newObfName) { + for (ClassMapping classMapping : new ArrayList(classes())) { + if (classMapping.renameObfClass(oldObfName, newObfName)) { + boolean wasRemoved = m_classesByObf.remove(oldObfName) != null; + assert (wasRemoved); + boolean wasAdded = m_classesByObf.put(newObfName, classMapping) == null; + assert (wasAdded); + } + } + } + + public Set getAllObfClassNames() { + final Set classNames = Sets.newHashSet(); + for (ClassMapping classMapping : classes()) { + // add the class name + classNames.add(classMapping.getObfName()); + + // add classes from method signatures + for (MethodMapping methodMapping : classMapping.methods()) { + SignatureUpdater.update(methodMapping.getObfSignature(), new ClassNameUpdater() { + @Override + public String update(String className) { + classNames.add(className); + return className; + } + }); + } + } + return classNames; + } + + public boolean containsDeobfClass(String deobfName) { + return m_classesByDeobf.containsKey(deobfName); + } + + public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { + ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName()); + if (classMapping != null) { + return classMapping.containsDeobfField(deobfName); + } + return false; + } + + public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, String deobfSignature) { + ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName()); + if (classMapping != null) { + return classMapping.containsDeobfMethod(deobfName, deobfSignature); + } + return false; + } + + public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { + ClassMapping classMapping = m_classesByObf.get(obfBehaviorEntry.getClassName()); + if (classMapping != null) { + return classMapping.containsArgument(obfBehaviorEntry, name); + } + return false; + } +} diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java new file mode 100644 index 00000000..72e829d5 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.Deque; + +import com.google.common.collect.Queues; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; + +public class MappingsReader { + + public Mappings read(Reader in) throws IOException, MappingParseException { + return read(new BufferedReader(in)); + } + + public Mappings read(BufferedReader in) throws IOException, MappingParseException { + Mappings mappings = new Mappings(); + Deque mappingStack = Queues.newArrayDeque(); + + int lineNumber = 0; + String line = null; + while ( (line = in.readLine()) != null) { + lineNumber++; + + // strip comments + int commentPos = line.indexOf('#'); + if (commentPos >= 0) { + line = line.substring(0, commentPos); + } + + // skip blank lines + if (line.trim().length() <= 0) { + continue; + } + + // get the indent of this line + int indent = 0; + for (int i = 0; i < line.length(); i++) { + if (line.charAt(i) != '\t') { + break; + } + indent++; + } + + // handle stack pops + while (indent < mappingStack.size()) { + mappingStack.pop(); + } + + String[] parts = line.trim().split("\\s"); + try { + // read the first token + String token = parts[0]; + + if (token.equalsIgnoreCase("CLASS")) { + ClassMapping classMapping; + if (indent == 0) { + // outer class + classMapping = readClass(parts, false); + mappings.addClassMapping(classMapping); + } else if (indent == 1) { + // inner class + if (! (mappingStack.getFirst() instanceof ClassMapping)) { + throw new MappingParseException(lineNumber, "Unexpected CLASS entry here!"); + } + + classMapping = readClass(parts, true); + ((ClassMapping)mappingStack.getFirst()).addInnerClassMapping(classMapping); + } else { + throw new MappingParseException(lineNumber, "Unexpected CLASS entry nesting!"); + } + mappingStack.push(classMapping); + } else if (token.equalsIgnoreCase("FIELD")) { + if (mappingStack.isEmpty() || ! (mappingStack.getFirst() instanceof ClassMapping)) { + throw new MappingParseException(lineNumber, "Unexpected FIELD entry here!"); + } + ((ClassMapping)mappingStack.getFirst()).addFieldMapping(readField(parts)); + } else if (token.equalsIgnoreCase("METHOD")) { + if (mappingStack.isEmpty() || ! (mappingStack.getFirst() instanceof ClassMapping)) { + throw new MappingParseException(lineNumber, "Unexpected METHOD entry here!"); + } + MethodMapping methodMapping = readMethod(parts); + ((ClassMapping)mappingStack.getFirst()).addMethodMapping(methodMapping); + mappingStack.push(methodMapping); + } else if (token.equalsIgnoreCase("ARG")) { + if (mappingStack.isEmpty() || ! (mappingStack.getFirst() instanceof MethodMapping)) { + throw new MappingParseException(lineNumber, "Unexpected ARG entry here!"); + } + ((MethodMapping)mappingStack.getFirst()).addArgumentMapping(readArgument(parts)); + } + } catch (ArrayIndexOutOfBoundsException | NumberFormatException ex) { + throw new MappingParseException(lineNumber, "Malformed line!"); + } + } + + return mappings; + } + + private ArgumentMapping readArgument(String[] parts) { + return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); + } + + private ClassMapping readClass(String[] parts, boolean makeSimple) { + if (parts.length == 2) { + String obfName = processName(parts[1], makeSimple); + return new ClassMapping(obfName); + } else { + String obfName = processName(parts[1], makeSimple); + String deobfName = processName(parts[2], makeSimple); + return new ClassMapping(obfName, deobfName); + } + } + + private String processName(String name, boolean makeSimple) { + if (makeSimple) { + return new ClassEntry(name).getSimpleName(); + } else { + return moveClassOutOfDefaultPackage(name, Constants.NonePackage); + } + } + + private String moveClassOutOfDefaultPackage(String className, String newPackageName) { + ClassEntry classEntry = new ClassEntry(className); + if (classEntry.isInDefaultPackage()) { + return newPackageName + "/" + classEntry.getName(); + } + return className; + } + + private FieldMapping readField(String[] parts) { + return new FieldMapping(parts[1], parts[2]); + } + + private MethodMapping readMethod(String[] parts) { + if (parts.length == 3) { + String obfName = parts[1]; + String obfSignature = moveSignatureOutOfDefaultPackage(parts[2], Constants.NonePackage); + return new MethodMapping(obfName, obfSignature); + } else { + String obfName = parts[1]; + String deobfName = parts[2]; + String obfSignature = moveSignatureOutOfDefaultPackage(parts[3], Constants.NonePackage); + if (obfName.equals(deobfName)) { + return new MethodMapping(obfName, obfSignature); + } else { + return new MethodMapping(obfName, obfSignature, deobfName); + } + } + } + + private String moveSignatureOutOfDefaultPackage(String signature, final String newPackageName) { + return SignatureUpdater.update(signature, new ClassNameUpdater() { + @Override + public String update(String className) { + ClassEntry classEntry = new ClassEntry(className); + if (classEntry.isInDefaultPackage()) { + return newPackageName + "/" + className; + } + return className; + } + }); + } +} diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java new file mode 100644 index 00000000..3aac65a1 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -0,0 +1,237 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.util.Set; +import java.util.zip.GZIPOutputStream; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.analysis.JarIndex; + +public class MappingsRenamer { + + private JarIndex m_index; + private Mappings m_mappings; + + public MappingsRenamer(JarIndex index, Mappings mappings) { + m_index = index; + m_mappings = mappings; + } + + public void setClassName(ClassEntry obf, String deobfName) { + deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass()); + ClassEntry targetEntry = new ClassEntry(deobfName); + if (m_mappings.containsDeobfClass(deobfName) || m_index.containsObfClass(targetEntry)) { + throw new IllegalNameException(deobfName, "There is already a class with that name"); + } + + ClassMapping classMapping = getOrCreateClassMapping(obf); + + if (obf.isInnerClass()) { + classMapping.setInnerClassName(obf.getInnerClassName(), deobfName); + } else { + if (classMapping.getDeobfName() != null) { + boolean wasRemoved = m_mappings.m_classesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (wasRemoved); + } + classMapping.setDeobfName(deobfName); + boolean wasAdded = m_mappings.m_classesByDeobf.put(deobfName, classMapping) == null; + assert (wasAdded); + } + } + + public void removeClassMapping(ClassEntry obf) { + ClassMapping classMapping = getClassMapping(obf); + if (obf.isInnerClass()) { + classMapping.setInnerClassName(obf.getName(), null); + } else { + boolean wasRemoved = m_mappings.m_classesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (wasRemoved); + classMapping.setDeobfName(null); + } + } + + public void markClassAsDeobfuscated(ClassEntry obf) { + ClassMapping classMapping = getOrCreateClassMapping(obf); + if (obf.isInnerClass()) { + String innerClassName = Constants.NonePackage + "/" + obf.getInnerClassName(); + classMapping.setInnerClassName(innerClassName, innerClassName); + } else { + classMapping.setDeobfName(obf.getName()); + boolean wasAdded = m_mappings.m_classesByDeobf.put(obf.getName(), classMapping) == null; + assert (wasAdded); + } + } + + public void setFieldName(FieldEntry obf, String deobfName) { + deobfName = NameValidator.validateFieldName(deobfName); + FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName); + if (m_mappings.containsDeobfField(obf.getClassEntry(), deobfName) || m_index.containsObfField(targetEntry)) { + throw new IllegalNameException(deobfName, "There is already a field with that name"); + } + + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setFieldName(obf.getName(), deobfName); + } + + public void removeFieldMapping(FieldEntry obf) { + ClassMapping classMapping = getClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setFieldName(obf.getName(), null); + } + + public void markFieldAsDeobfuscated(FieldEntry obf) { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setFieldName(obf.getName(), obf.getName()); + } + + public void setMethodTreeName(MethodEntry obf, String deobfName) { + Set implementations = m_index.getRelatedMethodImplementations(obf); + + deobfName = NameValidator.validateMethodName(deobfName); + for (MethodEntry entry : implementations) { + String deobfSignature = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateSignature(obf.getSignature()); + MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, deobfSignature); + if (m_mappings.containsDeobfMethod(entry.getClassEntry(), deobfName, entry.getSignature()) || m_index.containsObfBehavior(targetEntry)) { + String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(entry.getClassName()); + throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); + } + } + + for (MethodEntry entry : implementations) { + setMethodName(entry, deobfName); + } + } + + public void setMethodName(MethodEntry obf, String deobfName) { + deobfName = NameValidator.validateMethodName(deobfName); + MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); + if (m_mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) || m_index.containsObfBehavior(targetEntry)) { + String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(obf.getClassName()); + throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); + } + + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); + } + + public void removeMethodTreeMapping(MethodEntry obf) { + for (MethodEntry implementation : m_index.getRelatedMethodImplementations(obf)) { + removeMethodMapping(implementation); + } + } + + public void removeMethodMapping(MethodEntry obf) { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getSignature(), null); + } + + public void markMethodTreeAsDeobfuscated(MethodEntry obf) { + for (MethodEntry implementation : m_index.getRelatedMethodImplementations(obf)) { + markMethodAsDeobfuscated(implementation); + } + } + + public void markMethodAsDeobfuscated(MethodEntry obf) { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); + } + + public void setArgumentName(ArgumentEntry obf, String deobfName) { + deobfName = NameValidator.validateArgumentName(deobfName); + // NOTE: don't need to check arguments for name collisions with names determined by Procyon + if (m_mappings.containsArgument(obf.getBehaviorEntry(), deobfName)) { + throw new IllegalNameException(deobfName, "There is already an argument with that name"); + } + + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); + } + + public void removeArgumentMapping(ArgumentEntry obf) { + ClassMapping classMapping = getClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); + } + + public void markArgumentAsDeobfuscated(ArgumentEntry obf) { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); + } + + public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { + classMapping.removeFieldMapping(fieldMapping); + ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); + if (!targetClassMapping.containsObfField(fieldMapping.getObfName())) { + if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName())) { + targetClassMapping.addFieldMapping(fieldMapping); + return true; + } else { + System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName()); + } + } + return false; + } + + public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { + classMapping.removeMethodMapping(methodMapping); + ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); + if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { + if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { + targetClassMapping.addMethodMapping(methodMapping); + return true; + } else { + System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); + } + } + return false; + } + + public void write(OutputStream out) throws IOException { + // TEMP: just use the object output for now. We can find a more efficient storage format later + GZIPOutputStream gzipout = new GZIPOutputStream(out); + ObjectOutputStream oout = new ObjectOutputStream(gzipout); + oout.writeObject(this); + gzipout.finish(); + } + + private ClassMapping getClassMapping(ClassEntry obfClassEntry) { + return m_mappings.m_classesByObf.get(obfClassEntry.getOuterClassName()); + } + + private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { + String obfClassName = obfClassEntry.getOuterClassName(); + ClassMapping classMapping = m_mappings.m_classesByObf.get(obfClassName); + if (classMapping == null) { + classMapping = new ClassMapping(obfClassName); + boolean obfWasAdded = m_mappings.m_classesByObf.put(classMapping.getObfName(), classMapping) == null; + assert (obfWasAdded); + } + return classMapping; + } + + private ClassMapping getClassMappingOrInnerClassMapping(ClassEntry obfClassEntry) { + ClassMapping classMapping = getClassMapping(obfClassEntry); + if (obfClassEntry.isInDefaultPackage()) { + classMapping = classMapping.getInnerClassByObf(obfClassEntry.getInnerClassName()); + } + return classMapping; + } + + private ClassMapping getOrCreateClassMappingOrInnerClassMapping(ClassEntry obfClassEntry) { + ClassMapping classMapping = getOrCreateClassMapping(obfClassEntry); + if (obfClassEntry.isInnerClass()) { + classMapping = classMapping.getOrCreateInnerClass(obfClassEntry.getInnerClassName()); + } + return classMapping; + } +} diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java new file mode 100644 index 00000000..5ac409fc --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class MappingsWriter { + + public void write(Writer out, Mappings mappings) throws IOException { + write(new PrintWriter(out), mappings); + } + + public void write(PrintWriter out, Mappings mappings) throws IOException { + for (ClassMapping classMapping : sorted(mappings.classes())) { + write(out, classMapping, 0); + } + } + + private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { + if (classMapping.getDeobfName() == null) { + out.format("%sCLASS %s\n", getIndent(depth), classMapping.getObfName()); + } else { + out.format("%sCLASS %s %s\n", getIndent(depth), classMapping.getObfName(), classMapping.getDeobfName()); + } + + for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { + write(out, innerClassMapping, depth + 1); + } + + for (FieldMapping fieldMapping : sorted(classMapping.fields())) { + write(out, fieldMapping, depth + 1); + } + + for (MethodMapping methodMapping : sorted(classMapping.methods())) { + write(out, methodMapping, depth + 1); + } + } + + private void write(PrintWriter out, FieldMapping fieldMapping, int depth) throws IOException { + out.format("%sFIELD %s %s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName()); + } + + private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { + if (methodMapping.getDeobfName() == null) { + out.format("%sMETHOD %s %s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature()); + } else { + out.format("%sMETHOD %s %s %s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature()); + } + + for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { + write(out, argumentMapping, depth + 1); + } + } + + private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) throws IOException { + out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName()); + } + + private > List sorted(Iterable classes) { + List out = new ArrayList(); + for (T t : classes) { + out.add(t); + } + Collections.sort(out); + return out; + } + + private String getIndent(int depth) { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < depth; i++) { + buf.append("\t"); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java new file mode 100644 index 00000000..beb490d9 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +import cuchaz.enigma.Util; + +public class MethodEntry implements BehaviorEntry, Serializable { + + private static final long serialVersionUID = 4770915224467247458L; + + private ClassEntry m_classEntry; + private String m_name; + private String m_signature; + + public MethodEntry(ClassEntry classEntry, String name, String signature) { + if (classEntry == null) { + throw new IllegalArgumentException("Class cannot be null!"); + } + if (name == null) { + throw new IllegalArgumentException("Method name cannot be null!"); + } + if (signature == null) { + throw new IllegalArgumentException("Method signature cannot be null!"); + } + if (name.startsWith("<")) { + throw new IllegalArgumentException("Don't use MethodEntry for a constructor!"); + } + + m_classEntry = classEntry; + m_name = name; + m_signature = signature; + } + + public MethodEntry(MethodEntry other) { + m_classEntry = new ClassEntry(other.m_classEntry); + m_name = other.m_name; + m_signature = other.m_signature; + } + + public MethodEntry(MethodEntry other, String newClassName) { + m_classEntry = new ClassEntry(newClassName); + m_name = other.m_name; + m_signature = other.m_signature; + } + + @Override + public ClassEntry getClassEntry() { + return m_classEntry; + } + + @Override + public String getName() { + return m_name; + } + + @Override + public String getSignature() { + return m_signature; + } + + @Override + public String getClassName() { + return m_classEntry.getName(); + } + + @Override + public MethodEntry cloneToNewClass(ClassEntry classEntry) { + return new MethodEntry(this, classEntry.getName()); + } + + @Override + public int hashCode() { + return Util.combineHashesOrdered(m_classEntry, m_name, m_signature); + } + + @Override + public boolean equals(Object other) { + if (other instanceof MethodEntry) { + return equals((MethodEntry)other); + } + return false; + } + + public boolean equals(MethodEntry other) { + return m_classEntry.equals(other.m_classEntry) + && m_name.equals(other.m_name) + && m_signature.equals(other.m_signature); + } + + @Override + public String toString() { + return m_classEntry.getName() + "." + m_name + m_signature; + } +} diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java new file mode 100644 index 00000000..4dab3c6a --- /dev/null +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; +import java.util.Map; +import java.util.TreeMap; + +import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; + +public class MethodMapping implements Serializable, Comparable { + + private static final long serialVersionUID = -4409570216084263978L; + + private String m_obfName; + private String m_deobfName; + private String m_obfSignature; + private Map m_arguments; + + public MethodMapping(String obfName, String obfSignature) { + this(obfName, obfSignature, null); + } + + public MethodMapping(String obfName, String obfSignature, String deobfName) { + if (obfName == null) { + throw new IllegalArgumentException("obf name cannot be null!"); + } + if (obfSignature == null) { + throw new IllegalArgumentException("obf signature cannot be null!"); + } + m_obfName = obfName; + m_deobfName = NameValidator.validateMethodName(deobfName); + m_obfSignature = obfSignature; + m_arguments = new TreeMap(); + } + + public String getObfName() { + return m_obfName; + } + + public String getDeobfName() { + return m_deobfName; + } + + public void setDeobfName(String val) { + m_deobfName = NameValidator.validateMethodName(val); + } + + public String getObfSignature() { + return m_obfSignature; + } + + public Iterable arguments() { + return m_arguments.values(); + } + + public boolean isConstructor() { + return m_obfName.startsWith("<"); + } + + public void addArgumentMapping(ArgumentMapping argumentMapping) { + boolean wasAdded = m_arguments.put(argumentMapping.getIndex(), argumentMapping) == null; + assert (wasAdded); + } + + public String getObfArgumentName(int index) { + ArgumentMapping argumentMapping = m_arguments.get(index); + if (argumentMapping != null) { + return argumentMapping.getName(); + } + + return null; + } + + public String getDeobfArgumentName(int index) { + ArgumentMapping argumentMapping = m_arguments.get(index); + if (argumentMapping != null) { + return argumentMapping.getName(); + } + + return null; + } + + public void setArgumentName(int index, String name) { + ArgumentMapping argumentMapping = m_arguments.get(index); + if (argumentMapping == null) { + argumentMapping = new ArgumentMapping(index, name); + boolean wasAdded = m_arguments.put(index, argumentMapping) == null; + assert (wasAdded); + } else { + argumentMapping.setName(name); + } + } + + public void removeArgumentName(int index) { + boolean wasRemoved = m_arguments.remove(index) != null; + assert (wasRemoved); + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("\t"); + buf.append(m_obfName); + buf.append(" <-> "); + buf.append(m_deobfName); + buf.append("\n"); + buf.append("\t"); + buf.append(m_obfSignature); + buf.append("\n"); + buf.append("\tArguments:\n"); + for (ArgumentMapping argumentMapping : m_arguments.values()) { + buf.append("\t\t"); + buf.append(argumentMapping.getIndex()); + buf.append(" -> "); + buf.append(argumentMapping.getName()); + buf.append("\n"); + } + return buf.toString(); + } + + @Override + public int compareTo(MethodMapping other) { + return (m_obfName + m_obfSignature).compareTo(other.m_obfName + other.m_obfSignature); + } + + public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { + // rename obf classes in the signature + String newSignature = SignatureUpdater.update(m_obfSignature, new ClassNameUpdater() { + @Override + public String update(String className) { + if (className.equals(oldObfClassName)) { + return newObfClassName; + } + return className; + } + }); + + if (newSignature != m_obfSignature) { + m_obfSignature = newSignature; + return true; + } + return false; + } + + public boolean containsArgument(String name) { + for (ArgumentMapping argumentMapping : m_arguments.values()) { + if (argumentMapping.getName().equals(name)) { + return true; + } + } + return false; + } +} diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java new file mode 100644 index 00000000..35a17f90 --- /dev/null +++ b/src/cuchaz/enigma/mapping/NameValidator.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +import javassist.bytecode.Descriptor; + +public class NameValidator { + + private static final Pattern IdentifierPattern; + private static final Pattern ClassPattern; + private static final List ReservedWords = Arrays.asList( + "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", + "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", + "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", + "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", + "long", "strictfp", "volatile", "const", "float", "native", "super", "while" + ); + + static { + + // java allows all kinds of weird characters... + StringBuilder startChars = new StringBuilder(); + StringBuilder partChars = new StringBuilder(); + for (int i = Character.MIN_CODE_POINT; i <= Character.MAX_CODE_POINT; i++) { + if (Character.isJavaIdentifierStart(i)) { + startChars.appendCodePoint(i); + } + if (Character.isJavaIdentifierPart(i)) { + partChars.appendCodePoint(i); + } + } + + String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; + IdentifierPattern = Pattern.compile(identifierRegex); + ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex)); + } + + public static String validateClassName(String name, boolean packageRequired) { + if (name == null) { + return null; + } + if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { + throw new IllegalNameException(name, "This doesn't look like a legal class name"); + } + if (packageRequired && new ClassEntry(name).getPackageName() == null) { + throw new IllegalNameException(name, "Class must be in a package"); + } + return Descriptor.toJvmName(name); + } + + public static String validateFieldName(String name) { + if (name == null) { + return null; + } + if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) { + throw new IllegalNameException(name, "This doesn't look like a legal identifier"); + } + return name; + } + + public static String validateMethodName(String name) { + return validateFieldName(name); + } + + public static String validateArgumentName(String name) { + return validateFieldName(name); + } +} diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java new file mode 100644 index 00000000..3477cd56 --- /dev/null +++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +import com.google.common.collect.Lists; + +public class SignatureUpdater { + + public interface ClassNameUpdater { + String update(String className); + } + + public static String update(String signature, ClassNameUpdater updater) { + try { + StringBuilder buf = new StringBuilder(); + + // read the signature character-by-character + StringReader reader = new StringReader(signature); + int i = -1; + while ( (i = reader.read()) != -1) { + char c = (char)i; + + // does this character start a class name? + if (c == 'L') { + // update the class name and add it to the buffer + buf.append('L'); + String className = readClass(reader); + if (className == null) { + throw new IllegalArgumentException("Malformed signature: " + signature); + } + buf.append(updater.update(className)); + buf.append(';'); + } else { + // copy the character into the buffer + buf.append(c); + } + } + + return buf.toString(); + } catch (IOException ex) { + // I'm pretty sure a StringReader will never throw one of these + throw new Error(ex); + } + } + + private static String readClass(StringReader reader) throws IOException { + // read all the characters in the buffer until we hit a ';' + // remember to treat generics correctly + StringBuilder buf = new StringBuilder(); + int depth = 0; + int i = -1; + while ( (i = reader.read()) != -1) { + char c = (char)i; + + if (c == '<') { + depth++; + } else if (c == '>') { + depth--; + } else if (depth == 0) { + if (c == ';') { + return buf.toString(); + } else { + buf.append(c); + } + } + } + + return null; + } + + public static List getClasses(String signature) { + final List classNames = Lists.newArrayList(); + update(signature, new ClassNameUpdater() { + @Override + public String update(String className) { + classNames.add(className); + return className; + } + }); + return classNames; + } +} diff --git a/src/cuchaz/enigma/mapping/TranslationDirection.java b/src/cuchaz/enigma/mapping/TranslationDirection.java new file mode 100644 index 00000000..d1b14cd5 --- /dev/null +++ b/src/cuchaz/enigma/mapping/TranslationDirection.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public enum TranslationDirection { + + Deobfuscating { + @Override + public T choose(T deobfChoice, T obfChoice) { + return deobfChoice; + } + }, + Obfuscating { + @Override + public T choose(T deobfChoice, T obfChoice) { + return obfChoice; + } + }; + + public abstract T choose(T deobfChoice, T obfChoice); +} diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java new file mode 100644 index 00000000..a5a3e2f0 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -0,0 +1,235 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.util.Map; + +import com.google.common.collect.Maps; + +import cuchaz.enigma.analysis.TranslationIndex; +import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; + +public class Translator { + + private TranslationDirection m_direction; + private Map m_classes; + private TranslationIndex m_index; + + public Translator() { + m_direction = null; + m_classes = Maps.newHashMap(); + } + + public Translator(TranslationDirection direction, Map classes, TranslationIndex index) { + m_direction = direction; + m_classes = classes; + m_index = index; + } + + @SuppressWarnings("unchecked") + public T translateEntry(T entry) { + if (entry instanceof ClassEntry) { + return (T)translateEntry((ClassEntry)entry); + } else if (entry instanceof FieldEntry) { + return (T)translateEntry((FieldEntry)entry); + } else if (entry instanceof MethodEntry) { + return (T)translateEntry((MethodEntry)entry); + } else if (entry instanceof ConstructorEntry) { + return (T)translateEntry((ConstructorEntry)entry); + } else if (entry instanceof ArgumentEntry) { + return (T)translateEntry((ArgumentEntry)entry); + } else { + throw new Error("Unknown entry type: " + entry.getClass().getName()); + } + } + + public String translateClass(String className) { + return translate(new ClassEntry(className)); + } + + public String translate(ClassEntry in) { + ClassMapping classMapping = m_classes.get(in.getOuterClassName()); + if (classMapping != null) { + if (in.isInnerClass()) { + // translate the inner class + String translatedInnerClassName = m_direction.choose( + classMapping.getDeobfInnerClassName(in.getInnerClassName()), + classMapping.getObfInnerClassName(in.getInnerClassName()) + ); + if (translatedInnerClassName != null) { + // try to translate the outer name + String translatedOuterClassName = m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName()); + if (translatedOuterClassName != null) { + return translatedOuterClassName + "$" + translatedInnerClassName; + } else { + return in.getOuterClassName() + "$" + translatedInnerClassName; + } + } + } else { + // just return outer + return m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName()); + } + } + return null; + } + + public ClassEntry translateEntry(ClassEntry in) { + + // can we translate the inner class? + String name = translate(in); + if (name != null) { + return new ClassEntry(name); + } + + if (in.isInnerClass()) { + + // guess not. just translate the outer class name then + String outerClassName = translate(in.getOuterClassEntry()); + if (outerClassName != null) { + return new ClassEntry(outerClassName + "$" + in.getInnerClassName()); + } + } + + return in; + } + + public String translate(FieldEntry in) { + + // resolve the class entry + ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in); + if (resolvedClassEntry != null) { + + // look for the class + ClassMapping classMapping = findClassMapping(resolvedClassEntry); + if (classMapping != null) { + + // look for the field + String translatedName = m_direction.choose( + classMapping.getDeobfFieldName(in.getName()), + classMapping.getObfFieldName(in.getName()) + ); + if (translatedName != null) { + return translatedName; + } + } + } + return null; + } + + public FieldEntry translateEntry(FieldEntry in) { + String name = translate(in); + if (name == null) { + name = in.getName(); + } + return new FieldEntry(translateEntry(in.getClassEntry()), name); + } + + public String translate(MethodEntry in) { + + // resolve the class entry + ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in); + if (resolvedClassEntry != null) { + + // look for class + ClassMapping classMapping = findClassMapping(resolvedClassEntry); + if (classMapping != null) { + + // look for the method + MethodMapping methodMapping = m_direction.choose( + classMapping.getMethodByObf(in.getName(), in.getSignature()), + classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature())) + ); + if (methodMapping != null) { + return m_direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); + } + } + } + return null; + } + + public MethodEntry translateEntry(MethodEntry in) { + String name = translate(in); + if (name == null) { + name = in.getName(); + } + return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature())); + } + + public ConstructorEntry translateEntry(ConstructorEntry in) { + if (in.isStatic()) { + return new ConstructorEntry(translateEntry(in.getClassEntry())); + } else { + return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature())); + } + } + + public BehaviorEntry translateEntry(BehaviorEntry in) { + if (in instanceof MethodEntry) { + return translateEntry((MethodEntry)in); + } else if (in instanceof ConstructorEntry) { + return translateEntry((ConstructorEntry)in); + } + throw new Error("Wrong entry type!"); + } + + public String translate(ArgumentEntry in) { + + // look for the class + ClassMapping classMapping = findClassMapping(in.getClassEntry()); + if (classMapping != null) { + + // look for the method + MethodMapping methodMapping = m_direction.choose( + classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()), + classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature())) + ); + if (methodMapping != null) { + return m_direction.choose( + methodMapping.getDeobfArgumentName(in.getIndex()), + methodMapping.getObfArgumentName(in.getIndex()) + ); + } + } + return null; + } + + public ArgumentEntry translateEntry(ArgumentEntry in) { + String name = translate(in); + if (name == null) { + name = in.getName(); + } + return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name); + } + + public String translateSignature(String signature) { + return SignatureUpdater.update(signature, new ClassNameUpdater() { + @Override + public String update(String className) { + String translatedName = translateClass(className); + if (translatedName != null) { + return translatedName; + } + return className; + } + }); + } + + private ClassMapping findClassMapping(ClassEntry classEntry) { + ClassMapping classMapping = m_classes.get(classEntry.getOuterClassName()); + if (classMapping != null && classEntry.isInnerClass()) { + classMapping = m_direction.choose( + classMapping.getInnerClassByObf(classEntry.getInnerClassName()), + classMapping.getInnerClassByDeobfThenObf(classEntry.getInnerClassName()) + ); + } + return classMapping; + } +} diff --git a/test/cuchaz/enigma/EntryFactory.java b/test/cuchaz/enigma/EntryFactory.java new file mode 100644 index 00000000..d9317ef3 --- /dev/null +++ b/test/cuchaz/enigma/EntryFactory.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class EntryFactory { + + public static ClassEntry newClass(String name) { + return new ClassEntry(name); + } + + public static FieldEntry newField(String className, String fieldName) { + return new FieldEntry(newClass(className), fieldName); + } + + public static MethodEntry newMethod(String className, String methodName, String methodSignature) { + return new MethodEntry(newClass(className), methodName, methodSignature); + } + + public static ConstructorEntry newConstructor(String className, String signature) { + return new ConstructorEntry(newClass(className), signature); + } + + public static EntryReference newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) { + return new EntryReference(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature)); + } + + public static EntryReference newFieldReferenceByConstructor(FieldEntry fieldEntry, String callerClassName, String callerSignature) { + return new EntryReference(fieldEntry, "", newConstructor(callerClassName, callerSignature)); + } + + public static EntryReference newBehaviorReferenceByMethod(BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature) { + return new EntryReference(behaviorEntry, "", newMethod(callerClassName, callerName, callerSignature)); + } + + public static EntryReference newBehaviorReferenceByConstructor(BehaviorEntry behaviorEntry, String callerClassName, String callerSignature) { + return new EntryReference(behaviorEntry, "", newConstructor(callerClassName, callerSignature)); + } +} diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java new file mode 100644 index 00000000..129d7b25 --- /dev/null +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.List; +import java.util.jar.JarFile; + +import org.junit.Test; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; + +public class TestDeobfuscator { + + private Deobfuscator getDeobfuscator() throws IOException { + return new Deobfuscator(new JarFile("build/testLoneClass.obf.jar")); + } + + @Test + public void loadJar() throws Exception { + getDeobfuscator(); + } + + @Test + public void getClasses() throws Exception { + Deobfuscator deobfuscator = getDeobfuscator(); + List obfClasses = Lists.newArrayList(); + List deobfClasses = Lists.newArrayList(); + deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); + assertEquals(1, obfClasses.size()); + assertEquals("none/a", obfClasses.get(0).getName()); + assertEquals(1, deobfClasses.size()); + assertEquals("cuchaz/enigma/inputs/Keep", deobfClasses.get(0).getName()); + } + + @Test + public void decompileClass() throws Exception { + Deobfuscator deobfuscator = getDeobfuscator(); + deobfuscator.getSource(deobfuscator.getSourceTree("none/a")); + } +} diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java new file mode 100644 index 00000000..63c9b719 --- /dev/null +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.JarIndex; + +public class TestInnerClasses { + + private JarIndex m_index; + private Deobfuscator m_deobfuscator; + + private static final String AnonymousOuter = "none/a"; + private static final String AnonymousInner = "b"; + private static final String SimpleOuter = "none/g"; + private static final String SimpleInner = "h"; + private static final String ConstructorArgsOuter = "none/e"; + private static final String ConstructorArgsInner = "f"; + private static final String AnonymousWithScopeArgsOuter = "none/c"; + private static final String AnonymousWithScopeArgsInner = "d"; + private static final String AnonymousWithOuterAccessOuter = "none/i"; + private static final String AnonymousWithOuterAccessInner = "j"; + + public TestInnerClasses() throws Exception { + m_index = new JarIndex(); + JarFile jar = new JarFile("build/testInnerClasses.obf.jar"); + m_index.indexJar(jar, true); + m_deobfuscator = new Deobfuscator(jar); + } + + @Test + public void simple() { + assertThat(m_index.getOuterClass(SimpleInner), is(SimpleOuter)); + assertThat(m_index.getInnerClasses(SimpleOuter), containsInAnyOrder(SimpleInner)); + assertThat(m_index.isAnonymousClass(SimpleInner), is(false)); + decompile(SimpleOuter); + } + + @Test + public void anonymous() { + assertThat(m_index.getOuterClass(AnonymousInner), is(AnonymousOuter)); + assertThat(m_index.getInnerClasses(AnonymousOuter), containsInAnyOrder(AnonymousInner)); + assertThat(m_index.isAnonymousClass(AnonymousInner), is(true)); + decompile(AnonymousOuter); + } + + @Test + public void constructorArgs() { + assertThat(m_index.getOuterClass(ConstructorArgsInner), is(ConstructorArgsOuter)); + assertThat(m_index.getInnerClasses(ConstructorArgsOuter), containsInAnyOrder(ConstructorArgsInner)); + assertThat(m_index.isAnonymousClass(ConstructorArgsInner), is(false)); + decompile(ConstructorArgsOuter); + } + + @Test + public void anonymousWithScopeArgs() { + assertThat(m_index.getOuterClass(AnonymousWithScopeArgsInner), is(AnonymousWithScopeArgsOuter)); + assertThat(m_index.getInnerClasses(AnonymousWithScopeArgsOuter), containsInAnyOrder(AnonymousWithScopeArgsInner)); + assertThat(m_index.isAnonymousClass(AnonymousWithScopeArgsInner), is(true)); + decompile(AnonymousWithScopeArgsOuter); + } + + @Test + public void anonymousWithOuterAccess() { + assertThat(m_index.getOuterClass(AnonymousWithOuterAccessInner), is(AnonymousWithOuterAccessOuter)); + assertThat(m_index.getInnerClasses(AnonymousWithOuterAccessOuter), containsInAnyOrder(AnonymousWithOuterAccessInner)); + assertThat(m_index.isAnonymousClass(AnonymousWithOuterAccessInner), is(true)); + decompile(AnonymousWithOuterAccessOuter); + } + + private void decompile(String name) { + m_deobfuscator.getSourceTree(name); + } +} diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java new file mode 100644 index 00000000..8e3ad6d2 --- /dev/null +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.io.File; +import java.util.Collection; +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; + +public class TestJarIndexConstructorReferences { + + private JarIndex m_index; + + private ClassEntry m_baseClass = new ClassEntry("none/a"); + private ClassEntry m_subClass = new ClassEntry("none/d"); + private ClassEntry m_subsubClass = new ClassEntry("none/e"); + private ClassEntry m_defaultClass = new ClassEntry("none/c"); + private ClassEntry m_callerClass = new ClassEntry("none/b"); + + public TestJarIndexConstructorReferences() throws Exception { + File jarFile = new File("build/testConstructors.obf.jar"); + m_index = new JarIndex(); + m_index.indexJar(new JarFile(jarFile), false); + } + + @Test + public void obfEntries() { + assertThat(m_index.getObfClassEntries(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), m_baseClass, m_subClass, m_subsubClass, m_defaultClass, m_callerClass)); + } + + @Test + @SuppressWarnings("unchecked") + public void baseDefault() { + BehaviorEntry source = new ConstructorEntry(m_baseClass, "()V"); + Collection> references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "a", "()V"), + newBehaviorReferenceByConstructor(source, m_subClass.getName(), "()V"), + newBehaviorReferenceByConstructor(source, m_subClass.getName(), "(III)V") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void baseInt() { + BehaviorEntry source = new ConstructorEntry(m_baseClass, "(I)V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "b", "()V") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void subDefault() { + BehaviorEntry source = new ConstructorEntry(m_subClass, "()V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "c", "()V"), + newBehaviorReferenceByConstructor(source, m_subClass.getName(), "(I)V") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void subInt() { + BehaviorEntry source = new ConstructorEntry(m_subClass, "(I)V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "d", "()V"), + newBehaviorReferenceByConstructor(source, m_subClass.getName(), "(II)V"), + newBehaviorReferenceByConstructor(source, m_subsubClass.getName(), "(I)V") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void subIntInt() { + BehaviorEntry source = new ConstructorEntry(m_subClass, "(II)V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "e", "()V") + )); + } + + @Test + public void subIntIntInt() { + BehaviorEntry source = new ConstructorEntry(m_subClass, "(III)V"); + assertThat(m_index.getBehaviorReferences(source), is(empty())); + } + + @Test + @SuppressWarnings("unchecked") + public void subsubInt() { + BehaviorEntry source = new ConstructorEntry(m_subsubClass, "(I)V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "f", "()V") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void defaultConstructable() { + BehaviorEntry source = new ConstructorEntry(m_defaultClass, "()V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "g", "()V") + )); + } +} diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java new file mode 100644 index 00000000..4d663972 --- /dev/null +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -0,0 +1,228 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.util.Collection; +import java.util.Set; +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.Access; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.analysis.TranslationIndex; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class TestJarIndexInheritanceTree { + + private JarIndex m_index; + + private ClassEntry m_baseClass = new ClassEntry("none/a"); + private ClassEntry m_subClassA = new ClassEntry("none/b"); + private ClassEntry m_subClassAA = new ClassEntry("none/d"); + private ClassEntry m_subClassB = new ClassEntry("none/c"); + private FieldEntry m_nameField = new FieldEntry(m_baseClass, "a"); + private FieldEntry m_numThingsField = new FieldEntry(m_subClassB, "a"); + + public TestJarIndexInheritanceTree() throws Exception { + m_index = new JarIndex(); + m_index.indexJar(new JarFile("build/testInheritanceTree.obf.jar"), false); + } + + @Test + public void obfEntries() { + assertThat(m_index.getObfClassEntries(), containsInAnyOrder( + newClass("cuchaz/enigma/inputs/Keep"), + m_baseClass, + m_subClassA, + m_subClassAA, + m_subClassB + )); + } + + @Test + public void translationIndex() { + + TranslationIndex index = m_index.getTranslationIndex(); + + // base class + assertThat(index.getSuperclass(m_baseClass), is(nullValue())); + assertThat(index.getAncestry(m_baseClass), is(empty())); + assertThat(index.getSubclass(m_baseClass), containsInAnyOrder( + m_subClassA, + m_subClassB + )); + + // subclass a + assertThat(index.getSuperclass(m_subClassA), is(m_baseClass)); + assertThat(index.getAncestry(m_subClassA), contains(m_baseClass)); + assertThat(index.getSubclass(m_subClassA), contains(m_subClassAA)); + + // subclass aa + assertThat(index.getSuperclass(m_subClassAA), is(m_subClassA)); + assertThat(index.getAncestry(m_subClassAA), contains(m_subClassA, m_baseClass)); + assertThat(index.getSubclass(m_subClassAA), is(empty())); + + // subclass b + assertThat(index.getSuperclass(m_subClassB), is(m_baseClass)); + assertThat(index.getAncestry(m_subClassB), contains(m_baseClass)); + assertThat(index.getSubclass(m_subClassB), is(empty())); + } + + @Test + public void access() { + assertThat(m_index.getAccess(m_nameField), is(Access.Private)); + assertThat(m_index.getAccess(m_numThingsField), is(Access.Private)); + } + + @Test + public void relatedMethodImplementations() { + + Set entries; + + // getName() + entries = m_index.getRelatedMethodImplementations(new MethodEntry(m_baseClass, "a", "()Ljava/lang/String;")); + assertThat(entries, containsInAnyOrder( + new MethodEntry(m_baseClass, "a", "()Ljava/lang/String;"), + new MethodEntry(m_subClassAA, "a", "()Ljava/lang/String;") + )); + entries = m_index.getRelatedMethodImplementations(new MethodEntry(m_subClassAA, "a", "()Ljava/lang/String;")); + assertThat(entries, containsInAnyOrder( + new MethodEntry(m_baseClass, "a", "()Ljava/lang/String;"), + new MethodEntry(m_subClassAA, "a", "()Ljava/lang/String;") + )); + + // doBaseThings() + entries = m_index.getRelatedMethodImplementations(new MethodEntry(m_baseClass, "a", "()V")); + assertThat(entries, containsInAnyOrder( + new MethodEntry(m_baseClass, "a", "()V"), + new MethodEntry(m_subClassAA, "a", "()V"), + new MethodEntry(m_subClassB, "a", "()V") + )); + entries = m_index.getRelatedMethodImplementations(new MethodEntry(m_subClassAA, "a", "()V")); + assertThat(entries, containsInAnyOrder( + new MethodEntry(m_baseClass, "a", "()V"), + new MethodEntry(m_subClassAA, "a", "()V"), + new MethodEntry(m_subClassB, "a", "()V") + )); + entries = m_index.getRelatedMethodImplementations(new MethodEntry(m_subClassB, "a", "()V")); + assertThat(entries, containsInAnyOrder( + new MethodEntry(m_baseClass, "a", "()V"), + new MethodEntry(m_subClassAA, "a", "()V"), + new MethodEntry(m_subClassB, "a", "()V") + )); + + // doBThings + entries = m_index.getRelatedMethodImplementations(new MethodEntry(m_subClassB, "b", "()V")); + assertThat(entries, containsInAnyOrder(new MethodEntry(m_subClassB, "b", "()V"))); + } + + @Test + @SuppressWarnings("unchecked") + public void fieldReferences() { + Collection> references; + + // name + references = m_index.getFieldReferences(m_nameField); + assertThat(references, containsInAnyOrder( + newFieldReferenceByConstructor(m_nameField, m_baseClass.getName(), "(Ljava/lang/String;)V"), + newFieldReferenceByMethod(m_nameField, m_baseClass.getName(), "a", "()Ljava/lang/String;") + )); + + // numThings + references = m_index.getFieldReferences(m_numThingsField); + assertThat(references, containsInAnyOrder( + newFieldReferenceByConstructor(m_numThingsField, m_subClassB.getName(), "()V"), + newFieldReferenceByMethod(m_numThingsField, m_subClassB.getName(), "b", "()V") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void behaviorReferences() { + + BehaviorEntry source; + Collection> references; + + // baseClass constructor + source = new ConstructorEntry(m_baseClass, "(Ljava/lang/String;)V"); + references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByConstructor(source, m_subClassA.getName(), "(Ljava/lang/String;)V"), + newBehaviorReferenceByConstructor(source, m_subClassB.getName(), "()V") + )); + + // subClassA constructor + source = new ConstructorEntry(m_subClassA, "(Ljava/lang/String;)V"); + references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByConstructor(source, m_subClassAA.getName(), "()V") + )); + + // baseClass.getName() + source = new MethodEntry(m_baseClass, "a", "()Ljava/lang/String;"); + references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_subClassAA.getName(), "a", "()Ljava/lang/String;"), + newBehaviorReferenceByMethod(source, m_subClassB.getName(), "a", "()V") + )); + + // subclassAA.getName() + source = new MethodEntry(m_subClassAA, "a", "()Ljava/lang/String;"); + references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_subClassAA.getName(), "a", "()V") + )); + } + + @Test + public void containsEntries() { + + // classes + assertThat(m_index.containsObfClass(m_baseClass), is(true)); + assertThat(m_index.containsObfClass(m_subClassA), is(true)); + assertThat(m_index.containsObfClass(m_subClassAA), is(true)); + assertThat(m_index.containsObfClass(m_subClassB), is(true)); + + // fields + assertThat(m_index.containsObfField(m_nameField), is(true)); + assertThat(m_index.containsObfField(m_numThingsField), is(true)); + + // methods + // getName() + assertThat(m_index.containsObfBehavior(new MethodEntry(m_baseClass, "a", "()Ljava/lang/String;")), is(true)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassA, "a", "()Ljava/lang/String;")), is(false)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassAA, "a", "()Ljava/lang/String;")), is(true)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassB, "a", "()Ljava/lang/String;")), is(false)); + + // doBaseThings() + assertThat(m_index.containsObfBehavior(new MethodEntry(m_baseClass, "a", "()V")), is(true)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassA, "a", "()V")), is(false)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassAA, "a", "()V")), is(true)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassB, "a", "()V")), is(true)); + + // doBThings() + assertThat(m_index.containsObfBehavior(new MethodEntry(m_baseClass, "b", "()V")), is(false)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassA, "b", "()V")), is(false)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassAA, "b", "()V")), is(false)); + assertThat(m_index.containsObfBehavior(new MethodEntry(m_subClassB, "b", "()V")), is(true)); + + } +} diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java new file mode 100644 index 00000000..a061b72d --- /dev/null +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.util.Collection; +import java.util.Set; +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.Access; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class TestJarIndexLoneClass { + + private JarIndex m_index; + + public TestJarIndexLoneClass() throws Exception { + m_index = new JarIndex(); + m_index.indexJar(new JarFile("build/testLoneClass.obf.jar"), false); + } + + @Test + public void obfEntries() { + assertThat(m_index.getObfClassEntries(), containsInAnyOrder( + newClass("cuchaz/enigma/inputs/Keep"), + newClass("none/a") + )); + } + + @Test + public void translationIndex() { + assertThat(m_index.getTranslationIndex().getSuperclass(new ClassEntry("none/a")), is(nullValue())); + assertThat(m_index.getTranslationIndex().getSuperclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(nullValue())); + assertThat(m_index.getTranslationIndex().getAncestry(new ClassEntry("none/a")), is(empty())); + assertThat(m_index.getTranslationIndex().getAncestry(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty())); + assertThat(m_index.getTranslationIndex().getSubclass(new ClassEntry("none/a")), is(empty())); + assertThat(m_index.getTranslationIndex().getSubclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty())); + } + + @Test + public void access() { + assertThat(m_index.getAccess(newField("none/a", "a")), is(Access.Private)); + assertThat(m_index.getAccess(newMethod("none/a", "a", "()Ljava/lang/String;")), is(Access.Public)); + assertThat(m_index.getAccess(newField("none/a", "b")), is(nullValue())); + } + + @Test + public void classInheritance() { + ClassInheritanceTreeNode node = m_index.getClassInheritance(new Translator(), newClass("none/a")); + assertThat(node, is(not(nullValue()))); + assertThat(node.getObfClassName(), is("none/a")); + assertThat(node.getChildCount(), is(0)); + } + + @Test + public void methodInheritance() { + MethodEntry source = newMethod("none/a", "a", "()Ljava/lang/String;"); + MethodInheritanceTreeNode node = m_index.getMethodInheritance(new Translator(), source); + assertThat(node, is(not(nullValue()))); + assertThat(node.getMethodEntry(), is(source)); + assertThat(node.getChildCount(), is(0)); + } + + @Test + public void classImplementations() { + ClassImplementationsTreeNode node = m_index.getClassImplementations(new Translator(), newClass("none/a")); + assertThat(node, is(nullValue())); + } + + @Test + public void methodImplementations() { + MethodEntry source = newMethod("none/a", "a", "()Ljava/lang/String;"); + MethodImplementationsTreeNode node = m_index.getMethodImplementations(new Translator(), source); + assertThat(node, is(nullValue())); + } + + @Test + public void relatedMethodImplementations() { + Set entries = m_index.getRelatedMethodImplementations(newMethod("none/a", "a", "()Ljava/lang/String;")); + assertThat(entries, containsInAnyOrder( + newMethod("none/a", "a", "()Ljava/lang/String;") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void fieldReferences() { + FieldEntry source = newField("none/a", "a"); + Collection> references = m_index.getFieldReferences(source); + assertThat(references, containsInAnyOrder( + newFieldReferenceByConstructor(source, "none/a", "(Ljava/lang/String;)V"), + newFieldReferenceByMethod(source, "none/a", "a", "()Ljava/lang/String;") + )); + } + + @Test + public void behaviorReferences() { + assertThat(m_index.getBehaviorReferences(newMethod("none/a", "a", "()Ljava/lang/String;")), is(empty())); + } + + @Test + public void innerClasses() { + assertThat(m_index.getInnerClasses("none/a"), is(empty())); + } + + @Test + public void outerClass() { + assertThat(m_index.getOuterClass("a"), is(nullValue())); + } + + @Test + public void isAnonymousClass() { + assertThat(m_index.isAnonymousClass("none/a"), is(false)); + } + + @Test + public void interfaces() { + assertThat(m_index.getInterfaces("none/a"), is(empty())); + } + + @Test + public void implementingClasses() { + assertThat(m_index.getImplementingClasses("none/a"), is(empty())); + } + + @Test + public void isInterface() { + assertThat(m_index.isInterface("none/a"), is(false)); + } + + @Test + public void bridgeMethods() { + assertThat(m_index.getBridgeMethod(newMethod("none/a", "a", "()Ljava/lang/String;")), is(nullValue())); + } + + @Test + public void contains() { + assertThat(m_index.containsObfClass(newClass("none/a")), is(true)); + assertThat(m_index.containsObfClass(newClass("none/b")), is(false)); + assertThat(m_index.containsObfField(newField("none/a", "a")), is(true)); + assertThat(m_index.containsObfField(newField("none/a", "b")), is(false)); + assertThat(m_index.containsObfBehavior(newMethod("none/a", "a", "()Ljava/lang/String;")), is(true)); + assertThat(m_index.containsObfBehavior(newMethod("none/a", "b", "()Ljava/lang/String;")), is(false)); + } +} diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java new file mode 100644 index 00000000..70a5ee4c --- /dev/null +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.util.Set; +import java.util.jar.JarFile; + +import org.junit.Test; + +import com.google.common.collect.Sets; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; + +import cuchaz.enigma.mapping.ClassEntry; + +public class TestSourceIndex { + + // TEMP + @Test + public void indexEverything() throws Exception { + Deobfuscator deobfuscator = new Deobfuscator(new JarFile("input/1.8.jar")); + + // get all classes that aren't inner classes + Set classEntries = Sets.newHashSet(); + for (ClassEntry obfClassEntry : deobfuscator.getJarIndex().getObfClassEntries()) { + if (!obfClassEntry.isInnerClass()) { + classEntries.add(obfClassEntry); + } + } + + for (ClassEntry obfClassEntry : classEntries) { + try { + CompilationUnit tree = deobfuscator.getSourceTree(obfClassEntry.getName()); + String source = deobfuscator.getSource(tree); + deobfuscator.getSourceIndex(tree, source); + } catch (Throwable t) { + throw new Error("Unable to index " + obfClassEntry, t); + } + } + } +} diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java new file mode 100644 index 00000000..56424ae8 --- /dev/null +++ b/test/cuchaz/enigma/TestTokensConstructors.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.mapping.BehaviorEntry; + +public class TestTokensConstructors extends TokenChecker { + + public TestTokensConstructors() throws Exception { + super(new JarFile("build/testConstructors.obf.jar")); + } + + @Test + public void baseDeclarations() { + assertThat(getDeclarationToken(newConstructor("none/a", "()V")), is("a")); + assertThat(getDeclarationToken(newConstructor("none/a", "(I)V")), is("a")); + } + + @Test + public void subDeclarations() { + assertThat(getDeclarationToken(newConstructor("none/d", "()V")), is("d")); + assertThat(getDeclarationToken(newConstructor("none/d", "(I)V")), is("d")); + assertThat(getDeclarationToken(newConstructor("none/d", "(II)V")), is("d")); + assertThat(getDeclarationToken(newConstructor("none/d", "(III)V")), is("d")); + } + + @Test + public void subsubDeclarations() { + assertThat(getDeclarationToken(newConstructor("none/e", "(I)V")), is("e")); + } + + @Test + public void defaultDeclarations() { + assertThat(getDeclarationToken(newConstructor("none/c", "()V")), nullValue()); + } + + @Test + public void baseDefaultReferences() { + BehaviorEntry source = newConstructor("none/a", "()V"); + assertThat( + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "a", "()V")), + containsInAnyOrder("a") + ); + assertThat( + getReferenceTokens(newBehaviorReferenceByConstructor(source, "none/d", "()V")), + is(empty()) // implicit call, not decompiled to token + ); + assertThat( + getReferenceTokens(newBehaviorReferenceByConstructor(source, "none/d", "(III)V")), + is(empty()) // implicit call, not decompiled to token + ); + } + + @Test + public void baseIntReferences() { + BehaviorEntry source = newConstructor("none/a", "(I)V"); + assertThat( + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "b", "()V")), + containsInAnyOrder("a") + ); + } + + @Test + public void subDefaultReferences() { + BehaviorEntry source = newConstructor("none/d", "()V"); + assertThat( + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "c", "()V")), + containsInAnyOrder("d") + ); + assertThat( + getReferenceTokens(newBehaviorReferenceByConstructor(source, "none/d", "(I)V")), + containsInAnyOrder("this") + ); + } + + @Test + public void subIntReferences() { + BehaviorEntry source = newConstructor("none/d", "(I)V"); + assertThat(getReferenceTokens( + newBehaviorReferenceByMethod(source, "none/b", "d", "()V")), + containsInAnyOrder("d") + ); + assertThat(getReferenceTokens( + newBehaviorReferenceByConstructor(source, "none/d", "(II)V")), + containsInAnyOrder("this") + ); + assertThat(getReferenceTokens( + newBehaviorReferenceByConstructor(source, "none/e", "(I)V")), + containsInAnyOrder("super") + ); + } + + @Test + public void subIntIntReferences() { + BehaviorEntry source = newConstructor("none/d", "(II)V"); + assertThat( + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "e", "()V")), + containsInAnyOrder("d") + ); + } + + @Test + public void subsubIntReferences() { + BehaviorEntry source = newConstructor("none/e", "(I)V"); + assertThat( + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "f", "()V")), + containsInAnyOrder("e") + ); + } + + @Test + public void defaultConstructableReferences() { + BehaviorEntry source = newConstructor("none/c", "()V"); + assertThat( + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "g", "()V")), + containsInAnyOrder("c") + ); + } +} diff --git a/test/cuchaz/enigma/TokenChecker.java b/test/cuchaz/enigma/TokenChecker.java new file mode 100644 index 00000000..febea2ae --- /dev/null +++ b/test/cuchaz/enigma/TokenChecker.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.jar.JarFile; + +import com.google.common.collect.Lists; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.mapping.Entry; + +public class TokenChecker { + + private Deobfuscator m_deobfuscator; + + protected TokenChecker(JarFile jarFile) throws IOException { + m_deobfuscator = new Deobfuscator(jarFile); + } + + protected String getDeclarationToken(Entry entry) { + // decompile the class + CompilationUnit tree = m_deobfuscator.getSourceTree(entry.getClassName()); + // DEBUG + // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null ); + String source = m_deobfuscator.getSource(tree); + SourceIndex index = m_deobfuscator.getSourceIndex(tree, source); + + // get the token value + Token token = index.getDeclarationToken(entry); + if (token == null) { + return null; + } + return source.substring(token.start, token.end); + } + + @SuppressWarnings("unchecked") + protected Collection getReferenceTokens(EntryReference reference) { + // decompile the class + CompilationUnit tree = m_deobfuscator.getSourceTree(reference.context.getClassName()); + String source = m_deobfuscator.getSource(tree); + SourceIndex index = m_deobfuscator.getSourceIndex(tree, source); + + // get the token values + List values = Lists.newArrayList(); + for (Token token : index.getReferenceTokens((EntryReference)reference)) { + values.add(source.substring(token.start, token.end)); + } + return values; + } +} diff --git a/test/cuchaz/enigma/inputs/Keep.java b/test/cuchaz/enigma/inputs/Keep.java new file mode 100644 index 00000000..390e82fb --- /dev/null +++ b/test/cuchaz/enigma/inputs/Keep.java @@ -0,0 +1,7 @@ +package cuchaz.enigma.inputs; + +public class Keep { + public static void main(String[] args) { + System.out.println("Keep me!"); + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/BaseClass.java b/test/cuchaz/enigma/inputs/constructors/BaseClass.java new file mode 100644 index 00000000..93453086 --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/BaseClass.java @@ -0,0 +1,15 @@ +package cuchaz.enigma.inputs.constructors; + +// none/a +public class BaseClass { + + // ()V + public BaseClass() { + System.out.println("Default constructor"); + } + + // (I)V + public BaseClass(int i) { + System.out.println("Int constructor " + i); + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/Caller.java b/test/cuchaz/enigma/inputs/constructors/Caller.java new file mode 100644 index 00000000..57278751 --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/Caller.java @@ -0,0 +1,47 @@ +package cuchaz.enigma.inputs.constructors; + +// none/b +public class Caller { + + // a()V + public void callBaseDefault() { + // none/a.()V + System.out.println(new BaseClass()); + } + + // b()V + public void callBaseInt() { + // none/a.(I)V + System.out.println(new BaseClass(5)); + } + + // c()V + public void callSubDefault() { + // none/d.()V + System.out.println(new SubClass()); + } + + // d()V + public void callSubInt() { + // none/d.(I)V + System.out.println(new SubClass(6)); + } + + // e()V + public void callSubIntInt() { + // none/d.(II)V + System.out.println(new SubClass(4, 2)); + } + + // f()V + public void callSubSubInt() { + // none/e.(I)V + System.out.println(new SubSubClass(3)); + } + + // g()V + public void callDefaultConstructable() { + // none/c.()V + System.out.println(new DefaultConstructable()); + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java new file mode 100644 index 00000000..26a3ddbb --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java @@ -0,0 +1,5 @@ +package cuchaz.enigma.inputs.constructors; + +public class DefaultConstructable { + // only default constructor +} diff --git a/test/cuchaz/enigma/inputs/constructors/SubClass.java b/test/cuchaz/enigma/inputs/constructors/SubClass.java new file mode 100644 index 00000000..fecfa2b5 --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/SubClass.java @@ -0,0 +1,28 @@ +package cuchaz.enigma.inputs.constructors; + +// none/d extends none/a +public class SubClass extends BaseClass { + + // ()V + public SubClass() { + // none/a.()V + } + + // (I)V + public SubClass(int num) { + // ()V + this(); + System.out.println("SubClass " + num); + } + + // (II)V + public SubClass(int a, int b) { + // (I)V + this(a + b); + } + + // (III)V + public SubClass(int a, int b, int c) { + // none/a.()V + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java new file mode 100644 index 00000000..ab84161b --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java @@ -0,0 +1,11 @@ +package cuchaz.enigma.inputs.constructors; + +// none/e extends none/d +public class SubSubClass extends SubClass { + + // (I)V + public SubSubClass(int i) { + // none/c.(I)V + super(i); + } +} diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java new file mode 100644 index 00000000..5b416c41 --- /dev/null +++ b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java @@ -0,0 +1,21 @@ +package cuchaz.enigma.inputs.inheritanceTree; + +// none/a +public abstract class BaseClass { + + // a + private String m_name; + + // (Ljava/lang/String;)V + protected BaseClass(String name) { + m_name = name; + } + + // a()Ljava/lang/String; + public String getName() { + return m_name; + } + + // a()V + public abstract void doBaseThings(); +} diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java new file mode 100644 index 00000000..7a99d516 --- /dev/null +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java @@ -0,0 +1,11 @@ +package cuchaz.enigma.inputs.inheritanceTree; + +// none/b extends none/a +public abstract class SubclassA extends BaseClass { + + // (Ljava/lang/String;)V + protected SubclassA(String name) { + // call to none/a.(Ljava/lang/String)V + super(name); + } +} diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java new file mode 100644 index 00000000..c9485d31 --- /dev/null +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java @@ -0,0 +1,30 @@ +package cuchaz.enigma.inputs.inheritanceTree; + +// none/c extends none/a +public class SubclassB extends BaseClass { + + // a + private int m_numThings; + + // ()V + protected SubclassB() { + // none/a.(Ljava/lang/String;)V + super("B"); + + // access to a + m_numThings = 4; + } + + @Override + // a()V + public void doBaseThings() { + // call to none/a.a()Ljava/lang/String; + System.out.println("Base things by B! " + getName()); + } + + // b()V + public void doBThings() { + // access to a + System.out.println("" + m_numThings + " B things!"); + } +} diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java new file mode 100644 index 00000000..afd03ac4 --- /dev/null +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java @@ -0,0 +1,24 @@ +package cuchaz.enigma.inputs.inheritanceTree; + +// none/d extends none/b +public class SubsubclassAA extends SubclassA { + + protected SubsubclassAA() { + // call to none/b.(Ljava/lang/String;)V + super("AA"); + } + + @Override + // a()Ljava/lang/String; + public String getName() { + // call to none/b.a()Ljava/lang/String; + return "subsub" + super.getName(); + } + + @Override + // a()V + public void doBaseThings() { + // call to none/d.a()Ljava/lang/String; + System.out.println("Base things by " + getName()); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java b/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java new file mode 100644 index 00000000..f7118f63 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java @@ -0,0 +1,14 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class A_Anonymous { + + public void foo() { + Runnable runnable = new Runnable() { + @Override + public void run() { + // don't care + } + }; + runnable.run(); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java b/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java new file mode 100644 index 00000000..42fba9a8 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java @@ -0,0 +1,13 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class B_AnonymousWithScopeArgs { + + public static void foo(final D_Simple arg) { + System.out.println(new Object() { + @Override + public String toString() { + return arg.toString(); + } + }); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java b/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java new file mode 100644 index 00000000..8fa6c5b8 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java @@ -0,0 +1,20 @@ +package cuchaz.enigma.inputs.innerClasses; + +@SuppressWarnings("unused") +public class C_ConstructorArgs { + + class Inner { + + private int a; + + public Inner(int a) { + this.a = a; + } + } + + Inner i; + + public void foo() { + i = new Inner(5); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java b/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java new file mode 100644 index 00000000..c4fc0ef9 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java @@ -0,0 +1,8 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class D_Simple { + + class Inner { + // nothing to do + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java b/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java new file mode 100644 index 00000000..e1de53cb --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java @@ -0,0 +1,21 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class E_AnonymousWithOuterAccess { + + // reproduction of error case documented at: + // https://bitbucket.org/cuchaz/enigma/issue/61/stackoverflowerror-when-deobfuscating + + public Object makeInner() { + outerMethod(); + return new Object() { + @Override + public String toString() { + return outerMethod(); + } + }; + } + + private String outerMethod() { + return "foo"; + } +} diff --git a/test/cuchaz/enigma/inputs/loneClass/LoneClass.java b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java new file mode 100644 index 00000000..18c716e9 --- /dev/null +++ b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java @@ -0,0 +1,14 @@ +package cuchaz.enigma.inputs.loneClass; + +public class LoneClass { + + private String m_name; + + public LoneClass(String name) { + m_name = name; + } + + public String getName() { + return m_name; + } +} -- cgit v1.2.3 From 4a71742fed192bee96ce70c10a85dd2d855ec684 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 3 Feb 2015 22:02:40 -0500 Subject: explicitly add jre 7 to classpath --- .classpath | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.classpath b/.classpath index 7a9ca3de..389aeb52 100644 --- a/.classpath +++ b/.classpath @@ -3,8 +3,8 @@ - + -- cgit v1.2.3 From 6d05a33a8eddd389c27ae70e3485f6a8cf3807ea Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 4 Feb 2015 21:16:40 -0500 Subject: turn off debug stuff --- src/cuchaz/enigma/TranslatingTypeLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 9287999b..cfa03a1a 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -167,7 +167,7 @@ public class TranslatingTypeLoader implements ITypeLoader { assertClassName(c, deobfClassEntry); // DEBUG - Util.writeClass( c ); + //Util.writeClass( c ); // we have a transformed class! return c.toBytecode(); -- cgit v1.2.3 From 73313b0f1c1660986afc1449960889cac242eee0 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 5 Feb 2015 23:51:40 -0500 Subject: add new type/signature system --- src/cuchaz/enigma/mapping/BehaviorSignature.java | 96 +++++++++++ src/cuchaz/enigma/mapping/Type.java | 179 +++++++++++++++++++++ test/cuchaz/enigma/TestBehaviorSignature.java | 196 +++++++++++++++++++++++ test/cuchaz/enigma/TestType.java | 183 +++++++++++++++++++++ 4 files changed, 654 insertions(+) create mode 100644 src/cuchaz/enigma/mapping/BehaviorSignature.java create mode 100644 src/cuchaz/enigma/mapping/Type.java create mode 100644 test/cuchaz/enigma/TestBehaviorSignature.java create mode 100644 test/cuchaz/enigma/TestType.java diff --git a/src/cuchaz/enigma/mapping/BehaviorSignature.java b/src/cuchaz/enigma/mapping/BehaviorSignature.java new file mode 100644 index 00000000..a6371f85 --- /dev/null +++ b/src/cuchaz/enigma/mapping/BehaviorSignature.java @@ -0,0 +1,96 @@ +package cuchaz.enigma.mapping; + +import java.util.List; + +import com.beust.jcommander.internal.Lists; + +public class BehaviorSignature { + + public static interface ClassReplacer { + ClassEntry replace(ClassEntry entry); + } + + private List m_argumentTypes; + private Type m_returnType; + + public BehaviorSignature(String signature) { + m_argumentTypes = Lists.newArrayList(); + int i=0; + while (i getArgumentTypes() { + return m_argumentTypes; + } + + public Type getReturnType() { + return m_returnType; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("("); + for (int i=0; i 0) { + buf.append(","); + } + buf.append(m_argumentTypes.get(i).toString()); + } + buf.append(")"); + buf.append(m_returnType.toString()); + return buf.toString(); + } + + public Iterable types() { + List types = Lists.newArrayList(); + types.addAll(m_argumentTypes); + types.add(m_returnType); + return types; + } + + public Iterable classes() { + List out = Lists.newArrayList(); + for (Type type : types()) { + if (type.isClass()) { + out.add(type.getClassEntry()); + } + } + return out; + } +} diff --git a/src/cuchaz/enigma/mapping/Type.java b/src/cuchaz/enigma/mapping/Type.java new file mode 100644 index 00000000..2273e21b --- /dev/null +++ b/src/cuchaz/enigma/mapping/Type.java @@ -0,0 +1,179 @@ +package cuchaz.enigma.mapping; + +import java.util.Map; + +import com.google.common.collect.Maps; + +public class Type { + + public enum Primitive { + Byte('B'), + Character('C'), + Short('S'), + Integer('I'), + Long('J'), + Float('F'), + Double('D'), + Boolean('Z'); + + private static final Map m_lookup; + + static { + m_lookup = Maps.newTreeMap(); + for (Primitive val : values()) { + m_lookup.put(val.getCode(), val); + } + } + + public static Primitive get(char code) { + return m_lookup.get(code); + } + + private char m_code; + + private Primitive(char code) { + m_code = code; + } + + public char getCode() { + return m_code; + } + } + + public static String parseFirst(String in) { + + // read one type from the input + + char c = in.charAt(0); + + // first check for void + if (c == 'V') { + return "V"; + } + + // then check for primitives + Primitive primitive = Primitive.get(c); + if (primitive != null) { + return in.substring(0, 1); + } + + // then check for classes + if (c == 'L') { + return readClass(in); + } + + // then check for arrays + int dim = countArrayDimension(in); + if (dim > 0) { + String arrayType = Type.parseFirst(in.substring(dim)); + return in.substring(0, dim + arrayType.length()); + } + + throw new IllegalArgumentException("don't know how to parse: " + in); + } + + private String m_name; + + public Type(String name) { + m_name = name; + } + + public Type(ClassEntry classEntry) { + m_name = "L" + classEntry.getClassName() + ";"; + } + + @Override + public String toString() { + return m_name; + } + + public boolean isVoid() { + return m_name.length() == 1 && m_name.charAt(0) == 'V'; + } + + public boolean isPrimitive() { + return m_name.length() == 1 && Primitive.get(m_name.charAt(0)) != null; + } + + public Primitive getPrimitive() { + if (!isPrimitive()) { + throw new IllegalStateException("not a primitive"); + } + return Primitive.get(m_name.charAt(0)); + } + + public boolean isClass() { + return m_name.charAt(0) == 'L' && m_name.charAt(m_name.length() - 1) == ';'; + } + + public ClassEntry getClassEntry() { + if (!isClass()) { + throw new IllegalStateException("not a class"); + } + String name = m_name.substring(1, m_name.length() - 1); + + int pos = name.indexOf('<'); + if (pos >= 0) { + // remove the parameters from the class name + name = name.substring(0, pos); + } + + return new ClassEntry(name); + } + + public boolean isArray() { + return m_name.charAt(0) == '['; + } + + public int getArrayDimension() { + if (!isArray()) { + throw new IllegalStateException("not an array"); + } + return countArrayDimension(m_name); + } + + public Type getArrayType() { + if (!isArray()) { + throw new IllegalStateException("not an array"); + } + return new Type(m_name.substring(getArrayDimension(), m_name.length())); + } + + @Override + public boolean equals(Object other) { + if (other instanceof Type) { + return equals((Type)other); + } + return false; + } + + public boolean equals(Type other) { + return m_name.equals(other.m_name); + } + + private static int countArrayDimension(String in) { + int i=0; + for(; i < in.length() && in.charAt(i) == '['; i++); + return i; + } + + private static String readClass(String in) { + // read all the characters in the buffer until we hit a ';' + // remember to treat parameters correctly + StringBuilder buf = new StringBuilder(); + int depth = 0; + for (int i=0; i') { + depth--; + } else if (depth == 0 && c == ';') { + return buf.toString(); + } + } + return null; + } +} diff --git a/test/cuchaz/enigma/TestBehaviorSignature.java b/test/cuchaz/enigma/TestBehaviorSignature.java new file mode 100644 index 00000000..4705cd12 --- /dev/null +++ b/test/cuchaz/enigma/TestBehaviorSignature.java @@ -0,0 +1,196 @@ +package cuchaz.enigma; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import org.junit.Test; + +import cuchaz.enigma.mapping.BehaviorSignature; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Type; + + +public class TestBehaviorSignature { + + @Test + public void easiest() { + final BehaviorSignature sig = new BehaviorSignature("()V"); + assertThat(sig.getArgumentTypes(), is(empty())); + assertThat(sig.getReturnType(), is(new Type("V"))); + } + + @Test + public void primitives() { + { + final BehaviorSignature sig = new BehaviorSignature("(I)V"); + assertThat(sig.getArgumentTypes(), contains( + new Type("I") + )); + assertThat(sig.getReturnType(), is(new Type("V"))); + } + { + final BehaviorSignature sig = new BehaviorSignature("(I)I"); + assertThat(sig.getArgumentTypes(), contains( + new Type("I") + )); + assertThat(sig.getReturnType(), is(new Type("I"))); + } + { + final BehaviorSignature sig = new BehaviorSignature("(IBCJ)Z"); + assertThat(sig.getArgumentTypes(), contains( + new Type("I"), + new Type("B"), + new Type("C"), + new Type("J") + )); + assertThat(sig.getReturnType(), is(new Type("Z"))); + } + } + + @Test + public void classes() { + { + final BehaviorSignature sig = new BehaviorSignature("([LFoo;)V"); + assertThat(sig.getArgumentTypes().size(), is(1)); + assertThat(sig.getArgumentTypes().get(0), is(new Type("[LFoo;"))); + assertThat(sig.getReturnType(), is(new Type("V"))); + } + { + final BehaviorSignature sig = new BehaviorSignature("(LFoo;)LBar;"); + assertThat(sig.getArgumentTypes(), contains( + new Type("LFoo;") + )); + assertThat(sig.getReturnType(), is(new Type("LBar;"))); + } + { + final BehaviorSignature sig = new BehaviorSignature("(LFoo;LMoo;LZoo;)LBar;"); + assertThat(sig.getArgumentTypes(), contains( + new Type("LFoo;"), + new Type("LMoo;"), + new Type("LZoo;") + )); + assertThat(sig.getReturnType(), is(new Type("LBar;"))); + } + { + final BehaviorSignature sig = new BehaviorSignature("(LFoo;LMoo;)LBar;"); + assertThat(sig.getArgumentTypes(), contains( + new Type("LFoo;"), + new Type("LMoo;") + )); + assertThat(sig.getReturnType(), is(new Type("LBar;"))); + } + } + + @Test + public void arrays() { + { + final BehaviorSignature sig = new BehaviorSignature("([I)V"); + assertThat(sig.getArgumentTypes(), contains( + new Type("[I") + )); + assertThat(sig.getReturnType(), is(new Type("V"))); + } + { + final BehaviorSignature sig = new BehaviorSignature("([I)[J"); + assertThat(sig.getArgumentTypes(), contains( + new Type("[I") + )); + assertThat(sig.getReturnType(), is(new Type("[J"))); + } + { + final BehaviorSignature sig = new BehaviorSignature("([I[Z[F)[D"); + assertThat(sig.getArgumentTypes(), contains( + new Type("[I"), + new Type("[Z"), + new Type("[F") + )); + assertThat(sig.getReturnType(), is(new Type("[D"))); + } + } + + @Test + public void mixed() { + { + final BehaviorSignature sig = new BehaviorSignature("(I[JLFoo;)Z"); + assertThat(sig.getArgumentTypes(), contains( + new Type("I"), + new Type("[J"), + new Type("LFoo;") + )); + assertThat(sig.getReturnType(), is(new Type("Z"))); + } + { + final BehaviorSignature sig = new BehaviorSignature("(III)[LFoo;"); + assertThat(sig.getArgumentTypes(), contains( + new Type("I"), + new Type("I"), + new Type("I") + )); + assertThat(sig.getReturnType(), is(new Type("[LFoo;"))); + } + } + + @Test + public void replaceClasses() { + { + final BehaviorSignature oldSig = new BehaviorSignature("()V"); + final BehaviorSignature sig = new BehaviorSignature(oldSig, new BehaviorSignature.ClassReplacer() { + @Override + public ClassEntry replace(ClassEntry entry) { + return null; + } + }); + assertThat(sig.getArgumentTypes(), is(empty())); + assertThat(sig.getReturnType(), is(new Type("V"))); + } + { + final BehaviorSignature oldSig = new BehaviorSignature("(IJLFoo;)V"); + final BehaviorSignature sig = new BehaviorSignature(oldSig, new BehaviorSignature.ClassReplacer() { + @Override + public ClassEntry replace(ClassEntry entry) { + return null; + } + }); + assertThat(sig.getArgumentTypes(), contains( + new Type("I"), + new Type("J"), + new Type("LFoo;") + )); + assertThat(sig.getReturnType(), is(new Type("V"))); + } + { + final BehaviorSignature oldSig = new BehaviorSignature("(LFoo;LBar;)LMoo;"); + final BehaviorSignature sig = new BehaviorSignature(oldSig, new BehaviorSignature.ClassReplacer() { + @Override + public ClassEntry replace(ClassEntry entry) { + if (entry.getName().equals("Foo")) { + return new ClassEntry("Bar"); + } + return null; + } + }); + assertThat(sig.getArgumentTypes(), contains( + new Type("LBar;"), + new Type("LBar;") + )); + assertThat(sig.getReturnType(), is(new Type("LMoo;"))); + } + { + final BehaviorSignature oldSig = new BehaviorSignature("(LFoo;LBar;)LMoo;"); + final BehaviorSignature sig = new BehaviorSignature(oldSig, new BehaviorSignature.ClassReplacer() { + @Override + public ClassEntry replace(ClassEntry entry) { + if (entry.getName().equals("Moo")) { + return new ClassEntry("Cow"); + } + return null; + } + }); + assertThat(sig.getArgumentTypes(), contains( + new Type("LFoo;"), + new Type("LBar;") + )); + assertThat(sig.getReturnType(), is(new Type("LCow;"))); + } + } +} diff --git a/test/cuchaz/enigma/TestType.java b/test/cuchaz/enigma/TestType.java new file mode 100644 index 00000000..efc6a4c3 --- /dev/null +++ b/test/cuchaz/enigma/TestType.java @@ -0,0 +1,183 @@ +package cuchaz.enigma; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import cuchaz.enigma.mapping.Type; + +import static cuchaz.enigma.EntryFactory.*; + + +public class TestType { + + @Test + public void isVoid() { + assertThat(new Type("V").isVoid(), is(true)); + assertThat(new Type("Z").isVoid(), is(false)); + assertThat(new Type("B").isVoid(), is(false)); + assertThat(new Type("C").isVoid(), is(false)); + assertThat(new Type("I").isVoid(), is(false)); + assertThat(new Type("J").isVoid(), is(false)); + assertThat(new Type("F").isVoid(), is(false)); + assertThat(new Type("D").isVoid(), is(false)); + assertThat(new Type("LFoo;").isVoid(), is(false)); + assertThat(new Type("[I").isVoid(), is(false)); + } + + @Test + public void isPrimitive() { + assertThat(new Type("V").isPrimitive(), is(false)); + assertThat(new Type("Z").isPrimitive(), is(true)); + assertThat(new Type("B").isPrimitive(), is(true)); + assertThat(new Type("C").isPrimitive(), is(true)); + assertThat(new Type("I").isPrimitive(), is(true)); + assertThat(new Type("J").isPrimitive(), is(true)); + assertThat(new Type("F").isPrimitive(), is(true)); + assertThat(new Type("D").isPrimitive(), is(true)); + assertThat(new Type("LFoo;").isPrimitive(), is(false)); + assertThat(new Type("[I").isPrimitive(), is(false)); + } + + @Test + public void getPrimitive() { + assertThat(new Type("Z").getPrimitive(), is(Type.Primitive.Boolean)); + assertThat(new Type("B").getPrimitive(), is(Type.Primitive.Byte)); + assertThat(new Type("C").getPrimitive(), is(Type.Primitive.Character)); + assertThat(new Type("I").getPrimitive(), is(Type.Primitive.Integer)); + assertThat(new Type("J").getPrimitive(), is(Type.Primitive.Long)); + assertThat(new Type("F").getPrimitive(), is(Type.Primitive.Float)); + assertThat(new Type("D").getPrimitive(), is(Type.Primitive.Double)); + } + + @Test + public void isClass() { + assertThat(new Type("V").isClass(), is(false)); + assertThat(new Type("Z").isClass(), is(false)); + assertThat(new Type("B").isClass(), is(false)); + assertThat(new Type("C").isClass(), is(false)); + assertThat(new Type("I").isClass(), is(false)); + assertThat(new Type("J").isClass(), is(false)); + assertThat(new Type("F").isClass(), is(false)); + assertThat(new Type("D").isClass(), is(false)); + assertThat(new Type("LFoo;").isClass(), is(true)); + assertThat(new Type("[I").isClass(), is(false)); + } + + @Test + public void getClassEntry() { + assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); + assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); + } + + @Test + public void isArray() { + assertThat(new Type("V").isArray(), is(false)); + assertThat(new Type("Z").isArray(), is(false)); + assertThat(new Type("B").isArray(), is(false)); + assertThat(new Type("C").isArray(), is(false)); + assertThat(new Type("I").isArray(), is(false)); + assertThat(new Type("J").isArray(), is(false)); + assertThat(new Type("F").isArray(), is(false)); + assertThat(new Type("D").isArray(), is(false)); + assertThat(new Type("LFoo;").isArray(), is(false)); + assertThat(new Type("[I").isArray(), is(true)); + } + + @Test + public void getArrayDimension() { + assertThat(new Type("[I").getArrayDimension(), is(1)); + assertThat(new Type("[[I").getArrayDimension(), is(2)); + assertThat(new Type("[[[I").getArrayDimension(), is(3)); + } + + @Test + public void getArrayType() { + assertThat(new Type("[I").getArrayType(), is(new Type("I"))); + assertThat(new Type("[[I").getArrayType(), is(new Type("I"))); + assertThat(new Type("[[[I").getArrayType(), is(new Type("I"))); + assertThat(new Type("[Ljava/lang/String;").getArrayType(), is(new Type("Ljava/lang/String;"))); + } + + @Test + public void parseVoid() { + final String answer = "V"; + assertThat(Type.parseFirst("V"), is(answer)); + assertThat(Type.parseFirst("VVV"), is(answer)); + assertThat(Type.parseFirst("VIJ"), is(answer)); + assertThat(Type.parseFirst("V[I"), is(answer)); + assertThat(Type.parseFirst("VLFoo;"), is(answer)); + assertThat(Type.parseFirst("V[LFoo;"), is(answer)); + } + + @Test + public void parsePrimitive() { + final String answer = "I"; + assertThat(Type.parseFirst("I"), is(answer)); + assertThat(Type.parseFirst("III"), is(answer)); + assertThat(Type.parseFirst("IJZ"), is(answer)); + assertThat(Type.parseFirst("I[I"), is(answer)); + assertThat(Type.parseFirst("ILFoo;"), is(answer)); + assertThat(Type.parseFirst("I[LFoo;"), is(answer)); + } + + @Test + public void parseClass() { + { + final String answer = "LFoo;"; + assertThat(Type.parseFirst("LFoo;"), is(answer)); + assertThat(Type.parseFirst("LFoo;I"), is(answer)); + assertThat(Type.parseFirst("LFoo;JZ"), is(answer)); + assertThat(Type.parseFirst("LFoo;[I"), is(answer)); + assertThat(Type.parseFirst("LFoo;LFoo;"), is(answer)); + assertThat(Type.parseFirst("LFoo;[LFoo;"), is(answer)); + } + { + final String answer = "LFoo;"; + assertThat(Type.parseFirst("LFoo;"), is(answer)); + assertThat(Type.parseFirst("LFoo;I"), is(answer)); + assertThat(Type.parseFirst("LFoo;JZ"), is(answer)); + assertThat(Type.parseFirst("LFoo;[I"), is(answer)); + assertThat(Type.parseFirst("LFoo;LFoo;"), is(answer)); + assertThat(Type.parseFirst("LFoo;[LFoo;"), is(answer)); + } + { + final String answer = "LFoo;"; + assertThat(Type.parseFirst("LFoo;"), is(answer)); + assertThat(Type.parseFirst("LFoo;I"), is(answer)); + assertThat(Type.parseFirst("LFoo;JZ"), is(answer)); + assertThat(Type.parseFirst("LFoo;[I"), is(answer)); + assertThat(Type.parseFirst("LFoo;LFoo;"), is(answer)); + assertThat(Type.parseFirst("LFoo;[LFoo;"), is(answer)); + } + } + + @Test + public void parseArray() { + { + final String answer = "[I"; + assertThat(Type.parseFirst("[I"), is(answer)); + assertThat(Type.parseFirst("[III"), is(answer)); + assertThat(Type.parseFirst("[IJZ"), is(answer)); + assertThat(Type.parseFirst("[I[I"), is(answer)); + assertThat(Type.parseFirst("[ILFoo;"), is(answer)); + } + { + final String answer = "[[I"; + assertThat(Type.parseFirst("[[I"), is(answer)); + assertThat(Type.parseFirst("[[III"), is(answer)); + assertThat(Type.parseFirst("[[IJZ"), is(answer)); + assertThat(Type.parseFirst("[[I[I"), is(answer)); + assertThat(Type.parseFirst("[[ILFoo;"), is(answer)); + } + { + final String answer = "[LFoo;"; + assertThat(Type.parseFirst("[LFoo;"), is(answer)); + assertThat(Type.parseFirst("[LFoo;II"), is(answer)); + assertThat(Type.parseFirst("[LFoo;JZ"), is(answer)); + assertThat(Type.parseFirst("[LFoo;[I"), is(answer)); + assertThat(Type.parseFirst("[LFoo;LFoo;"), is(answer)); + } + } +} -- cgit v1.2.3 From 818716acb3992e602e01725b6e151c1eb0f6c2e2 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 5 Feb 2015 23:52:55 -0500 Subject: fix test code formatting and disable the super duper slow test for now --- test/cuchaz/enigma/TestDeobfuscator.java | 12 ++++++++---- test/cuchaz/enigma/TestInnerClasses.java | 3 ++- test/cuchaz/enigma/TestJarIndexConstructorReferences.java | 3 ++- test/cuchaz/enigma/TestJarIndexInheritanceTree.java | 3 ++- test/cuchaz/enigma/TestJarIndexLoneClass.java | 3 ++- test/cuchaz/enigma/TestSourceIndex.java | 5 +++-- test/cuchaz/enigma/TestTokensConstructors.java | 3 ++- test/cuchaz/enigma/TokenChecker.java | 3 ++- 8 files changed, 23 insertions(+), 12 deletions(-) diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java index 129d7b25..26d492d8 100644 --- a/test/cuchaz/enigma/TestDeobfuscator.java +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -25,17 +25,20 @@ import cuchaz.enigma.mapping.ClassEntry; public class TestDeobfuscator { - private Deobfuscator getDeobfuscator() throws IOException { + private Deobfuscator getDeobfuscator() + throws IOException { return new Deobfuscator(new JarFile("build/testLoneClass.obf.jar")); } @Test - public void loadJar() throws Exception { + public void loadJar() + throws Exception { getDeobfuscator(); } @Test - public void getClasses() throws Exception { + public void getClasses() + throws Exception { Deobfuscator deobfuscator = getDeobfuscator(); List obfClasses = Lists.newArrayList(); List deobfClasses = Lists.newArrayList(); @@ -47,7 +50,8 @@ public class TestDeobfuscator { } @Test - public void decompileClass() throws Exception { + public void decompileClass() + throws Exception { Deobfuscator deobfuscator = getDeobfuscator(); deobfuscator.getSource(deobfuscator.getSourceTree("none/a")); } diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java index 63c9b719..2e16a330 100644 --- a/test/cuchaz/enigma/TestInnerClasses.java +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -36,7 +36,8 @@ public class TestInnerClasses { private static final String AnonymousWithOuterAccessOuter = "none/i"; private static final String AnonymousWithOuterAccessInner = "j"; - public TestInnerClasses() throws Exception { + public TestInnerClasses() + throws Exception { m_index = new JarIndex(); JarFile jar = new JarFile("build/testInnerClasses.obf.jar"); m_index.indexJar(jar, true); diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java index 8e3ad6d2..a735de7c 100644 --- a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -36,7 +36,8 @@ public class TestJarIndexConstructorReferences { private ClassEntry m_defaultClass = new ClassEntry("none/c"); private ClassEntry m_callerClass = new ClassEntry("none/b"); - public TestJarIndexConstructorReferences() throws Exception { + public TestJarIndexConstructorReferences() + throws Exception { File jarFile = new File("build/testConstructors.obf.jar"); m_index = new JarIndex(); m_index.indexJar(new JarFile(jarFile), false); diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 4d663972..26499f06 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -41,7 +41,8 @@ public class TestJarIndexInheritanceTree { private FieldEntry m_nameField = new FieldEntry(m_baseClass, "a"); private FieldEntry m_numThingsField = new FieldEntry(m_subClassB, "a"); - public TestJarIndexInheritanceTree() throws Exception { + public TestJarIndexInheritanceTree() + throws Exception { m_index = new JarIndex(); m_index.indexJar(new JarFile("build/testInheritanceTree.obf.jar"), false); } diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index a061b72d..108c623a 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -38,7 +38,8 @@ public class TestJarIndexLoneClass { private JarIndex m_index; - public TestJarIndexLoneClass() throws Exception { + public TestJarIndexLoneClass() + throws Exception { m_index = new JarIndex(); m_index.indexJar(new JarFile("build/testLoneClass.obf.jar"), false); } diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java index 70a5ee4c..357acb62 100644 --- a/test/cuchaz/enigma/TestSourceIndex.java +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -24,8 +24,9 @@ import cuchaz.enigma.mapping.ClassEntry; public class TestSourceIndex { // TEMP - @Test - public void indexEverything() throws Exception { + //@Test + public void indexEverything() + throws Exception { Deobfuscator deobfuscator = new Deobfuscator(new JarFile("input/1.8.jar")); // get all classes that aren't inner classes diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java index 56424ae8..6758d2a7 100644 --- a/test/cuchaz/enigma/TestTokensConstructors.java +++ b/test/cuchaz/enigma/TestTokensConstructors.java @@ -22,7 +22,8 @@ import cuchaz.enigma.mapping.BehaviorEntry; public class TestTokensConstructors extends TokenChecker { - public TestTokensConstructors() throws Exception { + public TestTokensConstructors() + throws Exception { super(new JarFile("build/testConstructors.obf.jar")); } diff --git a/test/cuchaz/enigma/TokenChecker.java b/test/cuchaz/enigma/TokenChecker.java index febea2ae..a72c2fc8 100644 --- a/test/cuchaz/enigma/TokenChecker.java +++ b/test/cuchaz/enigma/TokenChecker.java @@ -27,7 +27,8 @@ public class TokenChecker { private Deobfuscator m_deobfuscator; - protected TokenChecker(JarFile jarFile) throws IOException { + protected TokenChecker(JarFile jarFile) + throws IOException { m_deobfuscator = new Deobfuscator(jarFile); } -- cgit v1.2.3 From cb22a55b241714a56f78b9c6ee863f0a37bbf005 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 5 Feb 2015 23:53:41 -0500 Subject: start some translation tests --- build.py | 1 + src/cuchaz/enigma/mapping/Mappings.java | 23 ------------- test/cuchaz/enigma/TestTranslator.java | 39 +++++++++++++++++++++++ test/cuchaz/enigma/inputs/translation/A.java | 8 +++++ test/cuchaz/enigma/resources/translation.mappings | 4 +++ 5 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 test/cuchaz/enigma/TestTranslator.java create mode 100644 test/cuchaz/enigma/inputs/translation/A.java create mode 100644 test/cuchaz/enigma/resources/translation.mappings diff --git a/build.py b/build.py index 8e37d0ff..47294987 100644 --- a/build.py +++ b/build.py @@ -105,6 +105,7 @@ def taskBuildTestJars(): buildTestJar("testConstructors", "cuchaz/enigma/inputs/constructors/*.class") buildTestJar("testInheritanceTree", "cuchaz/enigma/inputs/inheritanceTree/*.class") buildTestJar("testInnerClasses", "cuchaz/enigma/inputs/innerClasses/*.class") + buildTestJar("testTranslation", "cuchaz/enigma/inputs/translation/*.class") def taskBuild(): ssjb.file.delete(DirBuild) diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index cc560a87..92134edf 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -10,20 +10,15 @@ ******************************************************************************/ package cuchaz.enigma.mapping; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.Set; -import java.util.zip.GZIPInputStream; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import cuchaz.enigma.Util; import cuchaz.enigma.analysis.TranslationIndex; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; @@ -50,16 +45,6 @@ public class Mappings implements Serializable { } } - public static Mappings newFromResource(String resource) throws IOException { - InputStream in = null; - try { - in = Mappings.class.getResourceAsStream(resource); - return newFromStream(in); - } finally { - Util.closeQuietly(in); - } - } - public Collection classes() { assert (m_classesByObf.size() >= m_classesByDeobf.size()); return m_classesByObf.values(); @@ -134,14 +119,6 @@ public class Mappings implements Serializable { } } - public static Mappings newFromStream(InputStream in) throws IOException { - try { - return (Mappings)new ObjectInputStream(new GZIPInputStream(in)).readObject(); - } catch (ClassNotFoundException ex) { - throw new Error(ex); - } - } - @Override public String toString() { StringBuilder buf = new StringBuilder(); diff --git a/test/cuchaz/enigma/TestTranslator.java b/test/cuchaz/enigma/TestTranslator.java new file mode 100644 index 00000000..290f6f04 --- /dev/null +++ b/test/cuchaz/enigma/TestTranslator.java @@ -0,0 +1,39 @@ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.TranslationDirection; +import cuchaz.enigma.mapping.Translator; + + +public class TestTranslator { + + private Deobfuscator m_deobfuscator; + private Mappings m_mappings; + + public TestTranslator() + throws Exception { + m_deobfuscator = new Deobfuscator(new JarFile("build/testTranslation.obf.jar")); + try (InputStream in = getClass().getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) { + m_mappings = new MappingsReader().read(new InputStreamReader(in)); + m_deobfuscator.setMappings(m_mappings); + } + } + + @Test + public void deobfuscatingTranslations() + throws Exception { + Translator translator = m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating); + assertThat(translator.translateEntry(newClass("none/a")), is(newClass("deobf/A"))); + } +} diff --git a/test/cuchaz/enigma/inputs/translation/A.java b/test/cuchaz/enigma/inputs/translation/A.java new file mode 100644 index 00000000..b8aaf11e --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/A.java @@ -0,0 +1,8 @@ +package cuchaz.enigma.inputs.translation; + +public class A { + + public int one; + public float two; + public String three; +} diff --git a/test/cuchaz/enigma/resources/translation.mappings b/test/cuchaz/enigma/resources/translation.mappings new file mode 100644 index 00000000..70755bf6 --- /dev/null +++ b/test/cuchaz/enigma/resources/translation.mappings @@ -0,0 +1,4 @@ +CLASS none/a deobf/A + FIELD a one + FIELD b two + FIELD c three \ No newline at end of file -- cgit v1.2.3 From ed9b5cdfc648e86fd463bfa8d86b94c41671e14c Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 8 Feb 2015 21:29:25 -0500 Subject: switch all classes to new signature/type system --- .classpath | 11 + .hgignore | 9 + .project | 17 + .settings/org.eclipse.jdt.core.prefs | 13 + build.py | 120 ++ conf/about.html | 6 + license.APL2.txt | 55 + license.GPL3.txt | 674 +++++++++++ proguard.conf | 7 + readme.txt | 28 + src/cuchaz/enigma/CommandMain.java | 136 +++ src/cuchaz/enigma/Constants.java | 20 + src/cuchaz/enigma/Deobfuscator.java | 539 +++++++++ src/cuchaz/enigma/Main.java | 51 + src/cuchaz/enigma/TranslatingTypeLoader.java | 211 ++++ src/cuchaz/enigma/Util.java | 104 ++ src/cuchaz/enigma/analysis/Access.java | 43 + .../enigma/analysis/BehaviorReferenceTreeNode.java | 93 ++ .../analysis/ClassImplementationsTreeNode.java | 80 ++ .../enigma/analysis/ClassInheritanceTreeNode.java | 85 ++ src/cuchaz/enigma/analysis/EntryReference.java | 126 +++ src/cuchaz/enigma/analysis/EntryRenamer.java | 171 +++ .../enigma/analysis/FieldReferenceTreeNode.java | 81 ++ src/cuchaz/enigma/analysis/JarClassIterator.java | 137 +++ src/cuchaz/enigma/analysis/JarIndex.java | 734 ++++++++++++ .../analysis/MethodImplementationsTreeNode.java | 100 ++ .../enigma/analysis/MethodInheritanceTreeNode.java | 114 ++ src/cuchaz/enigma/analysis/ReferenceTreeNode.java | 18 + src/cuchaz/enigma/analysis/SourceIndex.java | 173 +++ .../analysis/SourceIndexBehaviorVisitor.java | 164 +++ .../enigma/analysis/SourceIndexClassVisitor.java | 115 ++ src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 452 ++++++++ src/cuchaz/enigma/analysis/Token.java | 56 + src/cuchaz/enigma/analysis/TranslationIndex.java | 227 ++++ src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 512 +++++++++ src/cuchaz/enigma/bytecode/CheckCastIterator.java | 127 +++ src/cuchaz/enigma/bytecode/ClassRenamer.java | 110 ++ src/cuchaz/enigma/bytecode/ClassTranslator.java | 144 +++ src/cuchaz/enigma/bytecode/ConstPoolEditor.java | 263 +++++ src/cuchaz/enigma/bytecode/InfoType.java | 317 ++++++ src/cuchaz/enigma/bytecode/InnerClassWriter.java | 102 ++ .../enigma/bytecode/MethodParameterWriter.java | 53 + .../enigma/bytecode/MethodParametersAttribute.java | 85 ++ .../bytecode/accessors/ClassInfoAccessor.java | 55 + .../bytecode/accessors/ConstInfoAccessor.java | 156 +++ .../accessors/InvokeDynamicInfoAccessor.java | 74 ++ .../bytecode/accessors/MemberRefInfoAccessor.java | 74 ++ .../accessors/MethodHandleInfoAccessor.java | 74 ++ .../bytecode/accessors/MethodTypeInfoAccessor.java | 55 + .../accessors/NameAndTypeInfoAccessor.java | 74 ++ .../bytecode/accessors/StringInfoAccessor.java | 55 + .../bytecode/accessors/Utf8InfoAccessor.java | 28 + src/cuchaz/enigma/convert/ClassIdentity.java | 411 +++++++ src/cuchaz/enigma/convert/ClassMatcher.java | 406 +++++++ src/cuchaz/enigma/convert/ClassMatching.java | 173 +++ src/cuchaz/enigma/convert/ClassNamer.java | 64 ++ src/cuchaz/enigma/gui/AboutDialog.java | 86 ++ src/cuchaz/enigma/gui/BoxHighlightPainter.java | 64 ++ src/cuchaz/enigma/gui/BrowserCaret.java | 45 + src/cuchaz/enigma/gui/ClassListCellRenderer.java | 36 + src/cuchaz/enigma/gui/ClassSelector.java | 164 +++ src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 35 + .../enigma/gui/ClassSelectorPackageNode.java | 33 + src/cuchaz/enigma/gui/CrashDialog.java | 101 ++ .../enigma/gui/DeobfuscatedHighlightPainter.java | 21 + src/cuchaz/enigma/gui/Gui.java | 1165 ++++++++++++++++++++ src/cuchaz/enigma/gui/GuiController.java | 355 ++++++ src/cuchaz/enigma/gui/GuiTricks.java | 36 + .../enigma/gui/ObfuscatedHighlightPainter.java | 21 + src/cuchaz/enigma/gui/OtherHighlightPainter.java | 21 + src/cuchaz/enigma/gui/ProgressDialog.java | 105 ++ src/cuchaz/enigma/gui/ReadableToken.java | 36 + src/cuchaz/enigma/gui/RenameListener.java | 17 + .../enigma/gui/SelectionHighlightPainter.java | 34 + src/cuchaz/enigma/gui/TokenListCellRenderer.java | 38 + src/cuchaz/enigma/mapping/ArgumentEntry.java | 116 ++ src/cuchaz/enigma/mapping/ArgumentMapping.java | 44 + src/cuchaz/enigma/mapping/BehaviorEntry.java | 15 + .../enigma/mapping/BehaviorEntryFactory.java | 57 + src/cuchaz/enigma/mapping/ClassEntry.java | 123 +++ src/cuchaz/enigma/mapping/ClassMapping.java | 405 +++++++ src/cuchaz/enigma/mapping/ClassNameReplacer.java | 5 + src/cuchaz/enigma/mapping/ConstructorEntry.java | 116 ++ src/cuchaz/enigma/mapping/Entry.java | 18 + src/cuchaz/enigma/mapping/EntryPair.java | 22 + src/cuchaz/enigma/mapping/FieldEntry.java | 88 ++ src/cuchaz/enigma/mapping/FieldMapping.java | 43 + .../enigma/mapping/IllegalNameException.java | 44 + src/cuchaz/enigma/mapping/JavassistUtil.java | 83 ++ .../enigma/mapping/MappingParseException.java | 29 + src/cuchaz/enigma/mapping/Mappings.java | 188 ++++ src/cuchaz/enigma/mapping/MappingsReader.java | 175 +++ src/cuchaz/enigma/mapping/MappingsRenamer.java | 237 ++++ src/cuchaz/enigma/mapping/MappingsWriter.java | 88 ++ src/cuchaz/enigma/mapping/MethodEntry.java | 104 ++ src/cuchaz/enigma/mapping/MethodMapping.java | 161 +++ src/cuchaz/enigma/mapping/NameValidator.java | 80 ++ src/cuchaz/enigma/mapping/Signature.java | 109 ++ src/cuchaz/enigma/mapping/SignatureUpdater.java | 94 ++ .../enigma/mapping/TranslationDirection.java | 29 + src/cuchaz/enigma/mapping/Translator.java | 239 ++++ src/cuchaz/enigma/mapping/Type.java | 218 ++++ test/cuchaz/enigma/EntryFactory.java | 67 ++ test/cuchaz/enigma/TestDeobfuscator.java | 58 + test/cuchaz/enigma/TestInnerClasses.java | 90 ++ .../enigma/TestJarIndexConstructorReferences.java | 124 +++ .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 228 ++++ test/cuchaz/enigma/TestJarIndexLoneClass.java | 165 +++ test/cuchaz/enigma/TestSignature.java | 266 +++++ test/cuchaz/enigma/TestSourceIndex.java | 50 + test/cuchaz/enigma/TestTokensConstructors.java | 136 +++ test/cuchaz/enigma/TestTranslator.java | 39 + test/cuchaz/enigma/TestType.java | 229 ++++ test/cuchaz/enigma/TokenChecker.java | 65 ++ test/cuchaz/enigma/inputs/Keep.java | 7 + .../enigma/inputs/constructors/BaseClass.java | 15 + test/cuchaz/enigma/inputs/constructors/Caller.java | 47 + .../inputs/constructors/DefaultConstructable.java | 5 + .../enigma/inputs/constructors/SubClass.java | 28 + .../enigma/inputs/constructors/SubSubClass.java | 11 + .../enigma/inputs/inheritanceTree/BaseClass.java | 21 + .../enigma/inputs/inheritanceTree/SubclassA.java | 11 + .../enigma/inputs/inheritanceTree/SubclassB.java | 30 + .../inputs/inheritanceTree/SubsubclassAA.java | 24 + .../enigma/inputs/innerClasses/A_Anonymous.java | 14 + .../innerClasses/B_AnonymousWithScopeArgs.java | 13 + .../inputs/innerClasses/C_ConstructorArgs.java | 20 + .../enigma/inputs/innerClasses/D_Simple.java | 8 + .../innerClasses/E_AnonymousWithOuterAccess.java | 21 + test/cuchaz/enigma/inputs/loneClass/LoneClass.java | 14 + test/cuchaz/enigma/inputs/translation/A.java | 8 + test/cuchaz/enigma/resources/translation.mappings | 4 + 132 files changed, 15543 insertions(+) create mode 100644 .classpath create mode 100644 .hgignore create mode 100644 .project create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 build.py create mode 100644 conf/about.html create mode 100644 license.APL2.txt create mode 100644 license.GPL3.txt create mode 100644 proguard.conf create mode 100644 readme.txt create mode 100644 src/cuchaz/enigma/CommandMain.java create mode 100644 src/cuchaz/enigma/Constants.java create mode 100644 src/cuchaz/enigma/Deobfuscator.java create mode 100644 src/cuchaz/enigma/Main.java create mode 100644 src/cuchaz/enigma/TranslatingTypeLoader.java create mode 100644 src/cuchaz/enigma/Util.java create mode 100644 src/cuchaz/enigma/analysis/Access.java create mode 100644 src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/EntryReference.java create mode 100644 src/cuchaz/enigma/analysis/EntryRenamer.java create mode 100644 src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/JarClassIterator.java create mode 100644 src/cuchaz/enigma/analysis/JarIndex.java create mode 100644 src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/ReferenceTreeNode.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndex.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java create mode 100644 src/cuchaz/enigma/analysis/SourceIndexVisitor.java create mode 100644 src/cuchaz/enigma/analysis/Token.java create mode 100644 src/cuchaz/enigma/analysis/TranslationIndex.java create mode 100644 src/cuchaz/enigma/analysis/TreeDumpVisitor.java create mode 100644 src/cuchaz/enigma/bytecode/CheckCastIterator.java create mode 100644 src/cuchaz/enigma/bytecode/ClassRenamer.java create mode 100644 src/cuchaz/enigma/bytecode/ClassTranslator.java create mode 100644 src/cuchaz/enigma/bytecode/ConstPoolEditor.java create mode 100644 src/cuchaz/enigma/bytecode/InfoType.java create mode 100644 src/cuchaz/enigma/bytecode/InnerClassWriter.java create mode 100644 src/cuchaz/enigma/bytecode/MethodParameterWriter.java create mode 100644 src/cuchaz/enigma/bytecode/MethodParametersAttribute.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java create mode 100644 src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java create mode 100644 src/cuchaz/enigma/convert/ClassIdentity.java create mode 100644 src/cuchaz/enigma/convert/ClassMatcher.java create mode 100644 src/cuchaz/enigma/convert/ClassMatching.java create mode 100644 src/cuchaz/enigma/convert/ClassNamer.java create mode 100644 src/cuchaz/enigma/gui/AboutDialog.java create mode 100644 src/cuchaz/enigma/gui/BoxHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/BrowserCaret.java create mode 100644 src/cuchaz/enigma/gui/ClassListCellRenderer.java create mode 100644 src/cuchaz/enigma/gui/ClassSelector.java create mode 100644 src/cuchaz/enigma/gui/ClassSelectorClassNode.java create mode 100644 src/cuchaz/enigma/gui/ClassSelectorPackageNode.java create mode 100644 src/cuchaz/enigma/gui/CrashDialog.java create mode 100644 src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/Gui.java create mode 100644 src/cuchaz/enigma/gui/GuiController.java create mode 100644 src/cuchaz/enigma/gui/GuiTricks.java create mode 100644 src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/OtherHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/ProgressDialog.java create mode 100644 src/cuchaz/enigma/gui/ReadableToken.java create mode 100644 src/cuchaz/enigma/gui/RenameListener.java create mode 100644 src/cuchaz/enigma/gui/SelectionHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/TokenListCellRenderer.java create mode 100644 src/cuchaz/enigma/mapping/ArgumentEntry.java create mode 100644 src/cuchaz/enigma/mapping/ArgumentMapping.java create mode 100644 src/cuchaz/enigma/mapping/BehaviorEntry.java create mode 100644 src/cuchaz/enigma/mapping/BehaviorEntryFactory.java create mode 100644 src/cuchaz/enigma/mapping/ClassEntry.java create mode 100644 src/cuchaz/enigma/mapping/ClassMapping.java create mode 100644 src/cuchaz/enigma/mapping/ClassNameReplacer.java create mode 100644 src/cuchaz/enigma/mapping/ConstructorEntry.java create mode 100644 src/cuchaz/enigma/mapping/Entry.java create mode 100644 src/cuchaz/enigma/mapping/EntryPair.java create mode 100644 src/cuchaz/enigma/mapping/FieldEntry.java create mode 100644 src/cuchaz/enigma/mapping/FieldMapping.java create mode 100644 src/cuchaz/enigma/mapping/IllegalNameException.java create mode 100644 src/cuchaz/enigma/mapping/JavassistUtil.java create mode 100644 src/cuchaz/enigma/mapping/MappingParseException.java create mode 100644 src/cuchaz/enigma/mapping/Mappings.java create mode 100644 src/cuchaz/enigma/mapping/MappingsReader.java create mode 100644 src/cuchaz/enigma/mapping/MappingsRenamer.java create mode 100644 src/cuchaz/enigma/mapping/MappingsWriter.java create mode 100644 src/cuchaz/enigma/mapping/MethodEntry.java create mode 100644 src/cuchaz/enigma/mapping/MethodMapping.java create mode 100644 src/cuchaz/enigma/mapping/NameValidator.java create mode 100644 src/cuchaz/enigma/mapping/Signature.java create mode 100644 src/cuchaz/enigma/mapping/SignatureUpdater.java create mode 100644 src/cuchaz/enigma/mapping/TranslationDirection.java create mode 100644 src/cuchaz/enigma/mapping/Translator.java create mode 100644 src/cuchaz/enigma/mapping/Type.java create mode 100644 test/cuchaz/enigma/EntryFactory.java create mode 100644 test/cuchaz/enigma/TestDeobfuscator.java create mode 100644 test/cuchaz/enigma/TestInnerClasses.java create mode 100644 test/cuchaz/enigma/TestJarIndexConstructorReferences.java create mode 100644 test/cuchaz/enigma/TestJarIndexInheritanceTree.java create mode 100644 test/cuchaz/enigma/TestJarIndexLoneClass.java create mode 100644 test/cuchaz/enigma/TestSignature.java create mode 100644 test/cuchaz/enigma/TestSourceIndex.java create mode 100644 test/cuchaz/enigma/TestTokensConstructors.java create mode 100644 test/cuchaz/enigma/TestTranslator.java create mode 100644 test/cuchaz/enigma/TestType.java create mode 100644 test/cuchaz/enigma/TokenChecker.java create mode 100644 test/cuchaz/enigma/inputs/Keep.java create mode 100644 test/cuchaz/enigma/inputs/constructors/BaseClass.java create mode 100644 test/cuchaz/enigma/inputs/constructors/Caller.java create mode 100644 test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java create mode 100644 test/cuchaz/enigma/inputs/constructors/SubClass.java create mode 100644 test/cuchaz/enigma/inputs/constructors/SubSubClass.java create mode 100644 test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java create mode 100644 test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java create mode 100644 test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java create mode 100644 test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/D_Simple.java create mode 100644 test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java create mode 100644 test/cuchaz/enigma/inputs/loneClass/LoneClass.java create mode 100644 test/cuchaz/enigma/inputs/translation/A.java create mode 100644 test/cuchaz/enigma/resources/translation.mappings diff --git a/.classpath b/.classpath new file mode 100644 index 00000000..16e0b310 --- /dev/null +++ b/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..659df811 --- /dev/null +++ b/.hgignore @@ -0,0 +1,9 @@ + +syntax: glob +bin +lib +build +data +input +ivy +*.pyc \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 00000000..08dff6c0 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + Enigma + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..b5d234f2 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,13 @@ +# +#Mon Sep 22 22:51:23 EDT 2014 +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error diff --git a/build.py b/build.py new file mode 100644 index 00000000..47294987 --- /dev/null +++ b/build.py @@ -0,0 +1,120 @@ + +import os +import sys + +# settings +PathSsjb = "../ssjb" +Author = "Cuchaz" + +DirBin = "bin" +DirLib = "lib" +DirBuild = "build" +PathLocalMavenRepo = "../maven" + + +# import ssjb +sys.path.insert(0, PathSsjb) +import ssjb +import ssjb.ivy + + +ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.6b") +ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.6b") + +# dependencies +ExtraRepos = [ + "http://maven.cuchazinteractive.com" +] +LibDeps = [ + ssjb.ivy.Dep("com.google.guava:guava:17.0"), + ssjb.ivy.Dep("org.javassist:javassist:3.18.1-GA"), + ssjb.ivy.Dep("org.bitbucket.mstrobel:procyon-decompiler:0.5.28-enigma") +] +StandaloneDeps = LibDeps + [ + ssjb.ivy.Dep("de.sciss:jsyntaxpane:1.0.0") +] +ProguardDep = ssjb.ivy.Dep("net.sf.proguard:proguard-base:5.1") +TestDeps = [ + ssjb.ivy.Dep("junit:junit:4.12"), + ssjb.ivy.Dep("org.hamcrest:hamcrest-all:1.3") +] + +# functions + +def buildTestJar(name, glob): + + pathJar = os.path.join(DirBuild, "%s.jar" % name) + pathObfJar = os.path.join(DirBuild, "%s.obf.jar" % name) + + # build the deobf jar + with ssjb.file.TempDir("tmp") as dirTemp: + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin, "cuchaz/enigma/inputs/Keep.class")) + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin, glob)) + ssjb.jar.makeJar(pathJar, dirTemp) + + # build the obf jar + ssjb.callJavaJar( + os.path.join(DirLib, "proguard.jar"), + ["@proguard.conf", "-injars", pathJar, "-outjars", pathObfJar] + ) + + +def applyReadme(dirTemp): + ssjb.file.copy(dirTemp, "license.APL2.txt") + ssjb.file.copy(dirTemp, "license.GPL3.txt") + ssjb.file.copy(dirTemp, "readme.txt") + + +def buildStandaloneJar(dirOut): + with ssjb.file.TempDir(os.path.join(dirOut, "tmp")) as dirTemp: + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin)) + for path in ssjb.ivy.getJarPaths(StandaloneDeps, ExtraRepos): + ssjb.jar.unpackJar(dirTemp, path) + ssjb.file.delete(os.path.join(dirTemp, "LICENSE.txt")) + ssjb.file.delete(os.path.join(dirTemp, "META-INF/maven")) + applyReadme(dirTemp) + manifest = ssjb.jar.buildManifest( + ArtifactStandalone.artifactId, + ArtifactStandalone.version, + Author, + "cuchaz.enigma.Main" + ) + pathJar = os.path.join(DirBuild, "%s.jar" % ArtifactStandalone.getName()) + ssjb.jar.makeJar(pathJar, dirTemp, manifest=manifest) + ssjb.ivy.deployJarToLocalMavenRepo(PathLocalMavenRepo, pathJar, ArtifactStandalone) + +def buildLibJar(dirOut): + with ssjb.file.TempDir(os.path.join(dirOut, "tmp")) as dirTemp: + ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin)) + applyReadme(dirTemp) + pathJar = os.path.join(DirBuild, "%s.jar" % ArtifactLib.getName()) + ssjb.jar.makeJar(pathJar, dirTemp) + ssjb.ivy.deployJarToLocalMavenRepo(PathLocalMavenRepo, pathJar, ArtifactLib, deps=LibDeps) + + +# tasks + +def taskGetDeps(): + ssjb.file.mkdir(DirLib) + ssjb.ivy.makeLibsJar(os.path.join(DirLib, "deps.jar"), StandaloneDeps, extraRepos=ExtraRepos) + ssjb.ivy.makeLibsJar(os.path.join(DirLib, "test-deps.jar"), TestDeps) + ssjb.ivy.makeJar(os.path.join(DirLib, "proguard.jar"), ProguardDep) + +def taskBuildTestJars(): + buildTestJar("testLoneClass", "cuchaz/enigma/inputs/loneClass/*.class") + buildTestJar("testConstructors", "cuchaz/enigma/inputs/constructors/*.class") + buildTestJar("testInheritanceTree", "cuchaz/enigma/inputs/inheritanceTree/*.class") + buildTestJar("testInnerClasses", "cuchaz/enigma/inputs/innerClasses/*.class") + buildTestJar("testTranslation", "cuchaz/enigma/inputs/translation/*.class") + +def taskBuild(): + ssjb.file.delete(DirBuild) + ssjb.file.mkdir(DirBuild) + buildStandaloneJar(DirBuild) + buildLibJar(DirBuild) + +ssjb.registerTask("getDeps", taskGetDeps) +ssjb.registerTask("buildTestJars", taskBuildTestJars) +ssjb.registerTask("build", taskBuild) +ssjb.run() + diff --git a/conf/about.html b/conf/about.html new file mode 100644 index 00000000..b75c1bf0 --- /dev/null +++ b/conf/about.html @@ -0,0 +1,6 @@ + +

%s

+

A tool for debofuscation of Java code

+

+

Version: %s

+ \ No newline at end of file diff --git a/license.APL2.txt b/license.APL2.txt new file mode 100644 index 00000000..a453e432 --- /dev/null +++ b/license.APL2.txt @@ -0,0 +1,55 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + + +You must cause any modified files to carry prominent notices stating that You changed the files; and + + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/license.GPL3.txt b/license.GPL3.txt new file mode 100644 index 00000000..20d40b6b --- /dev/null +++ b/license.GPL3.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/proguard.conf b/proguard.conf new file mode 100644 index 00000000..e1f04aee --- /dev/null +++ b/proguard.conf @@ -0,0 +1,7 @@ +-libraryjars /lib/rt.jar +-overloadaggressively +-repackageclasses +-allowaccessmodification +-dontoptimize +-dontshrink +-keep class cuchaz.enigma.inputs.Keep \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 00000000..3844f54e --- /dev/null +++ b/readme.txt @@ -0,0 +1,28 @@ + +Enigma v0.6 beta +A tool for deobfuscation of Java bytecode + +Copyright Jeff Martin, 2014 + + +LICENSE + +Enigma is distributed under the GNU General Public license version 3 + +Enigma includes a modified version of Procyon which is distributed under the Apache license version 2. Procyon is copyrighted by Mike Strobel, 2013 + +Enigma includes unmodified versions of the following libraries which are also released under the Apache license version 2. + Guava + Javassist + JSyntaxPane + +Copies of the GNU General Public license verion 3 and the Apache license v2 have been included in this distribution. + + +USING ENIGMA + +Launch the GUI: + java -jar enigma.jar + +Use Enigma on the command line: + java -cp enigma.jar cuchaz.enigma.CommandMain diff --git a/src/cuchaz/enigma/CommandMain.java b/src/cuchaz/enigma/CommandMain.java new file mode 100644 index 00000000..1ec2ad23 --- /dev/null +++ b/src/cuchaz/enigma/CommandMain.java @@ -0,0 +1,136 @@ +package cuchaz.enigma; + +import java.io.File; +import java.io.FileReader; +import java.util.jar.JarFile; + +import cuchaz.enigma.Deobfuscator.ProgressListener; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsReader; + +public class CommandMain { + + public static class ConsoleProgressListener implements ProgressListener { + + private static final int ReportTime = 5000; // 5s + + private int m_totalWork; + private long m_startTime; + private long m_lastReportTime; + + @Override + public void init(int totalWork, String title) { + m_totalWork = totalWork; + m_startTime = System.currentTimeMillis(); + m_lastReportTime = m_startTime; + System.out.println(title); + } + + @Override + public void onProgress(int numDone, String message) { + + long now = System.currentTimeMillis(); + boolean isLastUpdate = numDone == m_totalWork; + boolean shouldReport = isLastUpdate || now - m_lastReportTime > ReportTime; + + if (shouldReport) { + int percent = numDone*100/m_totalWork; + System.out.println(String.format("\tProgress: %3d%%", percent)); + m_lastReportTime = now; + } + if (isLastUpdate) { + double elapsedSeconds = (now - m_startTime)/1000; + System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds)); + } + } + } + + public static void main(String[] args) + throws Exception { + + try { + + // process the command + String command = getArg(args, 0, "command"); + if (command.equalsIgnoreCase("deobfuscate")) { + deobfuscate(args); + } else if(command.equalsIgnoreCase("decompile")) { + decompile(args); + } else { + throw new IllegalArgumentException("Command not recognized: " + command); + } + } catch (IllegalArgumentException ex) { + System.out.println(ex.getMessage()); + printHelp(); + } + } + + private static void printHelp() { + System.out.println(String.format("%s - %s", Constants.Name, Constants.Version)); + System.out.println("Usage:"); + System.out.println("\tjava -cp enigma.jar cuchaz.enigma.CommandMain "); + System.out.println("\twhere is one of:"); + System.out.println("\t\tdeobfuscate "); + System.out.println("\t\tdecompile "); + } + + private static void decompile(String[] args) + throws Exception { + File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); + File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); + File fileJarOut = getWritableFolder(getArg(args, 3, "out folder")); + Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); + deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); + } + + private static void deobfuscate(String[] args) + throws Exception { + File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); + File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); + File fileJarOut = getWritableFile(getArg(args, 3, "out jar")); + Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); + deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); + } + + private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) + throws Exception { + System.out.println("Reading mappings..."); + Mappings mappings = new MappingsReader().read(new FileReader(fileMappings)); + System.out.println("Reading jar..."); + Deobfuscator deobfuscator = new Deobfuscator(jar); + deobfuscator.setMappings(mappings); + return deobfuscator; + } + + private static String getArg(String[] args, int i, String name) { + if (i >= args.length) { + throw new IllegalArgumentException(name + " is required"); + } + return args[i]; + } + + private static File getWritableFile(String path) { + File file = new File(path).getAbsoluteFile(); + File dir = file.getParentFile(); + if (dir == null || !dir.exists()) { + throw new IllegalArgumentException("Cannot write to folder: " + file); + } + return file; + } + + private static File getWritableFolder(String path) { + File dir = new File(path).getAbsoluteFile(); + if (!dir.exists()) { + throw new IllegalArgumentException("Cannot write to folder: " + dir); + } + return dir; + } + + private static File getReadableFile(String path) { + File file = new File(path).getAbsoluteFile(); + if (!file.exists()) { + throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); + } + return file; + } +} diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java new file mode 100644 index 00000000..a1ba2e98 --- /dev/null +++ b/src/cuchaz/enigma/Constants.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +public class Constants { + public static final String Name = "Enigma"; + public static final String Version = "0.6 beta"; + public static final String Url = "http://www.cuchazinteractive.com/enigma"; + public static final int MiB = 1024 * 1024; // 1 mebibyte + public static final int KiB = 1024; // 1 kebibyte + public static final String NonePackage = "none"; +} diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java new file mode 100644 index 00000000..5f61686b --- /dev/null +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -0,0 +1,539 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; + +import javassist.CtClass; +import javassist.bytecode.Descriptor; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.strobel.assembler.metadata.MetadataSystem; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.decompiler.DecompilerContext; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import com.strobel.decompiler.languages.java.JavaOutputVisitor; +import com.strobel.decompiler.languages.java.ast.AstBuilder; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; +import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarClassIterator; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.SourceIndexVisitor; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.FieldMapping; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsRenamer; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.MethodMapping; +import cuchaz.enigma.mapping.TranslationDirection; +import cuchaz.enigma.mapping.Translator; + +public class Deobfuscator { + + public interface ProgressListener { + void init(int totalWork, String title); + void onProgress(int numDone, String message); + } + + private JarFile m_jar; + private DecompilerSettings m_settings; + private JarIndex m_jarIndex; + private Mappings m_mappings; + private MappingsRenamer m_renamer; + private Map m_translatorCache; + + public Deobfuscator(JarFile jar) throws IOException { + m_jar = jar; + + // build the jar index + m_jarIndex = new JarIndex(); + m_jarIndex.indexJar(m_jar, true); + + // config the decompiler + m_settings = DecompilerSettings.javaDefaults(); + m_settings.setMergeVariables(true); + m_settings.setForceExplicitImports(true); + m_settings.setForceExplicitTypeArguments(true); + // DEBUG + //m_settings.setShowSyntheticMembers(true); + + // init defaults + m_translatorCache = Maps.newTreeMap(); + + // init mappings + setMappings(new Mappings()); + } + + public String getJarName() { + return m_jar.getName(); + } + + public JarIndex getJarIndex() { + return m_jarIndex; + } + + public Mappings getMappings() { + return m_mappings; + } + + public void setMappings(Mappings val) { + if (val == null) { + val = new Mappings(); + } + + // pass 1: look for any classes that got moved to inner classes + Map renames = Maps.newHashMap(); + for (ClassMapping classMapping : val.classes()) { + // make sure we strip the packages off of obfuscated inner classes + String innerClassName = new ClassEntry(classMapping.getObfName()).getSimpleName(); + String outerClassName = m_jarIndex.getOuterClass(innerClassName); + if (outerClassName != null) { + // build the composite class name + String newName = outerClassName + "$" + innerClassName; + + // add a rename + renames.put(classMapping.getObfName(), newName); + + System.out.println(String.format("Converted class mapping %s to %s", classMapping.getObfName(), newName)); + } + } + for (Map.Entry entry : renames.entrySet()) { + val.renameObfClass(entry.getKey(), entry.getValue()); + } + + // pass 2: look for fields/methods that are actually declared in superclasses + MappingsRenamer renamer = new MappingsRenamer(m_jarIndex, val); + for (ClassMapping classMapping : Lists.newArrayList(val.classes())) { + ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfName()); + + // fields + for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { + FieldEntry fieldEntry = new FieldEntry(obfClassEntry, fieldMapping.getObfName()); + ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(fieldEntry); + if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(fieldEntry.getClassEntry())) { + boolean wasMoved = renamer.moveFieldToObfClass(classMapping, fieldMapping, resolvedObfClassEntry); + if (wasMoved) { + System.out.println(String.format("Moved field %s to class %s", fieldEntry, resolvedObfClassEntry)); + } else { + System.err.println(String.format("WARNING: Would move field %s to class %s but the field was already there. Dropping instead.", fieldEntry, resolvedObfClassEntry)); + } + } + } + + // methods + for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { + // skip constructors + if (methodMapping.isConstructor()) { + continue; + } + + MethodEntry methodEntry = new MethodEntry( + obfClassEntry, + methodMapping.getObfName(), + methodMapping.getObfSignature() + ); + ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(methodEntry); + if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(methodEntry.getClassEntry())) { + boolean wasMoved = renamer.moveMethodToObfClass(classMapping, methodMapping, resolvedObfClassEntry); + if (wasMoved) { + System.out.println(String.format("Moved method %s to class %s", methodEntry, resolvedObfClassEntry)); + } else { + System.err.println(String.format("WARNING: Would move method %s to class %s but the method was already there. Dropping instead.", methodEntry, resolvedObfClassEntry)); + } + } + } + + // TODO: recurse to inner classes? + } + + // drop mappings that don't match the jar + List unknownClasses = Lists.newArrayList(); + for (ClassMapping classMapping : val.classes()) { + checkClassMapping(unknownClasses, classMapping); + } + if (!unknownClasses.isEmpty()) { + throw new Error("Unable to find classes in jar: " + unknownClasses); + } + + m_mappings = val; + m_renamer = renamer; + m_translatorCache.clear(); + } + + private void checkClassMapping(List unknownClasses, ClassMapping classMapping) { + // check the class + ClassEntry classEntry = new ClassEntry(classMapping.getObfName()); + String outerClassName = m_jarIndex.getOuterClass(classEntry.getSimpleName()); + if (outerClassName != null) { + classEntry = new ClassEntry(outerClassName + "$" + classMapping.getObfName()); + } + if (!m_jarIndex.getObfClassEntries().contains(classEntry)) { + unknownClasses.add(classEntry); + } + + // check the fields + for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { + FieldEntry fieldEntry = new FieldEntry(classEntry, fieldMapping.getObfName()); + if (!m_jarIndex.containsObfField(fieldEntry)) { + System.err.println("WARNING: unable to find field " + fieldEntry + ". dropping mapping."); + classMapping.removeFieldMapping(fieldMapping); + } + } + + // check methods + for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { + BehaviorEntry obfBehaviorEntry = BehaviorEntryFactory.createObf(classEntry, methodMapping); + if (!m_jarIndex.containsObfBehavior(obfBehaviorEntry)) { + System.err.println("WARNING: unable to find behavior " + obfBehaviorEntry + ". dropping mapping."); + classMapping.removeMethodMapping(methodMapping); + } + } + + // check inner classes + for (ClassMapping innerClassMapping : classMapping.innerClasses()) { + checkClassMapping(unknownClasses, innerClassMapping); + } + } + + public Translator getTranslator(TranslationDirection direction) { + Translator translator = m_translatorCache.get(direction); + if (translator == null) { + translator = m_mappings.getTranslator(direction, m_jarIndex.getTranslationIndex()); + m_translatorCache.put(direction, translator); + } + return translator; + } + + public void getSeparatedClasses(List obfClasses, List deobfClasses) { + for (ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries()) { + // skip inner classes + if (obfClassEntry.isInnerClass()) { + continue; + } + + // separate the classes + ClassEntry deobfClassEntry = deobfuscateEntry(obfClassEntry); + if (!deobfClassEntry.equals(obfClassEntry)) { + // if the class has a mapping, clearly it's deobfuscated + deobfClasses.add(deobfClassEntry); + } else if (!obfClassEntry.getPackageName().equals(Constants.NonePackage)) { + // also call it deobufscated if it's not in the none package + deobfClasses.add(obfClassEntry); + } else { + // otherwise, assume it's still obfuscated + obfClasses.add(obfClassEntry); + } + } + } + + public CompilationUnit getSourceTree(String obfClassName) { + // is this class deobfuscated? + // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out + // the decompiler only sees the deobfuscated class, so we need to load it by the deobfuscated name + String lookupClassName = obfClassName; + ClassMapping classMapping = m_mappings.getClassByObf(obfClassName); + if (classMapping != null && classMapping.getDeobfName() != null) { + lookupClassName = classMapping.getDeobfName(); + } + + // is this class even in the jar? + if (!m_jarIndex.containsObfClass(new ClassEntry(obfClassName))) { + return null; + } + + // set the type loader + m_settings.setTypeLoader(new TranslatingTypeLoader( + m_jar, + m_jarIndex, + getTranslator(TranslationDirection.Obfuscating), + getTranslator(TranslationDirection.Deobfuscating) + )); + + // decompile it! + TypeDefinition resolvedType = new MetadataSystem(m_settings.getTypeLoader()).lookupType(lookupClassName).resolve(); + DecompilerContext context = new DecompilerContext(); + context.setCurrentType(resolvedType); + context.setSettings(m_settings); + AstBuilder builder = new AstBuilder(context); + builder.addType(resolvedType); + builder.runTransformations(null); + return builder.getCompilationUnit(); + } + + public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { + // build the source index + SourceIndex index = new SourceIndex(source); + sourceTree.acceptVisitor(new SourceIndexVisitor(), index); + + // DEBUG + // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); + + // resolve all the classes in the source references + for (Token token : index.referenceTokens()) { + EntryReference deobfReference = index.getDeobfReference(token); + + // get the obfuscated entry + Entry obfEntry = obfuscateEntry(deobfReference.entry); + + // try to resolve the class + ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(obfEntry); + if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { + // change the class of the entry + obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); + + // save the new deobfuscated reference + deobfReference.entry = deobfuscateEntry(obfEntry); + index.replaceDeobfReference(token, deobfReference); + } + + // DEBUG + // System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); + } + + return index; + } + + public String getSource(CompilationUnit sourceTree) { + // render the AST into source + StringWriter buf = new StringWriter(); + sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); + sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), m_settings), null); + return buf.toString(); + } + + public void writeSources(File dirOut, ProgressListener progress) throws IOException { + // get the classes to decompile + Set classEntries = Sets.newHashSet(); + for (ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries()) { + // skip inner classes + if (obfClassEntry.isInnerClass()) { + continue; + } + + classEntries.add(obfClassEntry); + } + + if (progress != null) { + progress.init(classEntries.size(), "Decompiling classes..."); + } + + // DEOBFUSCATE ALL THE THINGS!! @_@ + int i = 0; + for (ClassEntry obfClassEntry : classEntries) { + ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry)); + if (progress != null) { + progress.onProgress(i++, deobfClassEntry.toString()); + } + + try { + // get the source + String source = getSource(getSourceTree(obfClassEntry.getName())); + + // write the file + File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); + file.getParentFile().mkdirs(); + try (FileWriter out = new FileWriter(file)) { + out.write(source); + } + } catch (Throwable t) { + throw new Error("Unable to deobfuscate class " + deobfClassEntry.toString() + " (" + obfClassEntry.toString() + ")", t); + } + } + if (progress != null) { + progress.onProgress(i, "Done!"); + } + } + + public void writeJar(File out, ProgressListener progress) { + try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { + if (progress != null) { + progress.init(JarClassIterator.getClassEntries(m_jar).size(), "Translating classes..."); + } + + // prep the loader + TranslatingTypeLoader loader = new TranslatingTypeLoader( + m_jar, + m_jarIndex, + getTranslator(TranslationDirection.Obfuscating), + getTranslator(TranslationDirection.Deobfuscating) + ); + + int i = 0; + for (CtClass c : JarClassIterator.classes(m_jar)) { + if (progress != null) { + progress.onProgress(i++, c.getName()); + } + + try { + c = loader.transformClass(c); + outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); + outJar.write(c.toBytecode()); + outJar.closeEntry(); + } catch (Throwable t) { + throw new Error("Unable to deobfuscate class " + c.getName(), t); + } + } + if (progress != null) { + progress.onProgress(i, "Done!"); + } + + outJar.close(); + } catch (IOException ex) { + throw new Error("Unable to write to Jar file!"); + } + } + + public T obfuscateEntry(T deobfEntry) { + if (deobfEntry == null) { + return null; + } + return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry); + } + + public T deobfuscateEntry(T obfEntry) { + if (obfEntry == null) { + return null; + } + return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry); + } + + public EntryReference obfuscateReference(EntryReference deobfReference) { + if (deobfReference == null) { + return null; + } + return new EntryReference( + obfuscateEntry(deobfReference.entry), + obfuscateEntry(deobfReference.context), + deobfReference + ); + } + + public EntryReference deobfuscateReference(EntryReference obfReference) { + if (obfReference == null) { + return null; + } + return new EntryReference( + deobfuscateEntry(obfReference.entry), + deobfuscateEntry(obfReference.context), + obfReference + ); + } + + public boolean isObfuscatedIdentifier(Entry obfEntry) { + return m_jarIndex.containsObfEntry(obfEntry); + } + + public boolean isRenameable(EntryReference obfReference) { + return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry()); + } + + // NOTE: these methods are a bit messy... oh well + + public boolean hasDeobfuscatedName(Entry obfEntry) { + Translator translator = getTranslator(TranslationDirection.Deobfuscating); + if (obfEntry instanceof ClassEntry) { + return translator.translate((ClassEntry)obfEntry) != null; + } else if (obfEntry instanceof FieldEntry) { + return translator.translate((FieldEntry)obfEntry) != null; + } else if (obfEntry instanceof MethodEntry) { + return translator.translate((MethodEntry)obfEntry) != null; + } else if (obfEntry instanceof ConstructorEntry) { + // constructors have no names + return false; + } else if (obfEntry instanceof ArgumentEntry) { + return translator.translate((ArgumentEntry)obfEntry) != null; + } else { + throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); + } + } + + public void rename(Entry obfEntry, String newName) { + if (obfEntry instanceof ClassEntry) { + m_renamer.setClassName((ClassEntry)obfEntry, Descriptor.toJvmName(newName)); + } else if (obfEntry instanceof FieldEntry) { + m_renamer.setFieldName((FieldEntry)obfEntry, newName); + } else if (obfEntry instanceof MethodEntry) { + m_renamer.setMethodTreeName((MethodEntry)obfEntry, newName); + } else if (obfEntry instanceof ConstructorEntry) { + throw new IllegalArgumentException("Cannot rename constructors"); + } else if (obfEntry instanceof ArgumentEntry) { + m_renamer.setArgumentName((ArgumentEntry)obfEntry, newName); + } else { + throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); + } + + // clear caches + m_translatorCache.clear(); + } + + public void removeMapping(Entry obfEntry) { + if (obfEntry instanceof ClassEntry) { + m_renamer.removeClassMapping((ClassEntry)obfEntry); + } else if (obfEntry instanceof FieldEntry) { + m_renamer.removeFieldMapping((FieldEntry)obfEntry); + } else if (obfEntry instanceof MethodEntry) { + m_renamer.removeMethodTreeMapping((MethodEntry)obfEntry); + } else if (obfEntry instanceof ConstructorEntry) { + throw new IllegalArgumentException("Cannot rename constructors"); + } else if (obfEntry instanceof ArgumentEntry) { + m_renamer.removeArgumentMapping((ArgumentEntry)obfEntry); + } else { + throw new Error("Unknown entry type: " + obfEntry); + } + + // clear caches + m_translatorCache.clear(); + } + + public void markAsDeobfuscated(Entry obfEntry) { + if (obfEntry instanceof ClassEntry) { + m_renamer.markClassAsDeobfuscated((ClassEntry)obfEntry); + } else if (obfEntry instanceof FieldEntry) { + m_renamer.markFieldAsDeobfuscated((FieldEntry)obfEntry); + } else if (obfEntry instanceof MethodEntry) { + m_renamer.markMethodTreeAsDeobfuscated((MethodEntry)obfEntry); + } else if (obfEntry instanceof ConstructorEntry) { + throw new IllegalArgumentException("Cannot rename constructors"); + } else if (obfEntry instanceof ArgumentEntry) { + m_renamer.markArgumentAsDeobfuscated((ArgumentEntry)obfEntry); + } else { + throw new Error("Unknown entry type: " + obfEntry); + } + + // clear caches + m_translatorCache.clear(); + } +} diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java new file mode 100644 index 00000000..acae94b1 --- /dev/null +++ b/src/cuchaz/enigma/Main.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.File; +import java.util.jar.JarFile; + +import cuchaz.enigma.gui.Gui; + +public class Main { + + public static void main(String[] args) throws Exception { + Gui gui = new Gui(); + + // parse command-line args + if (args.length >= 1) { + gui.getController().openJar(new JarFile(getFile(args[0]))); + } + if (args.length >= 2) { + gui.getController().openMappings(getFile(args[1])); + } + + // DEBUG + //gui.getController().openDeclaration(new ClassEntry("none/bxq")); + } + + private static File getFile(String path) { + // expand ~ to the home dir + if (path.startsWith("~")) { + // get the home dir + File dirHome = new File(System.getProperty("user.home")); + + // is the path just ~/ or is it ~user/ ? + if (path.startsWith("~/")) { + return new File(dirHome, path.substring(2)); + } else { + return new File(dirHome.getParentFile(), path.substring(1)); + } + } + + return new File(path); + } +} diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java new file mode 100644 index 00000000..cfa03a1a --- /dev/null +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import javassist.ByteArrayClassPath; +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.Descriptor; + +import com.google.common.collect.Maps; +import com.strobel.assembler.metadata.Buffer; +import com.strobel.assembler.metadata.ClasspathTypeLoader; +import com.strobel.assembler.metadata.ITypeLoader; + +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.bytecode.ClassRenamer; +import cuchaz.enigma.bytecode.ClassTranslator; +import cuchaz.enigma.bytecode.InnerClassWriter; +import cuchaz.enigma.bytecode.MethodParameterWriter; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Translator; + +public class TranslatingTypeLoader implements ITypeLoader { + + private JarFile m_jar; + private JarIndex m_jarIndex; + private Translator m_obfuscatingTranslator; + private Translator m_deobfuscatingTranslator; + private Map m_cache; + private ClasspathTypeLoader m_defaultTypeLoader; + + public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex) { + this(jar, jarIndex, new Translator(), new Translator()); + } + + public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) { + m_jar = jar; + m_jarIndex = jarIndex; + m_obfuscatingTranslator = obfuscatingTranslator; + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_cache = Maps.newHashMap(); + m_defaultTypeLoader = new ClasspathTypeLoader(); + } + + public void clearCache() { + m_cache.clear(); + } + + @Override + public boolean tryLoadType(String deobfClassName, Buffer out) { + // check the cache + byte[] data; + if (m_cache.containsKey(deobfClassName)) { + data = m_cache.get(deobfClassName); + } else { + data = loadType(deobfClassName); + m_cache.put(deobfClassName, data); + } + + if (data == null) { + // chain to default type loader + return m_defaultTypeLoader.tryLoadType(deobfClassName, out); + } + + // send the class to the decompiler + out.reset(data.length); + System.arraycopy(data, 0, out.array(), out.position(), data.length); + out.position(0); + return true; + } + + public CtClass loadClass(String deobfClassName) { + byte[] data = loadType(deobfClassName); + if (data == null) { + return null; + } + + // return a javassist handle for the class + String javaClassFileName = Descriptor.toJavaName(deobfClassName); + ClassPool classPool = new ClassPool(); + classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, data)); + try { + return classPool.get(javaClassFileName); + } catch (NotFoundException ex) { + throw new Error(ex); + } + } + + private byte[] loadType(String deobfClassName) { + ClassEntry deobfClassEntry = new ClassEntry(deobfClassName); + ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(deobfClassEntry); + + // is this an inner class referenced directly? + String obfOuterClassName = m_jarIndex.getOuterClass(obfClassEntry.getSimpleName()); + if (obfOuterClassName != null) { + // this class doesn't really exist. Reference it by outer$inner instead + System.err.println(String.format("WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassName)); + return null; + } + + /* DEBUG + if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) { + System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); + } + */ + + // get the jar entry + String classFileName; + if (obfClassEntry.isInnerClass()) { + // use just the inner class name for inner classes + classFileName = obfClassEntry.getInnerClassName(); + } else if (obfClassEntry.getPackageName().equals(Constants.NonePackage)) { + // use the outer class simple name for classes in the none package + classFileName = obfClassEntry.getSimpleName(); + } else { + // otherwise, just use the class name (ie for classes in packages) + classFileName = obfClassEntry.getName(); + } + + JarEntry entry = m_jar.getJarEntry(classFileName + ".class"); + if (entry == null) { + return null; + } + + try { + // read the class file into a buffer + ByteArrayOutputStream data = new ByteArrayOutputStream(); + byte[] buf = new byte[1024 * 1024]; // 1 KiB + InputStream in = m_jar.getInputStream(entry); + while (true) { + int bytesRead = in.read(buf); + if (bytesRead <= 0) { + break; + } + data.write(buf, 0, bytesRead); + } + data.close(); + in.close(); + buf = data.toByteArray(); + + // load the javassist handle to the raw class + String javaClassFileName = Descriptor.toJavaName(classFileName); + ClassPool classPool = new ClassPool(); + classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, buf)); + CtClass c = classPool.get(javaClassFileName); + + c = transformClass(c); + + // sanity checking + assertClassName(c, deobfClassEntry); + + // DEBUG + //Util.writeClass( c ); + + // we have a transformed class! + return c.toBytecode(); + } catch (IOException | NotFoundException | CannotCompileException ex) { + throw new Error(ex); + } + } + + public CtClass transformClass(CtClass c) throws IOException, NotFoundException, CannotCompileException { + // we moved a lot of classes out of the default package into the none package + // make sure all the class references are consistent + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + + // reconstruct inner classes + new InnerClassWriter(m_jarIndex).write(c); + + // re-get the javassist handle since we changed class names + ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + String javaClassReconstructedName = Descriptor.toJavaName(obfClassEntry.getName()); + ClassPool classPool = new ClassPool(); + classPool.insertClassPath(new ByteArrayClassPath(javaClassReconstructedName, c.toBytecode())); + c = classPool.get(javaClassReconstructedName); + + // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong) + assertClassName(c, obfClassEntry); + + // do all kinds of deobfuscating transformations on the class + new MethodParameterWriter(m_deobfuscatingTranslator).writeMethodArguments(c); + new ClassTranslator(m_deobfuscatingTranslator).translate(c); + + return c; + } + + private void assertClassName(CtClass c, ClassEntry obfClassEntry) { + String name1 = Descriptor.toJvmName(c.getName()); + assert (name1.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name1); + + String name2 = Descriptor.toJvmName(c.getClassFile().getName()); + assert (name2.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name2); + } +} diff --git a/src/cuchaz/enigma/Util.java b/src/cuchaz/enigma/Util.java new file mode 100644 index 00000000..7f04bda0 --- /dev/null +++ b/src/cuchaz/enigma/Util.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.awt.Desktop; +import java.io.Closeable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.jar.JarFile; + +import javassist.CannotCompileException; +import javassist.CtClass; +import javassist.bytecode.Descriptor; + +import com.google.common.io.CharStreams; + +public class Util { + + public static int combineHashesOrdered(Object... objs) { + return combineHashesOrdered(Arrays.asList(objs)); + } + + public static int combineHashesOrdered(Iterable objs) { + final int prime = 67; + int result = 1; + for (Object obj : objs) { + result *= prime; + if (obj != null) { + result += obj.hashCode(); + } + } + return result; + } + + public static void closeQuietly(Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException ex) { + // just ignore any further exceptions + } + } + } + + public static void closeQuietly(JarFile jarFile) { + // silly library should implement Closeable... + if (jarFile != null) { + try { + jarFile.close(); + } catch (IOException ex) { + // just ignore any further exceptions + } + } + } + + public static String readStreamToString(InputStream in) throws IOException { + return CharStreams.toString(new InputStreamReader(in, "UTF-8")); + } + + public static String readResourceToString(String path) throws IOException { + InputStream in = Util.class.getResourceAsStream(path); + if (in == null) { + throw new IllegalArgumentException("Resource not found! " + path); + } + return readStreamToString(in); + } + + public static void openUrl(String url) { + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + try { + desktop.browse(new URI(url)); + } catch (IOException ex) { + throw new Error(ex); + } catch (URISyntaxException ex) { + throw new IllegalArgumentException(ex); + } + } + } + + public static void writeClass(CtClass c) { + String name = Descriptor.toJavaName(c.getName()); + File file = new File(name + ".class"); + try (FileOutputStream out = new FileOutputStream(file)) { + out.write(c.toBytecode()); + } catch (IOException | CannotCompileException ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/analysis/Access.java b/src/cuchaz/enigma/analysis/Access.java new file mode 100644 index 00000000..8d3409ac --- /dev/null +++ b/src/cuchaz/enigma/analysis/Access.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.lang.reflect.Modifier; + +import javassist.CtBehavior; +import javassist.CtField; + +public enum Access { + + Public, + Protected, + Private; + + public static Access get(CtBehavior behavior) { + return get(behavior.getModifiers()); + } + + public static Access get(CtField field) { + return get(field.getModifiers()); + } + + public static Access get(int modifiers) { + if (Modifier.isPublic(modifiers)) { + return Public; + } else if (Modifier.isProtected(modifiers)) { + return Protected; + } else if (Modifier.isPrivate(modifiers)) { + return Private; + } + // assume public by default + return Public; + } +} diff --git a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java new file mode 100644 index 00000000..9adac5e9 --- /dev/null +++ b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.Set; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; + +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.Translator; + +public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { + + private static final long serialVersionUID = -3658163700783307520L; + + private Translator m_deobfuscatingTranslator; + private BehaviorEntry m_entry; + private EntryReference m_reference; + private Access m_access; + + public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + m_reference = null; + } + + public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference reference, Access access) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = reference.entry; + m_reference = reference; + m_access = access; + } + + @Override + public BehaviorEntry getEntry() { + return m_entry; + } + + @Override + public EntryReference getReference() { + return m_reference; + } + + @Override + public String toString() { + if (m_reference != null) { + return String.format("%s (%s)", m_deobfuscatingTranslator.translateEntry(m_reference.context), m_access); + } + return m_deobfuscatingTranslator.translateEntry(m_entry).toString(); + } + + public void load(JarIndex index, boolean recurse) { + // get all the child nodes + for (EntryReference reference : index.getBehaviorReferences(m_entry)) { + add(new BehaviorReferenceTreeNode(m_deobfuscatingTranslator, reference, index.getAccess(m_entry))); + } + + if (recurse && children != null) { + for (Object child : children) { + if (child instanceof BehaviorReferenceTreeNode) { + BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode)child; + + // don't recurse into ancestor + Set ancestors = Sets.newHashSet(); + TreeNode n = (TreeNode)node; + while (n.getParent() != null) { + n = n.getParent(); + if (n instanceof BehaviorReferenceTreeNode) { + ancestors.add( ((BehaviorReferenceTreeNode)n).getEntry()); + } + } + if (ancestors.contains(node.getEntry())) { + continue; + } + + node.load(index, true); + } + } + } + } +} diff --git a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java new file mode 100644 index 00000000..49aac5f0 --- /dev/null +++ b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 3112703459157851912L; + + private Translator m_deobfuscatingTranslator; + private ClassEntry m_entry; + + public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + } + + public ClassEntry getClassEntry() { + return m_entry; + } + + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_entry.getClassName()); + } + + @Override + public String toString() { + String className = getDeobfClassName(); + if (className == null) { + className = m_entry.getClassName(); + } + return className; + } + + public void load(JarIndex index) { + // get all method implementations + List nodes = Lists.newArrayList(); + for (String implementingClassName : index.getImplementingClasses(m_entry.getClassName())) { + nodes.add(new ClassImplementationsTreeNode(m_deobfuscatingTranslator, new ClassEntry(implementingClassName))); + } + + // add them to this node + for (ClassImplementationsTreeNode node : nodes) { + this.add(node); + } + } + + public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { + // is this the node? + if (node.m_entry.equals(entry)) { + return node; + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode)node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } +} diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java new file mode 100644 index 00000000..3eaa3912 --- /dev/null +++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Translator; + +public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 4432367405826178490L; + + private Translator m_deobfuscatingTranslator; + private String m_obfClassName; + + public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_obfClassName = obfClassName; + } + + public String getObfClassName() { + return m_obfClassName; + } + + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_obfClassName); + } + + @Override + public String toString() { + String deobfClassName = getDeobfClassName(); + if (deobfClassName != null) { + return deobfClassName; + } + return m_obfClassName; + } + + public void load(TranslationIndex ancestries, boolean recurse) { + // get all the child nodes + List nodes = Lists.newArrayList(); + for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(m_obfClassName))) { + nodes.add(new ClassInheritanceTreeNode(m_deobfuscatingTranslator, subclassEntry.getName())); + } + + // add them to this node + for (ClassInheritanceTreeNode node : nodes) { + this.add(node); + } + + if (recurse) { + for (ClassInheritanceTreeNode node : nodes) { + node.load(ancestries, true); + } + } + } + + public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { + // is this the node? + if (node.getObfClassName().equals(entry.getName())) { + return node; + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode)node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } +} diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java new file mode 100644 index 00000000..bb611df5 --- /dev/null +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.Arrays; +import java.util.List; + +import cuchaz.enigma.Util; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; + +public class EntryReference { + + private static final List ConstructorNonNames = Arrays.asList("this", "super", "static"); + public E entry; + public C context; + + private boolean m_isNamed; + + public EntryReference(E entry, String sourceName) { + this(entry, sourceName, null); + } + + public EntryReference(E entry, String sourceName, C context) { + if (entry == null) { + throw new IllegalArgumentException("Entry cannot be null!"); + } + + this.entry = entry; + this.context = context; + + m_isNamed = sourceName != null && sourceName.length() > 0; + if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) { + m_isNamed = false; + } + } + + public EntryReference(E entry, C context, EntryReference other) { + this.entry = entry; + this.context = context; + m_isNamed = other.m_isNamed; + } + + public ClassEntry getLocationClassEntry() { + if (context != null) { + return context.getClassEntry(); + } + return entry.getClassEntry(); + } + + public boolean isNamed() { + return m_isNamed; + } + + public Entry getNameableEntry() { + if (entry instanceof ConstructorEntry) { + // renaming a constructor really means renaming the class + return entry.getClassEntry(); + } + return entry; + } + + public String getNamableName() { + if (getNameableEntry() instanceof ClassEntry) { + ClassEntry classEntry = (ClassEntry)getNameableEntry(); + if (classEntry.isInnerClass()) { + // make sure we only rename the inner class name + return classEntry.getInnerClassName(); + } + } + + return getNameableEntry().getName(); + } + + @Override + public int hashCode() { + if (context != null) { + return Util.combineHashesOrdered(entry.hashCode(), context.hashCode()); + } + return entry.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof EntryReference) { + return equals((EntryReference)other); + } + return false; + } + + public boolean equals(EntryReference other) { + // check entry first + boolean isEntrySame = entry.equals(other.entry); + if (!isEntrySame) { + return false; + } + + // check caller + if (context == null && other.context == null) { + return true; + } else if (context != null && other.context != null) { + return context.equals(other.context); + } + return false; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(entry); + if (context != null) { + buf.append(" called from "); + buf.append(context); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java new file mode 100644 index 00000000..b54489cd --- /dev/null +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.AbstractMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class EntryRenamer { + + public static void renameClassesInSet(Map renames, Set set) { + List entries = Lists.newArrayList(); + for (T val : set) { + entries.add(renameClassesInThing(renames, val)); + } + set.clear(); + set.addAll(entries); + } + + public static void renameClassesInMap(Map renames, Map map) { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for (Map.Entry entry : map.entrySet()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameClassesInThing(renames, entry.getKey()), + renameClassesInThing(renames, entry.getValue()) + )); + } + map.clear(); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); + } + } + + public static void renameClassesInMultimap(Map renames, Multimap map) { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for (Map.Entry entry : map.entries()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameClassesInThing(renames, entry.getKey()), + renameClassesInThing(renames, entry.getValue()) + )); + } + map.clear(); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); + } + } + + public static void renameMethodsInMultimap(Map renames, Multimap map) { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for (Map.Entry entry : map.entries()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameMethodsInThing(renames, entry.getKey()), + renameMethodsInThing(renames, entry.getValue()) + )); + } + map.clear(); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); + } + } + + public static void renameMethodsInMap(Map renames, Map map) { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for (Map.Entry entry : map.entrySet()) { + entriesToAdd.add(new AbstractMap.SimpleEntry( + renameMethodsInThing(renames, entry.getKey()), + renameMethodsInThing(renames, entry.getValue()) + )); + } + map.clear(); + for (Map.Entry entry : entriesToAdd) { + map.put(entry.getKey(), entry.getValue()); + } + } + + @SuppressWarnings("unchecked") + public static T renameMethodsInThing(Map renames, T thing) { + if (thing instanceof MethodEntry) { + MethodEntry methodEntry = (MethodEntry)thing; + MethodEntry newMethodEntry = renames.get(methodEntry); + if (newMethodEntry != null) { + return (T)new MethodEntry( + methodEntry.getClassEntry(), + newMethodEntry.getName(), + methodEntry.getSignature() + ); + } + return thing; + } else if (thing instanceof ArgumentEntry) { + ArgumentEntry argumentEntry = (ArgumentEntry)thing; + return (T)new ArgumentEntry( + renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()), + argumentEntry.getIndex(), + argumentEntry.getName() + ); + } else if (thing instanceof EntryReference) { + EntryReference reference = (EntryReference)thing; + reference.entry = renameMethodsInThing(renames, reference.entry); + reference.context = renameMethodsInThing(renames, reference.context); + return thing; + } + return thing; + } + + @SuppressWarnings("unchecked") + public static T renameClassesInThing(Map renames, T thing) { + if (thing instanceof String) { + String stringEntry = (String)thing; + if (renames.containsKey(stringEntry)) { + return (T)renames.get(stringEntry); + } + } else if (thing instanceof ClassEntry) { + ClassEntry classEntry = (ClassEntry)thing; + return (T)new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); + } else if (thing instanceof FieldEntry) { + FieldEntry fieldEntry = (FieldEntry)thing; + return (T)new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName()); + } else if (thing instanceof ConstructorEntry) { + ConstructorEntry constructorEntry = (ConstructorEntry)thing; + return (T)new ConstructorEntry( + renameClassesInThing(renames, constructorEntry.getClassEntry()), + constructorEntry.getSignature() + ); + } else if (thing instanceof MethodEntry) { + MethodEntry methodEntry = (MethodEntry)thing; + return (T)new MethodEntry( + renameClassesInThing(renames, methodEntry.getClassEntry()), + methodEntry.getName(), + methodEntry.getSignature() + ); + } else if (thing instanceof ArgumentEntry) { + ArgumentEntry argumentEntry = (ArgumentEntry)thing; + return (T)new ArgumentEntry( + renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), + argumentEntry.getIndex(), + argumentEntry.getName() + ); + } else if (thing instanceof EntryReference) { + EntryReference reference = (EntryReference)thing; + reference.entry = renameClassesInThing(renames, reference.entry); + reference.context = renameClassesInThing(renames, reference.context); + return thing; + } + + return thing; + } +} diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java new file mode 100644 index 00000000..2173eea6 --- /dev/null +++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import javax.swing.tree.DefaultMutableTreeNode; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.Translator; + +public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { + + private static final long serialVersionUID = -7934108091928699835L; + + private Translator m_deobfuscatingTranslator; + private FieldEntry m_entry; + private EntryReference m_reference; + private Access m_access; + + public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + m_reference = null; + } + + private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference reference, Access access) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = reference.entry; + m_reference = reference; + m_access = access; + } + + @Override + public FieldEntry getEntry() { + return m_entry; + } + + @Override + public EntryReference getReference() { + return m_reference; + } + + @Override + public String toString() { + if (m_reference != null) { + return String.format("%s (%s)", m_deobfuscatingTranslator.translateEntry(m_reference.context), m_access); + } + return m_deobfuscatingTranslator.translateEntry(m_entry).toString(); + } + + public void load(JarIndex index, boolean recurse) { + // get all the child nodes + if (m_reference == null) { + for (EntryReference reference : index.getFieldReferences(m_entry)) { + add(new FieldReferenceTreeNode(m_deobfuscatingTranslator, reference, index.getAccess(m_entry))); + } + } else { + for (EntryReference reference : index.getBehaviorReferences(m_reference.context)) { + add(new BehaviorReferenceTreeNode(m_deobfuscatingTranslator, reference, index.getAccess(m_reference.context))); + } + } + + if (recurse && children != null) { + for (Object node : children) { + if (node instanceof BehaviorReferenceTreeNode) { + ((BehaviorReferenceTreeNode)node).load(index, true); + } else if (node instanceof FieldReferenceTreeNode) { + ((FieldReferenceTreeNode)node).load(index, true); + } + } + } + } +} diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java new file mode 100644 index 00000000..72a99122 --- /dev/null +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import javassist.ByteArrayClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.Descriptor; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.mapping.ClassEntry; + +public class JarClassIterator implements Iterator { + + private JarFile m_jar; + private Iterator m_iter; + + public JarClassIterator(JarFile jar) { + m_jar = jar; + + // get the jar entries that correspond to classes + List classEntries = Lists.newArrayList(); + Enumeration entries = m_jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + + // is this a class file? + if (entry.getName().endsWith(".class")) { + classEntries.add(entry); + } + } + m_iter = classEntries.iterator(); + } + + @Override + public boolean hasNext() { + return m_iter.hasNext(); + } + + @Override + public CtClass next() { + JarEntry entry = m_iter.next(); + try { + return getClass(m_jar, entry); + } catch (IOException | NotFoundException ex) { + throw new Error("Unable to load class: " + entry.getName()); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + public static List getClassEntries(JarFile jar) { + List classEntries = Lists.newArrayList(); + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + + // is this a class file? + if (!entry.isDirectory() && entry.getName().endsWith(".class")) { + classEntries.add(getClassEntry(entry)); + } + } + return classEntries; + } + + public static Iterable classes(final JarFile jar) { + return new Iterable() { + @Override + public Iterator iterator() { + return new JarClassIterator(jar); + } + }; + } + + public static CtClass getClass(JarFile jar, ClassEntry classEntry) { + try { + return getClass(jar, new JarEntry(classEntry.getName() + ".class")); + } catch (IOException | NotFoundException ex) { + throw new Error("Unable to load class: " + classEntry.getName()); + } + } + + private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException { + // read the class into a buffer + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buf = new byte[Constants.KiB]; + int totalNumBytesRead = 0; + InputStream in = jar.getInputStream(entry); + while (in.available() > 0) { + int numBytesRead = in.read(buf); + if (numBytesRead < 0) { + break; + } + bos.write(buf, 0, numBytesRead); + + // sanity checking + totalNumBytesRead += numBytesRead; + if (totalNumBytesRead > Constants.MiB) { + throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!"); + } + } + + // get a javassist handle for the class + String className = Descriptor.toJavaName(getClassEntry(entry).getName()); + ClassPool classPool = new ClassPool(); + classPool.appendSystemPath(); + classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray())); + return classPool.get(className); + } + + private static ClassEntry getClassEntry(JarEntry entry) { + return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length())); + } +} diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java new file mode 100644 index 00000000..3aac8bd0 --- /dev/null +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -0,0 +1,734 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarFile; + +import javassist.CannotCompileException; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.bytecode.AccessFlag; +import javassist.bytecode.Descriptor; +import javassist.bytecode.FieldInfo; +import javassist.expr.ConstructorCall; +import javassist.expr.ExprEditor; +import javassist.expr.FieldAccess; +import javassist.expr.MethodCall; +import javassist.expr.NewExpr; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.bytecode.ClassRenamer; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class JarIndex { + + private Set m_obfClassEntries; + private TranslationIndex m_translationIndex; + private Multimap m_interfaces; + private Map m_access; + private Map m_fieldClasses; // TODO: this will become obsolete! + private Multimap m_methodImplementations; + private Multimap> m_behaviorReferences; + private Multimap> m_fieldReferences; + private Multimap m_innerClasses; + private Map m_outerClasses; + private Map m_anonymousClasses; + + public JarIndex() { + m_obfClassEntries = Sets.newHashSet(); + m_translationIndex = new TranslationIndex(); + m_interfaces = HashMultimap.create(); + m_access = Maps.newHashMap(); + m_fieldClasses = Maps.newHashMap(); + m_methodImplementations = HashMultimap.create(); + m_behaviorReferences = HashMultimap.create(); + m_fieldReferences = HashMultimap.create(); + m_innerClasses = HashMultimap.create(); + m_outerClasses = Maps.newHashMap(); + m_anonymousClasses = Maps.newHashMap(); + } + + public void indexJar(JarFile jar, boolean buildInnerClasses) { + + // step 1: read the class names + for (ClassEntry classEntry : JarClassIterator.getClassEntries(jar)) { + if (classEntry.isInDefaultPackage()) { + // move out of default package + classEntry = new ClassEntry(Constants.NonePackage + "/" + classEntry.getName()); + } + m_obfClassEntries.add(classEntry); + } + + // step 2: index field/method/constructor access + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + for (CtField field : c.getDeclaredFields()) { + m_access.put(JavassistUtil.getFieldEntry(field), Access.get(field)); + } + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + m_access.put(JavassistUtil.getBehaviorEntry(behavior), Access.get(behavior)); + } + } + + // step 3: index extends, implements, fields, and methods + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + m_translationIndex.indexClass(c); + String className = Descriptor.toJvmName(c.getName()); + for (String interfaceName : c.getClassFile().getInterfaces()) { + className = Descriptor.toJvmName(className); + interfaceName = Descriptor.toJvmName(interfaceName); + if (className.equals(interfaceName)) { + throw new IllegalArgumentException("Class cannot be its own interface! " + className); + } + m_interfaces.put(className, interfaceName); + } + for (CtField field : c.getDeclaredFields()) { + indexField(field); + } + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + indexBehavior(behavior); + } + } + + // step 4: index field, method, constructor references + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + indexBehaviorReferences(behavior); + } + } + + if (buildInnerClasses) { + // step 5: index inner classes and anonymous classes + for (CtClass c : JarClassIterator.classes(jar)) { + ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); + String outerClassName = findOuterClass(c); + if (outerClassName != null) { + String innerClassName = c.getSimpleName(); + m_innerClasses.put(outerClassName, innerClassName); + boolean innerWasAdded = m_outerClasses.put(innerClassName, outerClassName) == null; + assert (innerWasAdded); + + BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassName); + if (enclosingBehavior != null) { + m_anonymousClasses.put(innerClassName, enclosingBehavior); + + // DEBUG + // System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); + } else { + // DEBUG + // System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); + } + } + } + + // step 6: update other indices with inner class info + Map renames = Maps.newHashMap(); + for (Map.Entry entry : m_outerClasses.entrySet()) { + renames.put(Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey()); + } + EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); + m_translationIndex.renameClasses(renames); + EntryRenamer.renameClassesInMultimap(renames, m_interfaces); + EntryRenamer.renameClassesInMultimap(renames, m_methodImplementations); + EntryRenamer.renameClassesInMultimap(renames, m_behaviorReferences); + EntryRenamer.renameClassesInMultimap(renames, m_fieldReferences); + EntryRenamer.renameClassesInMap(renames, m_access); + } + } + + private void indexField(CtField field) { + // get the field entry + String className = Descriptor.toJvmName(field.getDeclaringClass().getName()); + FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName()); + + // is the field a class type? + if (field.getSignature().startsWith("L")) { + ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1)); + m_fieldClasses.put(fieldEntry, fieldTypeEntry); + } + } + + private void indexBehavior(CtBehavior behavior) { + // get the behavior entry + final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + if (behaviorEntry instanceof MethodEntry) { + MethodEntry methodEntry = (MethodEntry)behaviorEntry; + + // index implementation + m_methodImplementations.put(behaviorEntry.getClassName(), methodEntry); + } + // looks like we don't care about constructors here + } + + private void indexBehaviorReferences(CtBehavior behavior) { + // index method calls + final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + try { + behavior.instrument(new ExprEditor() { + @Override + public void edit(MethodCall call) { + MethodEntry calledMethodEntry = JavassistUtil.getMethodEntry(call); + ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledMethodEntry); + if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { + calledMethodEntry = new MethodEntry( + resolvedClassEntry, + calledMethodEntry.getName(), + calledMethodEntry.getSignature() + ); + } + EntryReference reference = new EntryReference( + calledMethodEntry, + call.getMethodName(), + behaviorEntry + ); + m_behaviorReferences.put(calledMethodEntry, reference); + } + + @Override + public void edit(FieldAccess call) { + FieldEntry calledFieldEntry = JavassistUtil.getFieldEntry(call); + ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry); + if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { + calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName()); + } + EntryReference reference = new EntryReference( + calledFieldEntry, + call.getFieldName(), + behaviorEntry + ); + m_fieldReferences.put(calledFieldEntry, reference); + } + + @Override + public void edit(ConstructorCall call) { + ConstructorEntry calledConstructorEntry = JavassistUtil.getConstructorEntry(call); + EntryReference reference = new EntryReference( + calledConstructorEntry, + call.getMethodName(), + behaviorEntry + ); + m_behaviorReferences.put(calledConstructorEntry, reference); + } + + @Override + public void edit(NewExpr call) { + ConstructorEntry calledConstructorEntry = JavassistUtil.getConstructorEntry(call); + EntryReference reference = new EntryReference( + calledConstructorEntry, + call.getClassName(), + behaviorEntry + ); + m_behaviorReferences.put(calledConstructorEntry, reference); + } + }); + } catch (CannotCompileException ex) { + throw new Error(ex); + } + } + + private String findOuterClass(CtClass c) { + + // inner classes: + // have constructors that can (illegally) set synthetic fields + // the outer class is the only class that calls constructors + + // use the synthetic fields to find the synthetic constructors + for (CtConstructor constructor : c.getDeclaredConstructors()) { + Set syntheticFieldTypes = Sets.newHashSet(); + if (!isIllegalConstructor(syntheticFieldTypes, constructor)) { + continue; + } + + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + ConstructorEntry constructorEntry = JavassistUtil.getConstructorEntry(constructor); + + // gather the classes from the illegally-set synthetic fields + Set illegallySetClasses = Sets.newHashSet(); + for (String type : syntheticFieldTypes) { + if (type.startsWith("L")) { + ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1)); + if (isSaneOuterClass(outerClassEntry, classEntry)) { + illegallySetClasses.add(outerClassEntry); + } + } + } + + // who calls this constructor? + Set callerClasses = Sets.newHashSet(); + for (EntryReference reference : getBehaviorReferences(constructorEntry)) { + + // make sure it's not a call to super + if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { + + // is the entry a superclass of the context? + ClassEntry calledClassEntry = reference.entry.getClassEntry(); + ClassEntry superclassEntry = m_translationIndex.getSuperclass(reference.context.getClassEntry()); + if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) { + // it's a super call, skip + continue; + } + } + + if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) { + callerClasses.add(reference.context.getClassEntry()); + } + } + + // do we have an answer yet? + if (callerClasses.isEmpty()) { + if (illegallySetClasses.size() == 1) { + return illegallySetClasses.iterator().next().getName(); + } else { + System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); + } + } else { + if (callerClasses.size() == 1) { + return callerClasses.iterator().next().getName(); + } else { + // multiple callers, do the illegally set classes narrow it down? + Set intersection = Sets.newHashSet(callerClasses); + intersection.retainAll(illegallySetClasses); + if (intersection.size() == 1) { + return intersection.iterator().next().getName(); + } else { + System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); + } + } + } + } + + return null; + } + + private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { + + // clearly this would be silly + if (outerClassEntry.equals(innerClassEntry)) { + return false; + } + + // is the outer class in the jar? + if (!m_obfClassEntries.contains(outerClassEntry)) { + return false; + } + + return true; + } + + @SuppressWarnings("unchecked") + private boolean isIllegalConstructor(Set syntheticFieldTypes, CtConstructor constructor) { + + // illegal constructors only set synthetic member fields, then call super() + String className = constructor.getDeclaringClass().getName(); + + // collect all the field accesses, constructor calls, and method calls + final List illegalFieldWrites = Lists.newArrayList(); + final List constructorCalls = Lists.newArrayList(); + try { + constructor.instrument(new ExprEditor() { + @Override + public void edit(FieldAccess fieldAccess) { + if (fieldAccess.isWriter() && constructorCalls.isEmpty()) { + illegalFieldWrites.add(fieldAccess); + } + } + + @Override + public void edit(ConstructorCall constructorCall) { + constructorCalls.add(constructorCall); + } + }); + } catch (CannotCompileException ex) { + // we're not compiling anything... this is stupid + throw new Error(ex); + } + + // are there any illegal field writes? + if (illegalFieldWrites.isEmpty()) { + return false; + } + + // are all the writes to synthetic fields? + for (FieldAccess fieldWrite : illegalFieldWrites) { + + // all illegal writes have to be to the local class + if (!fieldWrite.getClassName().equals(className)) { + System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName())); + return false; + } + + // find the field + FieldInfo fieldInfo = null; + for (FieldInfo info : (List)constructor.getDeclaringClass().getClassFile().getFields()) { + if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) { + fieldInfo = info; + break; + } + } + if (fieldInfo == null) { + // field is in a superclass or something, can't be a local synthetic member + return false; + } + + // is this field synthetic? + boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; + if (isSynthetic) { + syntheticFieldTypes.add(fieldInfo.getDescriptor()); + } else { + System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName())); + return false; + } + } + + // we passed all the tests! + return true; + } + + private BehaviorEntry isAnonymousClass(CtClass c, String outerClassName) { + + ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + + // anonymous classes: + // can't be abstract + // have only one constructor + // it's called exactly once by the outer class + // the type the instance is assigned to can't be this type + + // is abstract? + if (Modifier.isAbstract(c.getModifiers())) { + return null; + } + + // is there exactly one constructor? + if (c.getDeclaredConstructors().length != 1) { + return null; + } + CtConstructor constructor = c.getDeclaredConstructors()[0]; + + // is this constructor called exactly once? + ConstructorEntry constructorEntry = JavassistUtil.getConstructorEntry(constructor); + Collection> references = getBehaviorReferences(constructorEntry); + if (references.size() != 1) { + return null; + } + + // does the caller use this type? + BehaviorEntry caller = references.iterator().next().context; + for (FieldEntry fieldEntry : getReferencedFields(caller)) { + ClassEntry fieldClass = getFieldClass(fieldEntry); + if (fieldClass != null && fieldClass.equals(innerClassEntry)) { + // caller references this type, so it can't be anonymous + return null; + } + } + for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) { + if (behaviorEntry.getSignature().hasClass(innerClassEntry)) { + return null; + } + } + + return caller; + } + + public Set getObfClassEntries() { + return m_obfClassEntries; + } + + public TranslationIndex getTranslationIndex() { + return m_translationIndex; + } + + public Access getAccess(Entry entry) { + return m_access.get(entry); + } + + public ClassEntry getFieldClass(FieldEntry fieldEntry) { + return m_fieldClasses.get(fieldEntry); + } + + public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { + + // get the root node + List ancestry = Lists.newArrayList(); + ancestry.add(obfClassEntry.getName()); + for (ClassEntry classEntry : m_translationIndex.getAncestry(obfClassEntry)) { + ancestry.add(classEntry.getName()); + } + ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( + deobfuscatingTranslator, + ancestry.get(ancestry.size() - 1) + ); + + // expand all children recursively + rootNode.load(m_translationIndex, true); + + return rootNode; + } + + public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { + + // is this even an interface? + if (isInterface(obfClassEntry.getClassName())) { + ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry); + node.load(this); + return node; + } + return null; + } + + public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { + + // travel to the ancestor implementation + ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); + for (ClassEntry ancestorClassEntry : m_translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { + MethodEntry ancestorMethodEntry = new MethodEntry( + new ClassEntry(ancestorClassEntry), + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + if (containsObfBehavior(ancestorMethodEntry)) { + baseImplementationClassEntry = ancestorClassEntry; + } + } + + // make a root node at the base + MethodEntry methodEntry = new MethodEntry( + baseImplementationClassEntry, + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( + deobfuscatingTranslator, + methodEntry, + containsObfBehavior(methodEntry) + ); + + // expand the full tree + rootNode.load(this, true); + + return rootNode; + } + + public MethodImplementationsTreeNode getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { + + MethodEntry interfaceMethodEntry; + + // is this method on an interface? + if (isInterface(obfMethodEntry.getClassName())) { + interfaceMethodEntry = obfMethodEntry; + } else { + // get the interface class + List methodInterfaces = Lists.newArrayList(); + for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) { + // is this method defined in this interface? + MethodEntry methodInterface = new MethodEntry( + new ClassEntry(interfaceName), + obfMethodEntry.getName(), + obfMethodEntry.getSignature() + ); + if (containsObfBehavior(methodInterface)) { + methodInterfaces.add(methodInterface); + } + } + if (methodInterfaces.isEmpty()) { + return null; + } + if (methodInterfaces.size() > 1) { + throw new Error("Too many interfaces define this method! This is not yet supported by Enigma!"); + } + interfaceMethodEntry = methodInterfaces.get(0); + } + + MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); + rootNode.load(this); + return rootNode; + } + + public Set getRelatedMethodImplementations(MethodEntry obfMethodEntry) { + Set methodEntries = Sets.newHashSet(); + getRelatedMethodImplementations(methodEntries, getMethodInheritance(null, obfMethodEntry)); + return methodEntries; + } + + private void getRelatedMethodImplementations(Set methodEntries, MethodInheritanceTreeNode node) { + MethodEntry methodEntry = node.getMethodEntry(); + if (containsObfBehavior(methodEntry)) { + // collect the entry + methodEntries.add(methodEntry); + } + + // look at interface methods too + MethodImplementationsTreeNode implementations = getMethodImplementations(null, methodEntry); + if (implementations != null) { + getRelatedMethodImplementations(methodEntries, implementations); + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode)node.getChildAt(i)); + } + } + + private void getRelatedMethodImplementations(Set methodEntries, MethodImplementationsTreeNode node) { + MethodEntry methodEntry = node.getMethodEntry(); + if (containsObfBehavior(methodEntry)) { + // collect the entry + methodEntries.add(methodEntry); + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode)node.getChildAt(i)); + } + } + + public Collection> getFieldReferences(FieldEntry fieldEntry) { + return m_fieldReferences.get(fieldEntry); + } + + public Collection getReferencedFields(BehaviorEntry behaviorEntry) { + // linear search is fast enough for now + Set fieldEntries = Sets.newHashSet(); + for (EntryReference reference : m_fieldReferences.values()) { + if (reference.context == behaviorEntry) { + fieldEntries.add(reference.entry); + } + } + return fieldEntries; + } + + public Collection> getBehaviorReferences(BehaviorEntry behaviorEntry) { + return m_behaviorReferences.get(behaviorEntry); + } + + public Collection getReferencedBehaviors(BehaviorEntry behaviorEntry) { + // linear search is fast enough for now + Set behaviorEntries = Sets.newHashSet(); + for (EntryReference reference : m_behaviorReferences.values()) { + if (reference.context == behaviorEntry) { + behaviorEntries.add(reference.entry); + } + } + return behaviorEntries; + } + + public Collection getInnerClasses(String obfOuterClassName) { + return m_innerClasses.get(obfOuterClassName); + } + + public String getOuterClass(String obfInnerClassName) { + // make sure we use the right name + if (new ClassEntry(obfInnerClassName).getPackageName() != null) { + throw new IllegalArgumentException("Don't reference obfuscated inner classes using packages: " + obfInnerClassName); + } + return m_outerClasses.get(obfInnerClassName); + } + + public boolean isAnonymousClass(String obfInnerClassName) { + return m_anonymousClasses.containsKey(obfInnerClassName); + } + + public BehaviorEntry getAnonymousClassCaller(String obfInnerClassName) { + return m_anonymousClasses.get(obfInnerClassName); + } + + public Set getInterfaces(String className) { + Set interfaceNames = new HashSet(); + interfaceNames.addAll(m_interfaces.get(className)); + for (ClassEntry ancestor : m_translationIndex.getAncestry(new ClassEntry(className))) { + interfaceNames.addAll(m_interfaces.get(ancestor.getName())); + } + return interfaceNames; + } + + public Set getImplementingClasses(String targetInterfaceName) { + // linear search is fast enough for now + Set classNames = Sets.newHashSet(); + for (Map.Entry entry : m_interfaces.entries()) { + String className = entry.getKey(); + String interfaceName = entry.getValue(); + if (interfaceName.equals(targetInterfaceName)) { + classNames.add(className); + m_translationIndex.getSubclassNamesRecursively(classNames, new ClassEntry(className)); + } + } + return classNames; + } + + public boolean isInterface(String className) { + return m_interfaces.containsValue(className); + } + + public boolean containsObfClass(ClassEntry obfClassEntry) { + return m_obfClassEntries.contains(obfClassEntry); + } + + public boolean containsObfField(FieldEntry obfFieldEntry) { + return m_access.containsKey(obfFieldEntry); + } + + public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { + return m_access.containsKey(obfBehaviorEntry); + } + + public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { + // check the behavior + if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { + return false; + } + + // check the argument + if (obfArgumentEntry.getIndex() >= obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size()) { + return false; + } + + return true; + } + + public boolean containsObfEntry(Entry obfEntry) { + if (obfEntry instanceof ClassEntry) { + return containsObfClass((ClassEntry)obfEntry); + } else if (obfEntry instanceof FieldEntry) { + return containsObfField((FieldEntry)obfEntry); + } else if (obfEntry instanceof BehaviorEntry) { + return containsObfBehavior((BehaviorEntry)obfEntry); + } else if (obfEntry instanceof ArgumentEntry) { + return containsObfArgument((ArgumentEntry)obfEntry); + } else { + throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); + } + } +} diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java new file mode 100644 index 00000000..10092268 --- /dev/null +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 3781080657461899915L; + + private Translator m_deobfuscatingTranslator; + private MethodEntry m_entry; + + public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { + if (entry == null) { + throw new IllegalArgumentException("entry cannot be null!"); + } + + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + } + + public MethodEntry getMethodEntry() { + return m_entry; + } + + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_entry.getClassName()); + } + + public String getDeobfMethodName() { + return m_deobfuscatingTranslator.translate(m_entry); + } + + @Override + public String toString() { + String className = getDeobfClassName(); + if (className == null) { + className = m_entry.getClassName(); + } + + String methodName = getDeobfMethodName(); + if (methodName == null) { + methodName = m_entry.getName(); + } + return className + "." + methodName + "()"; + } + + public void load(JarIndex index) { + // get all method implementations + List nodes = Lists.newArrayList(); + for (String implementingClassName : index.getImplementingClasses(m_entry.getClassName())) { + MethodEntry methodEntry = new MethodEntry( + new ClassEntry(implementingClassName), + m_entry.getName(), + m_entry.getSignature() + ); + if (index.containsObfBehavior(methodEntry)) { + nodes.add(new MethodImplementationsTreeNode(m_deobfuscatingTranslator, methodEntry)); + } + } + + // add them to this node + for (MethodImplementationsTreeNode node : nodes) { + this.add(node); + } + } + + public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) { + // is this the node? + if (node.getMethodEntry().equals(entry)) { + return node; + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode)node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } +} diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java new file mode 100644 index 00000000..87182204 --- /dev/null +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 1096677030991810007L; + + private Translator m_deobfuscatingTranslator; + private MethodEntry m_entry; + private boolean m_isImplemented; + + public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { + m_deobfuscatingTranslator = deobfuscatingTranslator; + m_entry = entry; + m_isImplemented = isImplemented; + } + + public MethodEntry getMethodEntry() { + return m_entry; + } + + public String getDeobfClassName() { + return m_deobfuscatingTranslator.translateClass(m_entry.getClassName()); + } + + public String getDeobfMethodName() { + return m_deobfuscatingTranslator.translate(m_entry); + } + + public boolean isImplemented() { + return m_isImplemented; + } + + @Override + public String toString() { + String className = getDeobfClassName(); + if (className == null) { + className = m_entry.getClassName(); + } + + if (!m_isImplemented) { + return className; + } else { + String methodName = getDeobfMethodName(); + if (methodName == null) { + methodName = m_entry.getName(); + } + return className + "." + methodName + "()"; + } + } + + public void load(JarIndex index, boolean recurse) { + // get all the child nodes + List nodes = Lists.newArrayList(); + for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(m_entry.getClassEntry())) { + MethodEntry methodEntry = new MethodEntry( + subclassEntry, + m_entry.getName(), + m_entry.getSignature() + ); + nodes.add(new MethodInheritanceTreeNode( + m_deobfuscatingTranslator, + methodEntry, + index.containsObfBehavior(methodEntry) + )); + } + + // add them to this node + for (MethodInheritanceTreeNode node : nodes) { + this.add(node); + } + + if (recurse) { + for (MethodInheritanceTreeNode node : nodes) { + node.load(index, true); + } + } + } + + public static MethodInheritanceTreeNode findNode(MethodInheritanceTreeNode node, MethodEntry entry) { + // is this the node? + if (node.getMethodEntry().equals(entry)) { + return node; + } + + // recurse + for (int i = 0; i < node.getChildCount(); i++) { + MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode)node.getChildAt(i), entry); + if (foundNode != null) { + return foundNode; + } + } + return null; + } +} diff --git a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java new file mode 100644 index 00000000..2b08616a --- /dev/null +++ b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import cuchaz.enigma.mapping.Entry; + +public interface ReferenceTreeNode { + E getEntry(); + EntryReference getReference(); +} diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java new file mode 100644 index 00000000..b43ab614 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.strobel.decompiler.languages.Region; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.Identifier; + +import cuchaz.enigma.mapping.Entry; + +public class SourceIndex { + + private String m_source; + private TreeMap> m_tokenToReference; + private Multimap,Token> m_referenceToTokens; + private Map m_declarationToToken; + private List m_lineOffsets; + + public SourceIndex(String source) { + m_source = source; + m_tokenToReference = Maps.newTreeMap(); + m_referenceToTokens = HashMultimap.create(); + m_declarationToToken = Maps.newHashMap(); + m_lineOffsets = Lists.newArrayList(); + + // count the lines + m_lineOffsets.add(0); + for (int i = 0; i < source.length(); i++) { + if (source.charAt(i) == '\n') { + m_lineOffsets.add(i + 1); + } + } + } + + public String getSource() { + return m_source; + } + + public Token getToken(AstNode node) { + + // get the text of the node + String name = ""; + if (node instanceof Identifier) { + name = ((Identifier)node).getName(); + } + + // get a token for this node's region + Region region = node.getRegion(); + if (region.getBeginLine() == 0 || region.getEndLine() == 0) { + // DEBUG + System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region)); + return null; + } + Token token = new Token( + toPos(region.getBeginLine(), region.getBeginColumn()), + toPos(region.getEndLine(), region.getEndColumn()), + m_source + ); + if (token.start == 0) { + // DEBUG + System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region)); + return null; + } + + // DEBUG + // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); + + // for tokens representing inner classes, make sure we only get the simple name + int pos = name.lastIndexOf('$'); + if (pos >= 0) { + token.end -= pos + 1; + } + + return token; + } + + public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { + Token token = getToken(node); + if (token != null) { + EntryReference deobfReference = new EntryReference(deobfEntry, token.text, deobfContext); + m_tokenToReference.put(token, deobfReference); + m_referenceToTokens.put(deobfReference, token); + } + } + + public void addDeclaration(AstNode node, Entry deobfEntry) { + Token token = getToken(node); + if (token != null) { + EntryReference reference = new EntryReference(deobfEntry, token.text); + m_tokenToReference.put(token, reference); + m_referenceToTokens.put(reference, token); + m_declarationToToken.put(deobfEntry, token); + } + } + + public Token getReferenceToken(int pos) { + Token token = m_tokenToReference.floorKey(new Token(pos, pos, null)); + if (token != null && token.contains(pos)) { + return token; + } + return null; + } + + public Collection getReferenceTokens(EntryReference deobfReference) { + return m_referenceToTokens.get(deobfReference); + } + + public EntryReference getDeobfReference(Token token) { + if (token == null) { + return null; + } + return m_tokenToReference.get(token); + } + + public void replaceDeobfReference(Token token, EntryReference newDeobfReference) { + EntryReference oldDeobfReference = m_tokenToReference.get(token); + m_tokenToReference.put(token, newDeobfReference); + Collection tokens = m_referenceToTokens.get(oldDeobfReference); + m_referenceToTokens.removeAll(oldDeobfReference); + m_referenceToTokens.putAll(newDeobfReference, tokens); + } + + public Iterable referenceTokens() { + return m_tokenToReference.keySet(); + } + + public Iterable declarationTokens() { + return m_declarationToToken.values(); + } + + public Token getDeclarationToken(Entry deobfEntry) { + return m_declarationToToken.get(deobfEntry); + } + + public int getLineNumber(int pos) { + // line number is 1-based + int line = 0; + for (Integer offset : m_lineOffsets) { + if (offset > pos) { + break; + } + line++; + } + return line; + } + + public int getColumnNumber(int pos) { + // column number is 1-based + return pos - m_lineOffsets.get(getLineNumber(pos) - 1) + 1; + } + + private int toPos(int line, int col) { + // line and col are 1-based + return m_lineOffsets.get(line - 1) + col - 1; + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java new file mode 100644 index 00000000..41551283 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.strobel.assembler.metadata.MemberReference; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.MethodReference; +import com.strobel.assembler.metadata.ParameterDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.decompiler.languages.TextLocation; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Signature; + +public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { + + private BehaviorEntry m_behaviorEntry; + + public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) { + m_behaviorEntry = behaviorEntry; + } + + @Override + public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + + // get the behavior entry + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + BehaviorEntry behaviorEntry = null; + if (ref instanceof MethodReference) { + MethodReference methodRef = (MethodReference)ref; + if (methodRef.isConstructor()) { + behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getSignature())); + } else if (methodRef.isTypeInitializer()) { + behaviorEntry = new ConstructorEntry(classEntry); + } else { + behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getSignature())); + } + } + if (behaviorEntry != null) { + // get the node for the token + AstNode tokenNode = null; + if (node.getTarget() instanceof MemberReferenceExpression) { + tokenNode = ((MemberReferenceExpression)node.getTarget()).getMemberNameToken(); + } else if (node.getTarget() instanceof SuperReferenceExpression) { + tokenNode = node.getTarget(); + } else if (node.getTarget() instanceof ThisReferenceExpression) { + tokenNode = node.getTarget(); + } + if (tokenNode != null) { + index.addReference(tokenNode, behaviorEntry, m_behaviorEntry); + } + } + + return recurse(node, index); + } + + @Override + public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { + // make sure this is actually a field + if (ref.getSignature().indexOf('(') >= 0) { + throw new Error("Expected a field here! got " + ref); + } + + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName()); + index.addReference(node.getMemberNameToken(), fieldEntry, m_behaviorEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitSimpleType(SimpleType node, SourceIndex index) { + TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); + if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { + ClassEntry classEntry = new ClassEntry(ref.getInternalName()); + index.addReference(node.getIdentifierToken(), classEntry, m_behaviorEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { + ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + MethodDefinition methodDef = (MethodDefinition)def.getMethod(); + BehaviorEntry behaviorEntry; + if (methodDef.isConstructor()) { + behaviorEntry = new ConstructorEntry(classEntry, new Signature(methodDef.getSignature())); + } else { + behaviorEntry = new MethodEntry(classEntry, methodDef.getName(), new Signature(methodDef.getSignature())); + } + ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); + index.addDeclaration(node.getNameToken(), argumentEntry); + + return recurse(node, index); + } + + @Override + public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName()); + index.addReference(node.getIdentifierToken(), fieldEntry, m_behaviorEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { + MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { + ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); + ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getSignature())); + if (node.getType() instanceof SimpleType) { + SimpleType simpleTypeNode = (SimpleType)node.getType(); + index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, m_behaviorEntry); + } + } + + return recurse(node, index); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java new file mode 100644 index 00000000..72220350 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.strobel.assembler.metadata.FieldDefinition; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.decompiler.languages.TextLocation; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.Signature; + +public class SourceIndexClassVisitor extends SourceIndexVisitor { + + private ClassEntry m_classEntry; + + public SourceIndexClassVisitor(ClassEntry classEntry) { + m_classEntry = classEntry; + } + + @Override + public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { + // is this this class, or a subtype? + TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getInternalName()); + if (!classEntry.equals(m_classEntry)) { + // it's a sub-type, recurse + index.addDeclaration(node.getNameToken(), classEntry); + return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); + } + + return recurse(node, index); + } + + @Override + public Void visitSimpleType(SimpleType node, SourceIndex index) { + TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); + if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { + ClassEntry classEntry = new ClassEntry(ref.getInternalName()); + index.addReference(node.getIdentifierToken(), classEntry, m_classEntry); + } + + return recurse(node, index); + } + + @Override + public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { + MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(classEntry, def.getName(), def.getSignature()); + AstNode tokenNode = node.getNameToken(); + if (behaviorEntry instanceof ConstructorEntry) { + ConstructorEntry constructorEntry = (ConstructorEntry)behaviorEntry; + if (constructorEntry.isStatic()) { + tokenNode = node.getModifiers().firstOrNullObject(); + } + } + index.addDeclaration(tokenNode, behaviorEntry); + return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry), index); + } + + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { + MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(def.getSignature())); + index.addDeclaration(node.getNameToken(), constructorEntry); + return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); + } + + @Override + public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { + FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName()); + assert (node.getVariables().size() == 1); + VariableInitializer variable = node.getVariables().firstOrNullObject(); + index.addDeclaration(variable.getNameToken(), fieldEntry); + + return recurse(node, index); + } + + @Override + public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { + // treat enum declarations as field declarations + FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName()); + index.addDeclaration(node.getNameToken(), fieldEntry); + + return recurse(node, index); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java new file mode 100644 index 00000000..0d5bdc02 --- /dev/null +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -0,0 +1,452 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.decompiler.languages.java.ast.Annotation; +import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; +import com.strobel.decompiler.languages.java.ast.ArraySpecifier; +import com.strobel.decompiler.languages.java.ast.AssertStatement; +import com.strobel.decompiler.languages.java.ast.AssignmentExpression; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.BinaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.BlockStatement; +import com.strobel.decompiler.languages.java.ast.BreakStatement; +import com.strobel.decompiler.languages.java.ast.CaseLabel; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.CatchClause; +import com.strobel.decompiler.languages.java.ast.ClassOfExpression; +import com.strobel.decompiler.languages.java.ast.Comment; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; +import com.strobel.decompiler.languages.java.ast.ComposedType; +import com.strobel.decompiler.languages.java.ast.ConditionalExpression; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.ContinueStatement; +import com.strobel.decompiler.languages.java.ast.DoWhileStatement; +import com.strobel.decompiler.languages.java.ast.EmptyStatement; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.ExpressionStatement; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.ForEachStatement; +import com.strobel.decompiler.languages.java.ast.ForStatement; +import com.strobel.decompiler.languages.java.ast.GotoStatement; +import com.strobel.decompiler.languages.java.ast.IAstVisitor; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.IfElseStatement; +import com.strobel.decompiler.languages.java.ast.ImportDeclaration; +import com.strobel.decompiler.languages.java.ast.IndexerExpression; +import com.strobel.decompiler.languages.java.ast.InstanceInitializer; +import com.strobel.decompiler.languages.java.ast.InstanceOfExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.JavaTokenNode; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.LabelStatement; +import com.strobel.decompiler.languages.java.ast.LabeledStatement; +import com.strobel.decompiler.languages.java.ast.LambdaExpression; +import com.strobel.decompiler.languages.java.ast.LocalTypeDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.MethodGroupExpression; +import com.strobel.decompiler.languages.java.ast.NewLineNode; +import com.strobel.decompiler.languages.java.ast.NullReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.PackageDeclaration; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.ParenthesizedExpression; +import com.strobel.decompiler.languages.java.ast.PrimitiveExpression; +import com.strobel.decompiler.languages.java.ast.ReturnStatement; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.SwitchSection; +import com.strobel.decompiler.languages.java.ast.SwitchStatement; +import com.strobel.decompiler.languages.java.ast.SynchronizedStatement; +import com.strobel.decompiler.languages.java.ast.TextNode; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThrowStatement; +import com.strobel.decompiler.languages.java.ast.TryCatchStatement; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; +import com.strobel.decompiler.languages.java.ast.UnaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; +import com.strobel.decompiler.languages.java.ast.WhileStatement; +import com.strobel.decompiler.languages.java.ast.WildcardType; +import com.strobel.decompiler.patterns.Pattern; + +import cuchaz.enigma.mapping.ClassEntry; + +public class SourceIndexVisitor implements IAstVisitor { + + @Override + public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { + TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); + ClassEntry classEntry = new ClassEntry(def.getInternalName()); + index.addDeclaration(node.getNameToken(), classEntry); + + return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); + } + + protected Void recurse(AstNode node, SourceIndex index) { + for (final AstNode child : node.getChildren()) { + child.acceptVisitor(this, index); + } + return null; + } + + @Override + public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSimpleType(SimpleType node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitComment(Comment node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitPatternPlaceholder(AstNode node, Pattern pattern, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitTypeReference(TypeReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitJavaTokenNode(JavaTokenNode node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitIdentifier(Identifier node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitNullReferenceExpression(NullReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitThisReferenceExpression(ThisReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSuperReferenceExpression(SuperReferenceExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitClassOfExpression(ClassOfExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitBlockStatement(BlockStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitExpressionStatement(ExpressionStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitBreakStatement(BreakStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitContinueStatement(ContinueStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitDoWhileStatement(DoWhileStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitEmptyStatement(EmptyStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitIfElseStatement(IfElseStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitLabelStatement(LabelStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitLabeledStatement(LabeledStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitReturnStatement(ReturnStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSwitchStatement(SwitchStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSwitchSection(SwitchSection node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitCaseLabel(CaseLabel node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitThrowStatement(ThrowStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitCatchClause(CatchClause node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitAnnotation(Annotation node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitNewLine(NewLineNode node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitVariableInitializer(VariableInitializer node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitText(TextNode node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitImportDeclaration(ImportDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitInitializerBlock(InstanceInitializer node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitCompilationUnit(CompilationUnit node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitPackageDeclaration(PackageDeclaration node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitArraySpecifier(ArraySpecifier node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitComposedType(ComposedType node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitWhileStatement(WhileStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitPrimitiveExpression(PrimitiveExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitCastExpression(CastExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitInstanceOfExpression(InstanceOfExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitIndexerExpression(IndexerExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitConditionalExpression(ConditionalExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitArrayInitializerExpression(ArrayInitializerExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitArrayCreationExpression(ArrayCreationExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitAssignmentExpression(AssignmentExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitForStatement(ForStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitTryCatchStatement(TryCatchStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitGotoStatement(GotoStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitParenthesizedExpression(ParenthesizedExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitSynchronizedStatement(SynchronizedStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitWildcardType(WildcardType node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitAssertStatement(AssertStatement node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitLambdaExpression(LambdaExpression node, SourceIndex index) { + return recurse(node, index); + } + + @Override + public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, SourceIndex index) { + return recurse(node, index); + } +} diff --git a/src/cuchaz/enigma/analysis/Token.java b/src/cuchaz/enigma/analysis/Token.java new file mode 100644 index 00000000..481d2f47 --- /dev/null +++ b/src/cuchaz/enigma/analysis/Token.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +public class Token implements Comparable { + + public int start; + public int end; + public String text; + + public Token(int start, int end) { + this(start, end, null); + } + + public Token(int start, int end, String source) { + this.start = start; + this.end = end; + if (source != null) { + this.text = source.substring(start, end); + } + } + + public boolean contains(int pos) { + return pos >= start && pos <= end; + } + + @Override + public int compareTo(Token other) { + return start - other.start; + } + + @Override + public boolean equals(Object other) { + if (other instanceof Token) { + return equals((Token)other); + } + return false; + } + + public boolean equals(Token other) { + return start == other.start && end == other.end; + } + + @Override + public String toString() { + return String.format("[%d,%d]", start, end); + } +} diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java new file mode 100644 index 00000000..7597c3ae --- /dev/null +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtField; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.Translator; + +public class TranslationIndex implements Serializable { + + private static final long serialVersionUID = 738687982126844179L; + + private Map m_superclasses; + private Multimap m_fieldEntries; + private Multimap m_behaviorEntries; + + public TranslationIndex() { + m_superclasses = Maps.newHashMap(); + m_fieldEntries = HashMultimap.create(); + m_behaviorEntries = HashMultimap.create(); + } + + public TranslationIndex(TranslationIndex other, Translator translator) { + + // translate the superclasses + m_superclasses = Maps.newHashMap(); + for (Map.Entry mapEntry : other.m_superclasses.entrySet()) { + m_superclasses.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + + // translate the fields + m_fieldEntries = HashMultimap.create(); + for (Map.Entry mapEntry : other.m_fieldEntries.entries()) { + m_fieldEntries.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + + m_behaviorEntries = HashMultimap.create(); + for (Map.Entry mapEntry : other.m_behaviorEntries.entries()) { + m_behaviorEntries.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + } + + public void indexClass(CtClass c) { + + ClassEntry classEntry = JavassistUtil.getClassEntry(c); + + // add the superclass + ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c); + if (!isJre(classEntry) && superclassEntry != null && !isJre(superclassEntry)) { + m_superclasses.put(classEntry, superclassEntry); + } + + // add fields + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); + } + + // add behaviors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior); + m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); + } + } + + public void renameClasses(Map renames) { + EntryRenamer.renameClassesInMap(renames, m_superclasses); + EntryRenamer.renameClassesInMultimap(renames, m_fieldEntries); + EntryRenamer.renameClassesInMultimap(renames, m_behaviorEntries); + } + + public ClassEntry getSuperclass(ClassEntry classEntry) { + return m_superclasses.get(classEntry); + } + + public List getAncestry(ClassEntry classEntry) { + List ancestors = Lists.newArrayList(); + while (classEntry != null) { + classEntry = getSuperclass(classEntry); + if (classEntry != null) { + ancestors.add(classEntry); + } + } + return ancestors; + } + + public List getSubclass(ClassEntry classEntry) { + // linear search is fast enough for now + List subclasses = Lists.newArrayList(); + for (Map.Entry entry : m_superclasses.entrySet()) { + ClassEntry subclass = entry.getKey(); + ClassEntry superclass = entry.getValue(); + if (classEntry.equals(superclass)) { + subclasses.add(subclass); + } + } + return subclasses; + } + + public void getSubclassesRecursively(Set out, ClassEntry classEntry) { + for (ClassEntry subclassEntry : getSubclass(classEntry)) { + out.add(subclassEntry); + getSubclassesRecursively(out, subclassEntry); + } + } + + public void getSubclassNamesRecursively(Set out, ClassEntry classEntry) { + for (ClassEntry subclassEntry : getSubclass(classEntry)) { + out.add(subclassEntry.getName()); + getSubclassNamesRecursively(out, subclassEntry); + } + } + + public boolean entryExists(Entry entry) { + if (entry instanceof FieldEntry) { + return fieldExists((FieldEntry)entry); + } else if (entry instanceof BehaviorEntry) { + return behaviorExists((BehaviorEntry)entry); + } else if (entry instanceof ArgumentEntry) { + return behaviorExists(((ArgumentEntry)entry).getBehaviorEntry()); + } + throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); + } + + public boolean fieldExists(FieldEntry fieldEntry) { + return m_fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); + } + + public boolean behaviorExists(BehaviorEntry behaviorEntry) { + return m_behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); + } + + public ClassEntry resolveEntryClass(Entry entry) { + + if (entry instanceof ClassEntry) { + return (ClassEntry)entry; + } + + // this entry could refer to a method on a class where the method is not actually implemented + // travel up the inheritance tree to find the closest implementation + while (!entryExists(entry)) { + + // is there a parent class? + ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); + if (superclassEntry == null) { + // this is probably a method from a class in a library + // we can't trace the implementation up any higher unless we index the library + return null; + } + + // move up to the parent class + entry = entry.cloneToNewClass(superclassEntry); + } + return entry.getClassEntry(); + } + + private boolean isJre(ClassEntry classEntry) { + String packageName = classEntry.getPackageName(); + return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); + } + + public void write(OutputStream out) + throws IOException { + GZIPOutputStream gzipout = new GZIPOutputStream(out); + ObjectOutputStream oout = new ObjectOutputStream(gzipout); + oout.writeObject(m_superclasses); + oout.writeObject(m_fieldEntries); + oout.writeObject(m_behaviorEntries); + gzipout.finish(); + } + + @SuppressWarnings("unchecked") + public void read(InputStream in) + throws IOException { + try { + ObjectInputStream oin = new ObjectInputStream(new GZIPInputStream(in)); + m_superclasses = (HashMap)oin.readObject(); + m_fieldEntries = (HashMultimap)oin.readObject(); + m_behaviorEntries = (HashMultimap)oin.readObject(); + } catch (ClassNotFoundException ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java new file mode 100644 index 00000000..23f80899 --- /dev/null +++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -0,0 +1,512 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; + +import com.strobel.componentmodel.Key; +import com.strobel.decompiler.languages.java.ast.Annotation; +import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; +import com.strobel.decompiler.languages.java.ast.ArraySpecifier; +import com.strobel.decompiler.languages.java.ast.AssertStatement; +import com.strobel.decompiler.languages.java.ast.AssignmentExpression; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.BinaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.BlockStatement; +import com.strobel.decompiler.languages.java.ast.BreakStatement; +import com.strobel.decompiler.languages.java.ast.CaseLabel; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.CatchClause; +import com.strobel.decompiler.languages.java.ast.ClassOfExpression; +import com.strobel.decompiler.languages.java.ast.Comment; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; +import com.strobel.decompiler.languages.java.ast.ComposedType; +import com.strobel.decompiler.languages.java.ast.ConditionalExpression; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.ContinueStatement; +import com.strobel.decompiler.languages.java.ast.DoWhileStatement; +import com.strobel.decompiler.languages.java.ast.EmptyStatement; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.ExpressionStatement; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.ForEachStatement; +import com.strobel.decompiler.languages.java.ast.ForStatement; +import com.strobel.decompiler.languages.java.ast.GotoStatement; +import com.strobel.decompiler.languages.java.ast.IAstVisitor; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.IfElseStatement; +import com.strobel.decompiler.languages.java.ast.ImportDeclaration; +import com.strobel.decompiler.languages.java.ast.IndexerExpression; +import com.strobel.decompiler.languages.java.ast.InstanceInitializer; +import com.strobel.decompiler.languages.java.ast.InstanceOfExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.JavaTokenNode; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.LabelStatement; +import com.strobel.decompiler.languages.java.ast.LabeledStatement; +import com.strobel.decompiler.languages.java.ast.LambdaExpression; +import com.strobel.decompiler.languages.java.ast.LocalTypeDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.MethodGroupExpression; +import com.strobel.decompiler.languages.java.ast.NewLineNode; +import com.strobel.decompiler.languages.java.ast.NullReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.PackageDeclaration; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.ParenthesizedExpression; +import com.strobel.decompiler.languages.java.ast.PrimitiveExpression; +import com.strobel.decompiler.languages.java.ast.ReturnStatement; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.SwitchSection; +import com.strobel.decompiler.languages.java.ast.SwitchStatement; +import com.strobel.decompiler.languages.java.ast.SynchronizedStatement; +import com.strobel.decompiler.languages.java.ast.TextNode; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThrowStatement; +import com.strobel.decompiler.languages.java.ast.TryCatchStatement; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; +import com.strobel.decompiler.languages.java.ast.UnaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; +import com.strobel.decompiler.languages.java.ast.WhileStatement; +import com.strobel.decompiler.languages.java.ast.WildcardType; +import com.strobel.decompiler.patterns.Pattern; + +public class TreeDumpVisitor implements IAstVisitor { + + private File m_file; + private Writer m_out; + + public TreeDumpVisitor(File file) { + m_file = file; + m_out = null; + } + + @Override + public Void visitCompilationUnit(CompilationUnit node, Void ignored) { + try { + m_out = new FileWriter(m_file); + recurse(node, ignored); + m_out.close(); + return null; + } catch (IOException ex) { + throw new Error(ex); + } + } + + private Void recurse(AstNode node, Void ignored) { + // show the tree + try { + m_out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n"); + } catch (IOException ex) { + throw new Error(ex); + } + + // recurse + for (final AstNode child : node.getChildren()) { + child.acceptVisitor(this, ignored); + } + return null; + } + + private String getText(AstNode node) { + if (node instanceof Identifier) { + return "\"" + ((Identifier)node).getName() + "\""; + } + return ""; + } + + private String dumpUserData(AstNode node) { + StringBuilder buf = new StringBuilder(); + for (Key key : Keys.ALL_KEYS) { + Object val = node.getUserData(key); + if (val != null) { + buf.append(String.format(" [%s=%s]", key, val)); + } + } + return buf.toString(); + } + + private String getIndent(AstNode node) { + StringBuilder buf = new StringBuilder(); + int depth = getDepth(node); + for (int i = 0; i < depth; i++) { + buf.append("\t"); + } + return buf.toString(); + } + + private int getDepth(AstNode node) { + int depth = -1; + while (node != null) { + depth++; + node = node.getParent(); + } + return depth; + } + + // OVERRIDES WE DON'T CARE ABOUT + + @Override + public Void visitInvocationExpression(InvocationExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitMemberReferenceExpression(MemberReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSimpleType(SimpleType node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitMethodDeclaration(MethodDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitParameterDeclaration(ParameterDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitFieldDeclaration(FieldDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitTypeDeclaration(TypeDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitComment(Comment node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitPatternPlaceholder(AstNode node, Pattern pattern, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitTypeReference(TypeReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitJavaTokenNode(JavaTokenNode node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitIdentifier(Identifier node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitNullReferenceExpression(NullReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitThisReferenceExpression(ThisReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSuperReferenceExpression(SuperReferenceExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitClassOfExpression(ClassOfExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitBlockStatement(BlockStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitExpressionStatement(ExpressionStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitBreakStatement(BreakStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitContinueStatement(ContinueStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitDoWhileStatement(DoWhileStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitEmptyStatement(EmptyStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitIfElseStatement(IfElseStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitLabelStatement(LabelStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitLabeledStatement(LabeledStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitReturnStatement(ReturnStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSwitchStatement(SwitchStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSwitchSection(SwitchSection node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitCaseLabel(CaseLabel node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitThrowStatement(ThrowStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitCatchClause(CatchClause node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitAnnotation(Annotation node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitNewLine(NewLineNode node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitVariableDeclaration(VariableDeclarationStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitVariableInitializer(VariableInitializer node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitText(TextNode node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitImportDeclaration(ImportDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitInitializerBlock(InstanceInitializer node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitPackageDeclaration(PackageDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitArraySpecifier(ArraySpecifier node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitComposedType(ComposedType node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitWhileStatement(WhileStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitPrimitiveExpression(PrimitiveExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitCastExpression(CastExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitInstanceOfExpression(InstanceOfExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitIndexerExpression(IndexerExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitIdentifierExpression(IdentifierExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitConditionalExpression(ConditionalExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitArrayInitializerExpression(ArrayInitializerExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitObjectCreationExpression(ObjectCreationExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitArrayCreationExpression(ArrayCreationExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitAssignmentExpression(AssignmentExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitForStatement(ForStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitForEachStatement(ForEachStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitTryCatchStatement(TryCatchStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitGotoStatement(GotoStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitParenthesizedExpression(ParenthesizedExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitSynchronizedStatement(SynchronizedStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitWildcardType(WildcardType node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitMethodGroupExpression(MethodGroupExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitAssertStatement(AssertStatement node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitLambdaExpression(LambdaExpression node, Void ignored) { + return recurse(node, ignored); + } + + @Override + public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, Void ignored) { + return recurse(node, ignored); + } +} diff --git a/src/cuchaz/enigma/bytecode/CheckCastIterator.java b/src/cuchaz/enigma/bytecode/CheckCastIterator.java new file mode 100644 index 00000000..52845573 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/CheckCastIterator.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Iterator; + +import javassist.bytecode.BadBytecode; +import javassist.bytecode.CodeAttribute; +import javassist.bytecode.CodeIterator; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.Opcode; +import cuchaz.enigma.bytecode.CheckCastIterator.CheckCast; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Signature; + +public class CheckCastIterator implements Iterator { + + public static class CheckCast { + + public String className; + public MethodEntry prevMethodEntry; + + public CheckCast(String className, MethodEntry prevMethodEntry) { + this.className = className; + this.prevMethodEntry = prevMethodEntry; + } + } + + private ConstPool m_constants; + private CodeAttribute m_attribute; + private CodeIterator m_iter; + private CheckCast m_next; + + public CheckCastIterator(CodeAttribute codeAttribute) throws BadBytecode { + m_constants = codeAttribute.getConstPool(); + m_attribute = codeAttribute; + m_iter = m_attribute.iterator(); + + m_next = getNext(); + } + + @Override + public boolean hasNext() { + return m_next != null; + } + + @Override + public CheckCast next() { + CheckCast out = m_next; + try { + m_next = getNext(); + } catch (BadBytecode ex) { + throw new Error(ex); + } + return out; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + private CheckCast getNext() throws BadBytecode { + int prevPos = 0; + while (m_iter.hasNext()) { + int pos = m_iter.next(); + int opcode = m_iter.byteAt(pos); + switch (opcode) { + case Opcode.CHECKCAST: + + // get the type of this op code (next two bytes are a classinfo index) + MethodEntry prevMethodEntry = getMethodEntry(prevPos); + if (prevMethodEntry != null) { + return new CheckCast(m_constants.getClassInfo(m_iter.s16bitAt(pos + 1)), prevMethodEntry); + } + break; + } + prevPos = pos; + } + return null; + } + + private MethodEntry getMethodEntry(int pos) { + switch (m_iter.byteAt(pos)) { + case Opcode.INVOKEVIRTUAL: + case Opcode.INVOKESTATIC: + case Opcode.INVOKEDYNAMIC: + case Opcode.INVOKESPECIAL: { + int index = m_iter.s16bitAt(pos + 1); + return new MethodEntry( + new ClassEntry(Descriptor.toJvmName(m_constants.getMethodrefClassName(index))), + m_constants.getMethodrefName(index), + new Signature(m_constants.getMethodrefType(index)) + ); + } + + case Opcode.INVOKEINTERFACE: { + int index = m_iter.s16bitAt(pos + 1); + return new MethodEntry( + new ClassEntry(Descriptor.toJvmName(m_constants.getInterfaceMethodrefClassName(index))), + m_constants.getInterfaceMethodrefName(index), + new Signature(m_constants.getInterfaceMethodrefType(index)) + ); + } + } + return null; + } + + public Iterable casts() { + return new Iterable() { + @Override + public Iterator iterator() { + return CheckCastIterator.this; + } + }; + } +} diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java new file mode 100644 index 00000000..a5fea926 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Map; +import java.util.Set; + +import javassist.ClassMap; +import javassist.CtClass; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.InnerClassesAttribute; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ClassEntry; + +public class ClassRenamer { + + public static void renameClasses(CtClass c, Map map) { + + // build the map used by javassist + ClassMap nameMap = new ClassMap(); + for (Map.Entry entry : map.entrySet()) { + nameMap.put(entry.getKey().getName(), entry.getValue().getName()); + } + + c.replaceClassName(nameMap); + + // replace simple names in the InnerClasses attribute too + ConstPool constants = c.getClassFile().getConstPool(); + InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (attr != null) { + for (int i = 0; i < attr.tableLength(); i++) { + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(attr.innerClass(i))); + if (attr.innerNameIndex(i) != 0) { + attr.setInnerNameIndex(i, constants.addUtf8Info(classEntry.getInnerClassName())); + } + + /* DEBUG + System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); + */ + } + } + } + + public static Set getAllClassEntries(final CtClass c) { + + // get the classes that javassist knows about + final Set entries = Sets.newHashSet(); + ClassMap map = new ClassMap() { + @Override + public Object get(Object obj) { + if (obj instanceof String) { + String str = (String)obj; + + // javassist throws a lot of weird things at this map + // I either have to implement my on class scanner, or just try to filter out the weirdness + // I'm opting to filter out the weirdness for now + + // skip anything with generic arguments + if (str.indexOf('<') >= 0 || str.indexOf('>') >= 0 || str.indexOf(';') >= 0) { + return null; + } + + // convert path/to/class.inner to path/to/class$inner + str = str.replace('.', '$'); + + // remember everything else + entries.add(new ClassEntry(str)); + } + return null; + } + + private static final long serialVersionUID = -202160293602070641L; + }; + c.replaceClassName(map); + + return entries; + } + + public static void moveAllClassesOutOfDefaultPackage(CtClass c, String newPackageName) { + Map map = Maps.newHashMap(); + for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { + if (classEntry.isInDefaultPackage()) { + map.put(classEntry, new ClassEntry(newPackageName + "/" + classEntry.getName())); + } + } + ClassRenamer.renameClasses(c, map); + } + + public static void moveAllClassesIntoDefaultPackage(CtClass c, String oldPackageName) { + Map map = Maps.newHashMap(); + for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { + if (classEntry.getPackageName().equals(oldPackageName)) { + map.put(classEntry, new ClassEntry(classEntry.getSimpleName())); + } + } + ClassRenamer.renameClasses(c, map); + } +} diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java new file mode 100644 index 00000000..afd3a777 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Map; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtField; +import javassist.CtMethod; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.SourceFileAttribute; + +import com.google.common.collect.Maps; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Signature; +import cuchaz.enigma.mapping.Translator; +import cuchaz.enigma.mapping.Type; + +public class ClassTranslator { + + private Translator m_translator; + + public ClassTranslator(Translator translator) { + m_translator = translator; + } + + public void translate(CtClass c) { + + // NOTE: the order of these translations is very important + + // translate all the field and method references in the code by editing the constant pool + ConstPool constants = c.getClassFile().getConstPool(); + ConstPoolEditor editor = new ConstPoolEditor(constants); + for (int i = 1; i < constants.getSize(); i++) { + switch (constants.getTag(i)) { + + case ConstPool.CONST_Fieldref: { + + // translate the name + FieldEntry entry = new FieldEntry( + new ClassEntry(Descriptor.toJvmName(constants.getFieldrefClassName(i))), + constants.getFieldrefName(i) + ); + FieldEntry translatedEntry = m_translator.translateEntry(entry); + + // translate the type + Type type = new Type(constants.getFieldrefType(i)); + Type translatedType = m_translator.translateType(type); + + if (!entry.equals(translatedEntry) || !type.equals(translatedType)) { + editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedType.toString()); + } + } + break; + + case ConstPool.CONST_Methodref: + case ConstPool.CONST_InterfaceMethodref: { + + // translate the name and type + BehaviorEntry entry = BehaviorEntryFactory.create( + Descriptor.toJvmName(editor.getMemberrefClassname(i)), + editor.getMemberrefName(i), + editor.getMemberrefType(i) + ); + BehaviorEntry translatedEntry = m_translator.translateEntry(entry); + + if (!entry.getName().equals(translatedEntry.getName()) || !entry.getSignature().equals(translatedEntry.getSignature())) { + editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); + } + } + break; + } + } + + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + + // translate all the fields + for (CtField field : c.getDeclaredFields()) { + + // translate the name + FieldEntry entry = new FieldEntry(classEntry, field.getName()); + String translatedName = m_translator.translate(entry); + if (translatedName != null) { + field.setName(translatedName); + } + + // translate the type + Type translatedType = m_translator.translateType(new Type(field.getFieldInfo().getDescriptor())); + field.getFieldInfo().setDescriptor(translatedType.toString()); + } + + // translate all the methods and constructors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + if (behavior instanceof CtMethod) { + CtMethod method = (CtMethod)behavior; + + // translate the name + MethodEntry entry = JavassistUtil.getMethodEntry(method); + String translatedName = m_translator.translate(entry); + if (translatedName != null) { + method.setName(translatedName); + } + } + + // translate the type + Signature translatedSignature = m_translator.translateSignature(new Signature(behavior.getMethodInfo().getDescriptor())); + behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); + } + + // translate all the class names referenced in the code + // the above code only changed method/field/reference names and types, but not the class names themselves + Map map = Maps.newHashMap(); + for (ClassEntry obfClassEntry : ClassRenamer.getAllClassEntries(c)) { + ClassEntry deobfClassEntry = m_translator.translateEntry(obfClassEntry); + if (!obfClassEntry.equals(deobfClassEntry)) { + map.put(obfClassEntry, deobfClassEntry); + } + } + ClassRenamer.renameClasses(c, map); + + // translate the source file attribute too + ClassEntry deobfClassEntry = map.get(classEntry); + if (deobfClassEntry != null) { + String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOuterClassName()) + ".java"; + c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java new file mode 100644 index 00000000..2dec3b76 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java @@ -0,0 +1,263 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; + +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor; +import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; +import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; + +public class ConstPoolEditor { + + private static Method m_getItem; + private static Method m_addItem; + private static Method m_addItem0; + private static Field m_items; + private static Field m_cache; + private static Field m_numItems; + private static Field m_objects; + private static Field m_elements; + private static Method m_methodWritePool; + private static Constructor m_constructorPool; + + static { + try { + m_getItem = ConstPool.class.getDeclaredMethod("getItem", int.class); + m_getItem.setAccessible(true); + + m_addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo")); + m_addItem.setAccessible(true); + + m_addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo")); + m_addItem0.setAccessible(true); + + m_items = ConstPool.class.getDeclaredField("items"); + m_items.setAccessible(true); + + m_cache = ConstPool.class.getDeclaredField("itemsCache"); + m_cache.setAccessible(true); + + m_numItems = ConstPool.class.getDeclaredField("numOfItems"); + m_numItems.setAccessible(true); + + m_objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects"); + m_objects.setAccessible(true); + + m_elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements"); + m_elements.setAccessible(true); + + m_methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class); + m_methodWritePool.setAccessible(true); + + m_constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class); + m_constructorPool.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + private ConstPool m_pool; + + public ConstPoolEditor(ConstPool pool) { + m_pool = pool; + } + + public void writePool(DataOutputStream out) { + try { + m_methodWritePool.invoke(m_pool, out); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static ConstPool readPool(DataInputStream in) { + try { + return m_constructorPool.newInstance(in); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public String getMemberrefClassname(int memberrefIndex) { + return Descriptor.toJvmName(m_pool.getClassInfo(m_pool.getMemberClass(memberrefIndex))); + } + + public String getMemberrefName(int memberrefIndex) { + return m_pool.getUtf8Info(m_pool.getNameAndTypeName(m_pool.getMemberNameAndType(memberrefIndex))); + } + + public String getMemberrefType(int memberrefIndex) { + return m_pool.getUtf8Info(m_pool.getNameAndTypeDescriptor(m_pool.getMemberNameAndType(memberrefIndex))); + } + + public ConstInfoAccessor getItem(int index) { + try { + Object entry = m_getItem.invoke(m_pool, index); + if (entry == null) { + return null; + } + return new ConstInfoAccessor(entry); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int addItem(Object item) { + try { + return (Integer)m_addItem.invoke(m_pool, item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int addItemForceNew(Object item) { + try { + return (Integer)m_addItem0.invoke(m_pool, item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + @SuppressWarnings("rawtypes") + public void removeLastItem() { + try { + // remove the item from the cache + HashMap cache = getCache(); + if (cache != null) { + Object item = getItem(m_pool.getSize() - 1); + cache.remove(item); + } + + // remove the actual item + // based off of LongVector.addElement() + Object items = m_items.get(m_pool); + Object[][] objects = (Object[][])m_objects.get(items); + int numElements = (Integer)m_elements.get(items) - 1; + int nth = numElements >> 7; + int offset = numElements & (128 - 1); + objects[nth][offset] = null; + + // decrement the number of items + m_elements.set(items, numElements); + m_numItems.set(m_pool, (Integer)m_numItems.get(m_pool) - 1); + } catch (Exception ex) { + throw new Error(ex); + } + } + + @SuppressWarnings("rawtypes") + public HashMap getCache() { + try { + return (HashMap)m_cache.get(m_pool); + } catch (Exception ex) { + throw new Error(ex); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) { + // NOTE: when changing values, we always need to copy-on-write + try { + // get the memberref item + Object item = getItem(memberrefIndex).getItem(); + + // update the cache + HashMap cache = getCache(); + if (cache != null) { + cache.remove(item); + } + + new MemberRefInfoAccessor(item).setNameAndTypeIndex(m_pool.addNameAndTypeInfo(newName, newType)); + + // update the cache + if (cache != null) { + cache.put(item, item); + } + } catch (Exception ex) { + throw new Error(ex); + } + + // make sure the change worked + assert (newName.equals(getMemberrefName(memberrefIndex))); + assert (newType.equals(getMemberrefType(memberrefIndex))); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void changeClassName(int classNameIndex, String newName) { + // NOTE: when changing values, we always need to copy-on-write + try { + // get the class item + Object item = getItem(classNameIndex).getItem(); + + // update the cache + HashMap cache = getCache(); + if (cache != null) { + cache.remove(item); + } + + // add the new name and repoint the name-and-type to it + new ClassInfoAccessor(item).setNameIndex(m_pool.addUtf8Info(newName)); + + // update the cache + if (cache != null) { + cache.put(item, item); + } + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static ConstPool newConstPool() { + // const pool expects the name of a class to initialize itself + // but we want an empty pool + // so give it a bogus name, and then clear the entries afterwards + ConstPool pool = new ConstPool("a"); + + ConstPoolEditor editor = new ConstPoolEditor(pool); + int size = pool.getSize(); + for (int i = 0; i < size - 1; i++) { + editor.removeLastItem(); + } + + // make sure the pool is actually empty + // although, in this case "empty" means one thing in it + // the JVM spec says index 0 should be reserved + assert (pool.getSize() == 1); + assert (editor.getItem(0) == null); + assert (editor.getItem(1) == null); + assert (editor.getItem(2) == null); + assert (editor.getItem(3) == null); + + // also, clear the cache + editor.getCache().clear(); + + return pool; + } + + public String dump() { + StringBuilder buf = new StringBuilder(); + for (int i = 1; i < m_pool.getSize(); i++) { + buf.append(String.format("%4d", i)); + buf.append(" "); + buf.append(getItem(i).toString()); + buf.append("\n"); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/bytecode/InfoType.java b/src/cuchaz/enigma/bytecode/InfoType.java new file mode 100644 index 00000000..deaf6232 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/InfoType.java @@ -0,0 +1,317 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor; +import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; +import cuchaz.enigma.bytecode.accessors.InvokeDynamicInfoAccessor; +import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; +import cuchaz.enigma.bytecode.accessors.MethodHandleInfoAccessor; +import cuchaz.enigma.bytecode.accessors.MethodTypeInfoAccessor; +import cuchaz.enigma.bytecode.accessors.NameAndTypeInfoAccessor; +import cuchaz.enigma.bytecode.accessors.StringInfoAccessor; + +public enum InfoType { + + Utf8Info( 1, 0 ), + IntegerInfo( 3, 0 ), + FloatInfo( 4, 0 ), + LongInfo( 5, 0 ), + DoubleInfo( 6, 0 ), + ClassInfo( 7, 1 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getNameIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); + accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); + ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); + return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag(); + } + }, + StringInfo( 8, 1 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getStringIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); + accessor.setStringIndex(remapIndex(map, accessor.getStringIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); + ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex()); + return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag(); + } + }, + FieldRefInfo( 9, 2 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getClassIndex()); + gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); + accessor.setClassIndex(remapIndex(map, accessor.getClassIndex())); + accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); + ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex()); + ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); + return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); + } + }, + // same as FieldRefInfo + MethodRefInfo( 10, 2 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + FieldRefInfo.gatherIndexTree(indices, editor, entry); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + FieldRefInfo.remapIndices(map, entry); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + return FieldRefInfo.subIndicesAreValid(entry, pool); + } + }, + // same as FieldRefInfo + InterfaceMethodRefInfo( 11, 2 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + FieldRefInfo.gatherIndexTree(indices, editor, entry); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + FieldRefInfo.remapIndices(map, entry); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + return FieldRefInfo.subIndicesAreValid(entry, pool); + } + }, + NameAndTypeInfo( 12, 1 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getNameIndex()); + gatherIndexTree(indices, editor, accessor.getTypeIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); + accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); + accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); + ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); + ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); + return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); + } + }, + MethodHandleInfo( 15, 3 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getTypeIndex()); + gatherIndexTree(indices, editor, accessor.getMethodRefIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); + accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); + accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); + ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); + ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex()); + return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag(); + } + }, + MethodTypeInfo( 16, 1 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getTypeIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); + accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); + ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); + return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); + } + }, + InvokeDynamicInfo( 18, 2 ) { + + @Override + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); + gatherIndexTree(indices, editor, accessor.getBootstrapIndex()); + gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); + } + + @Override + public void remapIndices(Map map, ConstInfoAccessor entry) { + InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); + accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex())); + accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); + } + + @Override + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); + ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex()); + ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); + return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); + } + }; + + private static Map m_types; + + static { + m_types = Maps.newTreeMap(); + for (InfoType type : values()) { + m_types.put(type.getTag(), type); + } + } + + private int m_tag; + private int m_level; + + private InfoType(int tag, int level) { + m_tag = tag; + m_level = level; + } + + public int getTag() { + return m_tag; + } + + public int getLevel() { + return m_level; + } + + public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { + // by default, do nothing + } + + public void remapIndices(Map map, ConstInfoAccessor entry) { + // by default, do nothing + } + + public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + // by default, everything is good + return true; + } + + public boolean selfIndexIsValid(ConstInfoAccessor entry, ConstPoolEditor pool) { + ConstInfoAccessor entryCheck = pool.getItem(entry.getIndex()); + if (entryCheck == null) { + return false; + } + return entryCheck.getItem().equals(entry.getItem()); + } + + public static InfoType getByTag(int tag) { + return m_types.get(tag); + } + + public static List getByLevel(int level) { + List types = Lists.newArrayList(); + for (InfoType type : values()) { + if (type.getLevel() == level) { + types.add(type); + } + } + return types; + } + + public static List getSortedByLevel() { + List types = Lists.newArrayList(); + types.addAll(getByLevel(0)); + types.addAll(getByLevel(1)); + types.addAll(getByLevel(2)); + types.addAll(getByLevel(3)); + return types; + } + + public static void gatherIndexTree(Collection indices, ConstPoolEditor editor, int index) { + // add own index + indices.add(index); + + // recurse + ConstInfoAccessor entry = editor.getItem(index); + entry.getType().gatherIndexTree(indices, editor, entry); + } + + private static int remapIndex(Map map, int index) { + Integer newIndex = map.get(index); + if (newIndex == null) { + newIndex = index; + } + return newIndex; + } +} diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java new file mode 100644 index 00000000..817500b7 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Collection; + +import javassist.CtClass; +import javassist.bytecode.AccessFlag; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.EnclosingMethodAttribute; +import javassist.bytecode.InnerClassesAttribute; +import cuchaz.enigma.Constants; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; + +public class InnerClassWriter { + + private JarIndex m_jarIndex; + + public InnerClassWriter(JarIndex jarIndex) { + m_jarIndex = jarIndex; + } + + public void write(CtClass c) { + + // is this an inner or outer class? + String obfInnerClassName = new ClassEntry(Descriptor.toJvmName(c.getName())).getSimpleName(); + String obfOuterClassName = m_jarIndex.getOuterClass(obfInnerClassName); + if (obfOuterClassName == null) { + // this is an outer class + obfOuterClassName = Descriptor.toJvmName(c.getName()); + } else { + // this is an inner class, rename it to outer$inner + ClassEntry obfClassEntry = new ClassEntry(obfOuterClassName + "$" + obfInnerClassName); + c.setName(obfClassEntry.getName()); + + BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller(obfInnerClassName); + if (caller != null) { + // write the enclosing method attribute + if (caller.getName().equals("")) { + c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); + } else { + c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString())); + } + } + } + + // write the inner classes if needed + Collection obfInnerClassNames = m_jarIndex.getInnerClasses(obfOuterClassName); + if (obfInnerClassNames != null && !obfInnerClassNames.isEmpty()) { + writeInnerClasses(c, obfOuterClassName, obfInnerClassNames); + } + } + + private void writeInnerClasses(CtClass c, String obfOuterClassName, Collection obfInnerClassNames) { + InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); + c.getClassFile().addAttribute(attr); + for (String obfInnerClassName : obfInnerClassNames) { + // get the new inner class name + ClassEntry obfClassEntry = new ClassEntry(obfOuterClassName + "$" + obfInnerClassName); + + // here's what the JVM spec says about the InnerClasses attribute + // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); + + // update the attribute with this inner class + ConstPool constPool = c.getClassFile().getConstPool(); + int innerClassIndex = constPool.addClassInfo(obfClassEntry.getName()); + int outerClassIndex = 0; + int innerClassSimpleNameIndex = 0; + if (!m_jarIndex.isAnonymousClass(obfInnerClassName)) { + outerClassIndex = constPool.addClassInfo(obfClassEntry.getOuterClassName()); + innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getInnerClassName()); + } + + attr.append(innerClassIndex, outerClassIndex, innerClassSimpleNameIndex, c.getClassFile().getAccessFlags() & ~AccessFlag.SUPER); + + /* DEBUG + System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", + obfClassEntry, + attr.outerClass(attr.tableLength() - 1), + attr.innerClass(attr.tableLength() - 1), + attr.innerName(attr.tableLength() - 1), + Constants.NonePackage + "/" + obfInnerClassName, + obfClassEntry.getName() + )); + */ + + // make sure the outer class references only the new inner class names + c.replaceClassName(Constants.NonePackage + "/" + obfInnerClassName, obfClassEntry.getName()); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java new file mode 100644 index 00000000..5d4ca1ad --- /dev/null +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.ArrayList; +import java.util.List; + +import javassist.CtBehavior; +import javassist.CtClass; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.Translator; + +public class MethodParameterWriter { + + private Translator m_translator; + + public MethodParameterWriter(Translator translator) { + m_translator = translator; + } + + public void writeMethodArguments(CtClass c) { + + // Procyon will read method arguments from the "MethodParameters" attribute, so write those + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + + // get the number of arguments + int numParams = behaviorEntry.getSignature().getArgumentTypes().size(); + if (numParams <= 0) { + continue; + } + + // get the list of argument names + List names = new ArrayList(numParams); + for (int i = 0; i < numParams; i++) { + names.add(m_translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); + } + + // save the mappings to the class + MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java new file mode 100644 index 00000000..bf959564 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javassist.bytecode.AttributeInfo; +import javassist.bytecode.ConstPool; +import javassist.bytecode.MethodInfo; + +public class MethodParametersAttribute extends AttributeInfo { + + private MethodParametersAttribute(ConstPool pool, List parameterNameIndices) { + super(pool, "MethodParameters", writeStruct(parameterNameIndices)); + } + + public static void updateClass(MethodInfo info, List names) { + // add the names to the class const pool + ConstPool constPool = info.getConstPool(); + List parameterNameIndices = new ArrayList(); + for (String name : names) { + if (name != null) { + parameterNameIndices.add(constPool.addUtf8Info(name)); + } else { + parameterNameIndices.add(0); + } + } + + // add the attribute to the method + info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices)); + } + + private static byte[] writeStruct(List parameterNameIndices) { + // JVM 8 Spec says the struct looks like this: + // http://cr.openjdk.java.net/~mr/se/8/java-se-8-fr-spec-01/java-se-8-jvms-fr-diffs.pdf + // uint8 num_params + // for each param: + // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry + // uint16 access_flags -> don't care, just set to 0 + + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(buf); + + // NOTE: java hates unsigned integers, so we have to be careful here + // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument + // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte + // if the int is out of range, the byte stream won't look the way we want and weird things will happen + final int SIZEOF_UINT8 = 1; + final int SIZEOF_UINT16 = 2; + final int MAX_UINT8 = (1 << 8) - 1; + final int MAX_UINT16 = (1 << 16) - 1; + + try { + assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8); + out.writeByte(parameterNameIndices.size()); + + for (Integer index : parameterNameIndices) { + assert (index >= 0 && index <= MAX_UINT16); + out.writeShort(index); + + // just write 0 for the access flags + out.writeShort(0); + } + + out.close(); + byte[] data = buf.toByteArray(); + assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16)); + return data; + } catch (IOException ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java new file mode 100644 index 00000000..d76f0567 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class ClassInfoAccessor { + + private static Class m_class; + private static Field m_nameIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.ClassInfo"); + m_nameIndex = m_class.getDeclaredField("name"); + m_nameIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public ClassInfoAccessor(Object item) { + m_item = item; + } + + public int getNameIndex() { + try { + return (Integer)m_nameIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setNameIndex(int val) { + try { + m_nameIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java new file mode 100644 index 00000000..d00c1021 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import cuchaz.enigma.bytecode.InfoType; + +public class ConstInfoAccessor { + + private static Class m_class; + private static Field m_index; + private static Method m_getTag; + + static { + try { + m_class = Class.forName("javassist.bytecode.ConstInfo"); + m_index = m_class.getDeclaredField("index"); + m_index.setAccessible(true); + m_getTag = m_class.getMethod("getTag"); + m_getTag.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + private Object m_item; + + public ConstInfoAccessor(Object item) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null!"); + } + m_item = item; + } + + public ConstInfoAccessor(DataInputStream in) throws IOException { + try { + // read the entry + String className = in.readUTF(); + int oldIndex = in.readInt(); + + // NOTE: ConstInfo instances write a type id (a "tag"), but they don't read it back + // so we have to read it here + in.readByte(); + + Constructor constructor = Class.forName(className).getConstructor(DataInputStream.class, int.class); + constructor.setAccessible(true); + m_item = constructor.newInstance(in, oldIndex); + } catch (IOException ex) { + throw ex; + } catch (Exception ex) { + throw new Error(ex); + } + } + + public Object getItem() { + return m_item; + } + + public int getIndex() { + try { + return (Integer)m_index.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setIndex(int val) { + try { + m_index.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int getTag() { + try { + return (Integer)m_getTag.invoke(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public ConstInfoAccessor copy() { + return new ConstInfoAccessor(copyItem()); + } + + public Object copyItem() { + // I don't know of a simpler way to copy one of these silly things... + try { + // serialize the item + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(buf); + write(out); + + // deserialize the item + DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray())); + Object item = new ConstInfoAccessor(in).getItem(); + in.close(); + + return item; + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void write(DataOutputStream out) throws IOException { + try { + out.writeUTF(m_item.getClass().getName()); + out.writeInt(getIndex()); + + Method method = m_item.getClass().getMethod("write", DataOutputStream.class); + method.setAccessible(true); + method.invoke(m_item, out); + } catch (IOException ex) { + throw ex; + } catch (Exception ex) { + throw new Error(ex); + } + } + + @Override + public String toString() { + try { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + PrintWriter out = new PrintWriter(buf); + Method print = m_item.getClass().getMethod("print", PrintWriter.class); + print.setAccessible(true); + print.invoke(m_item, out); + out.close(); + return buf.toString().replace("\n", ""); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public InfoType getType() { + return InfoType.getByTag(getTag()); + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java new file mode 100644 index 00000000..0d780ea6 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class InvokeDynamicInfoAccessor { + + private static Class m_class; + private static Field m_bootstrapIndex; + private static Field m_nameAndTypeIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.InvokeDynamicInfo"); + m_bootstrapIndex = m_class.getDeclaredField("bootstrap"); + m_bootstrapIndex.setAccessible(true); + m_nameAndTypeIndex = m_class.getDeclaredField("nameAndType"); + m_nameAndTypeIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public InvokeDynamicInfoAccessor(Object item) { + m_item = item; + } + + public int getBootstrapIndex() { + try { + return (Integer)m_bootstrapIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setBootstrapIndex(int val) { + try { + m_bootstrapIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int getNameAndTypeIndex() { + try { + return (Integer)m_nameAndTypeIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setNameAndTypeIndex(int val) { + try { + m_nameAndTypeIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java new file mode 100644 index 00000000..9fe945f7 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class MemberRefInfoAccessor { + + private static Class m_class; + private static Field m_classIndex; + private static Field m_nameAndTypeIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.MemberrefInfo"); + m_classIndex = m_class.getDeclaredField("classIndex"); + m_classIndex.setAccessible(true); + m_nameAndTypeIndex = m_class.getDeclaredField("nameAndTypeIndex"); + m_nameAndTypeIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public MemberRefInfoAccessor(Object item) { + m_item = item; + } + + public int getClassIndex() { + try { + return (Integer)m_classIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setClassIndex(int val) { + try { + m_classIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int getNameAndTypeIndex() { + try { + return (Integer)m_nameAndTypeIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setNameAndTypeIndex(int val) { + try { + m_nameAndTypeIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java new file mode 100644 index 00000000..4c95b226 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class MethodHandleInfoAccessor { + + private static Class m_class; + private static Field m_kindIndex; + private static Field m_indexIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.MethodHandleInfo"); + m_kindIndex = m_class.getDeclaredField("refKind"); + m_kindIndex.setAccessible(true); + m_indexIndex = m_class.getDeclaredField("refIndex"); + m_indexIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public MethodHandleInfoAccessor(Object item) { + m_item = item; + } + + public int getTypeIndex() { + try { + return (Integer)m_kindIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setTypeIndex(int val) { + try { + m_kindIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int getMethodRefIndex() { + try { + return (Integer)m_indexIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setMethodRefIndex(int val) { + try { + m_indexIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java new file mode 100644 index 00000000..e1511179 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class MethodTypeInfoAccessor { + + private static Class m_class; + private static Field m_descriptorIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.MethodTypeInfo"); + m_descriptorIndex = m_class.getDeclaredField("descriptor"); + m_descriptorIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public MethodTypeInfoAccessor(Object item) { + m_item = item; + } + + public int getTypeIndex() { + try { + return (Integer)m_descriptorIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setTypeIndex(int val) { + try { + m_descriptorIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java new file mode 100644 index 00000000..6e82f3e9 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class NameAndTypeInfoAccessor { + + private static Class m_class; + private static Field m_nameIndex; + private static Field m_typeIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.NameAndTypeInfo"); + m_nameIndex = m_class.getDeclaredField("memberName"); + m_nameIndex.setAccessible(true); + m_typeIndex = m_class.getDeclaredField("typeDescriptor"); + m_typeIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public NameAndTypeInfoAccessor(Object item) { + m_item = item; + } + + public int getNameIndex() { + try { + return (Integer)m_nameIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setNameIndex(int val) { + try { + m_nameIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public int getTypeIndex() { + try { + return (Integer)m_typeIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setTypeIndex(int val) { + try { + m_typeIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java new file mode 100644 index 00000000..6665ffe4 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +import java.lang.reflect.Field; + +public class StringInfoAccessor { + + private static Class m_class; + private static Field m_stringIndex; + + static { + try { + m_class = Class.forName("javassist.bytecode.StringInfo"); + m_stringIndex = m_class.getDeclaredField("string"); + m_stringIndex.setAccessible(true); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } + + private Object m_item; + + public StringInfoAccessor(Object item) { + m_item = item; + } + + public int getStringIndex() { + try { + return (Integer)m_stringIndex.get(m_item); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public void setStringIndex(int val) { + try { + m_stringIndex.set(m_item, val); + } catch (Exception ex) { + throw new Error(ex); + } + } +} diff --git a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java new file mode 100644 index 00000000..2abf60b4 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode.accessors; + +public class Utf8InfoAccessor { + + private static Class m_class; + + static { + try { + m_class = Class.forName("javassist.bytecode.Utf8Info"); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static boolean isType(ConstInfoAccessor accessor) { + return m_class.isAssignableFrom(accessor.getItem().getClass()); + } +} diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java new file mode 100644 index 00000000..1be61239 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -0,0 +1,411 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +import javassist.CannotCompileException; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.CtMethod; +import javassist.bytecode.BadBytecode; +import javassist.bytecode.CodeIterator; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.Opcode; +import javassist.expr.ConstructorCall; +import javassist.expr.ExprEditor; +import javassist.expr.FieldAccess; +import javassist.expr.MethodCall; +import javassist.expr.NewExpr; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multiset; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.Util; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.bytecode.ConstPoolEditor; +import cuchaz.enigma.bytecode.InfoType; +import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; +import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassNameReplacer; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.Signature; + +public class ClassIdentity { + + private ClassEntry m_classEntry; + private SidedClassNamer m_namer; + private Multiset m_fields; + private Multiset m_methods; + private Multiset m_constructors; + private String m_staticInitializer; + private String m_extends; + private Multiset m_implements; + private Multiset m_implementations; + private Multiset m_references; + + public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) { + m_namer = namer; + + // stuff from the bytecode + + m_classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + m_fields = HashMultiset.create(); + for (CtField field : c.getDeclaredFields()) { + m_fields.add(scrubSignature(field.getSignature())); + } + m_methods = HashMultiset.create(); + for (CtMethod method : c.getDeclaredMethods()) { + m_methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method)); + } + m_constructors = HashMultiset.create(); + for (CtConstructor constructor : c.getDeclaredConstructors()) { + m_constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor)); + } + m_staticInitializer = ""; + if (c.getClassInitializer() != null) { + m_staticInitializer = getBehaviorSignature(c.getClassInitializer()); + } + m_extends = ""; + if (c.getClassFile().getSuperclass() != null) { + m_extends = scrubClassName(c.getClassFile().getSuperclass()); + } + m_implements = HashMultiset.create(); + for (String interfaceName : c.getClassFile().getInterfaces()) { + m_implements.add(scrubClassName(interfaceName)); + } + + // stuff from the jar index + + m_implementations = HashMultiset.create(); + ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, m_classEntry); + if (implementationsNode != null) { + @SuppressWarnings("unchecked") + Enumeration implementations = implementationsNode.children(); + while (implementations.hasMoreElements()) { + ClassImplementationsTreeNode node = implementations.nextElement(); + m_implementations.add(scrubClassName(node.getClassEntry().getName())); + } + } + + m_references = HashMultiset.create(); + if (useReferences) { + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = new FieldEntry(m_classEntry, field.getName()); + for (EntryReference reference : index.getFieldReferences(fieldEntry)) { + addReference(reference); + } + } + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior); + for (EntryReference reference : index.getBehaviorReferences(behaviorEntry)) { + addReference(reference); + } + } + } + } + + private void addReference(EntryReference reference) { + if (reference.context.getSignature() != null) { + m_references.add(String.format("%s_%s", scrubClassName(reference.context.getClassName()), scrubSignature(reference.context.getSignature()))); + } else { + m_references.add(String.format("%s_", scrubClassName(reference.context.getClassName()))); + } + } + + public ClassEntry getClassEntry() { + return m_classEntry; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("class: "); + buf.append(m_classEntry.getName()); + buf.append(" "); + buf.append(hashCode()); + buf.append("\n"); + for (String field : m_fields) { + buf.append("\tfield "); + buf.append(field); + buf.append("\n"); + } + for (String method : m_methods) { + buf.append("\tmethod "); + buf.append(method); + buf.append("\n"); + } + for (String constructor : m_constructors) { + buf.append("\tconstructor "); + buf.append(constructor); + buf.append("\n"); + } + if (m_staticInitializer.length() > 0) { + buf.append("\tinitializer "); + buf.append(m_staticInitializer); + buf.append("\n"); + } + if (m_extends.length() > 0) { + buf.append("\textends "); + buf.append(m_extends); + buf.append("\n"); + } + for (String interfaceName : m_implements) { + buf.append("\timplements "); + buf.append(interfaceName); + buf.append("\n"); + } + for (String implementation : m_implementations) { + buf.append("\timplemented by "); + buf.append(implementation); + buf.append("\n"); + } + for (String reference : m_references) { + buf.append("\treference "); + buf.append(reference); + buf.append("\n"); + } + return buf.toString(); + } + + private String scrubClassName(String className) { + return scrubSignature("L" + Descriptor.toJvmName(className) + ";"); + } + + private String scrubSignature(String signature) { + return scrubSignature(new Signature(signature)); + } + + private String scrubSignature(Signature signature) { + + return new Signature(signature, new ClassNameReplacer() { + + private Map m_classNames = Maps.newHashMap(); + + @Override + public String replace(String className) { + + // classes not in the none package can be passed through + ClassEntry classEntry = new ClassEntry(className); + if (!classEntry.getPackageName().equals(Constants.NonePackage)) { + return className; + } + + // is this class ourself? + if (className.equals(m_classEntry.getName())) { + return "CSelf"; + } + + // try the namer + if (m_namer != null) { + String newName = m_namer.getName(className); + if (newName != null) { + return newName; + } + } + + // otherwise, use local naming + if (!m_classNames.containsKey(className)) { + m_classNames.put(className, getNewClassName()); + } + return m_classNames.get(className); + } + + private String getNewClassName() { + return String.format("C%03d", m_classNames.size()); + } + }).toString(); + } + + private boolean isClassMatchedUniquely(String className) { + return m_namer != null && m_namer.getName(Descriptor.toJvmName(className)) != null; + } + + private String getBehaviorSignature(CtBehavior behavior) { + try { + // does this method have an implementation? + if (behavior.getMethodInfo().getCodeAttribute() == null) { + return "(none)"; + } + + // compute the hash from the opcodes + ConstPool constants = behavior.getMethodInfo().getConstPool(); + final MessageDigest digest = MessageDigest.getInstance("MD5"); + CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); + while (iter.hasNext()) { + int pos = iter.next(); + + // update the hash with the opcode + int opcode = iter.byteAt(pos); + digest.update((byte)opcode); + + switch (opcode) { + case Opcode.LDC: { + int constIndex = iter.byteAt(pos + 1); + updateHashWithConstant(digest, constants, constIndex); + } + break; + + case Opcode.LDC_W: + case Opcode.LDC2_W: { + int constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2); + updateHashWithConstant(digest, constants, constIndex); + } + break; + } + } + + // update hash with method and field accesses + behavior.instrument(new ExprEditor() { + @Override + public void edit(MethodCall call) { + updateHashWithString(digest, scrubClassName(call.getClassName())); + updateHashWithString(digest, scrubSignature(call.getSignature())); + if (isClassMatchedUniquely(call.getClassName())) { + updateHashWithString(digest, call.getMethodName()); + } + } + + @Override + public void edit(FieldAccess access) { + updateHashWithString(digest, scrubClassName(access.getClassName())); + updateHashWithString(digest, scrubSignature(access.getSignature())); + if (isClassMatchedUniquely(access.getClassName())) { + updateHashWithString(digest, access.getFieldName()); + } + } + + @Override + public void edit(ConstructorCall call) { + updateHashWithString(digest, scrubClassName(call.getClassName())); + updateHashWithString(digest, scrubSignature(call.getSignature())); + } + + @Override + public void edit(NewExpr expr) { + updateHashWithString(digest, scrubClassName(expr.getClassName())); + } + }); + + // convert the hash to a hex string + return toHex(digest.digest()); + } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) { + throw new Error(ex); + } + } + + private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) { + ConstPoolEditor editor = new ConstPoolEditor(constants); + ConstInfoAccessor item = editor.getItem(index); + if (item.getType() == InfoType.StringInfo) { + updateHashWithString(digest, constants.getStringInfo(index)); + } + // TODO: other constants + } + + private void updateHashWithString(MessageDigest digest, String val) { + try { + digest.update(val.getBytes("UTF8")); + } catch (UnsupportedEncodingException ex) { + throw new Error(ex); + } + } + + private String toHex(byte[] bytes) { + // function taken from: + // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java + final char[] hexArray = "0123456789ABCDEF".toCharArray(); + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } + + @Override + public boolean equals(Object other) { + if (other instanceof ClassIdentity) { + return equals((ClassIdentity)other); + } + return false; + } + + public boolean equals(ClassIdentity other) { + return m_fields.equals(other.m_fields) + && m_methods.equals(other.m_methods) + && m_constructors.equals(other.m_constructors) + && m_staticInitializer.equals(other.m_staticInitializer) + && m_extends.equals(other.m_extends) + && m_implements.equals(other.m_implements) + && m_implementations.equals(other.m_implementations) + && m_references.equals(other.m_references); + } + + @Override + public int hashCode() { + List objs = Lists.newArrayList(); + objs.addAll(m_fields); + objs.addAll(m_methods); + objs.addAll(m_constructors); + objs.add(m_staticInitializer); + objs.add(m_extends); + objs.addAll(m_implements); + objs.addAll(m_implementations); + objs.addAll(m_references); + return Util.combineHashesOrdered(objs); + } + + public int getMatchScore(ClassIdentity other) { + return getNumMatches(m_fields, other.m_fields) + + getNumMatches(m_methods, other.m_methods) + + getNumMatches(m_constructors, other.m_constructors); + } + + public int getMaxMatchScore() { + return m_fields.size() + m_methods.size() + m_constructors.size(); + } + + public boolean matches(CtClass c) { + // just compare declaration counts + return m_fields.size() == c.getDeclaredFields().length + && m_methods.size() == c.getDeclaredMethods().length + && m_constructors.size() == c.getDeclaredConstructors().length; + } + + private int getNumMatches(Multiset a, Multiset b) { + int numMatches = 0; + for (String val : a) { + if (b.contains(val)) { + numMatches++; + } + } + return numMatches; + } +} diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java new file mode 100644 index 00000000..ccf6b787 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -0,0 +1,406 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarFile; + +import javassist.CtBehavior; +import javassist.CtClass; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.TranslatingTypeLoader; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.MappingParseException; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.MappingsWriter; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.MethodMapping; + +public class ClassMatcher { + + public static void main(String[] args) throws IOException, MappingParseException { + // TEMP + JarFile sourceJar = new JarFile(new File("input/1.8-pre3.jar")); + JarFile destJar = new JarFile(new File("input/1.8.jar")); + File inMappingsFile = new File("../Enigma Mappings/1.8-pre3.mappings"); + File outMappingsFile = new File("../Enigma Mappings/1.8.mappings"); + + // define a matching to use when the automated system cannot find a match + Map fallbackMatching = Maps.newHashMap(); + fallbackMatching.put("none/ayb", "none/ayf"); + fallbackMatching.put("none/ayd", "none/ayd"); + fallbackMatching.put("none/bgk", "unknown/bgk"); + + // do the conversion + Mappings mappings = new MappingsReader().read(new FileReader(inMappingsFile)); + convertMappings(sourceJar, destJar, mappings, fallbackMatching); + + // write out the converted mappings + FileWriter writer = new FileWriter(outMappingsFile); + new MappingsWriter().write(writer, mappings); + writer.close(); + System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); + } + + private static void convertMappings(JarFile sourceJar, JarFile destJar, Mappings mappings, Map fallbackMatching) { + // index jars + System.out.println("Indexing source jar..."); + JarIndex sourceIndex = new JarIndex(); + sourceIndex.indexJar(sourceJar, false); + System.out.println("Indexing dest jar..."); + JarIndex destIndex = new JarIndex(); + destIndex.indexJar(destJar, false); + TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader(sourceJar, sourceIndex); + TranslatingTypeLoader destLoader = new TranslatingTypeLoader(destJar, destIndex); + + // compute the matching + ClassMatching matching = computeMatching(sourceIndex, sourceLoader, destIndex, destLoader); + Map>> matchingIndex = matching.getIndex(); + + // get all the obf class names used in the mappings + Set usedClassNames = mappings.getAllObfClassNames(); + Set allClassNames = Sets.newHashSet(); + for (ClassEntry classEntry : sourceIndex.getObfClassEntries()) { + allClassNames.add(classEntry.getName()); + } + usedClassNames.retainAll(allClassNames); + System.out.println("Used " + usedClassNames.size() + " classes in the mappings"); + + // probabilistically match the non-uniquely-matched source classes + for (Map.Entry> entry : matchingIndex.values()) { + ClassIdentity sourceClass = entry.getKey(); + List destClasses = entry.getValue(); + + // skip classes that are uniquely matched + if (destClasses.size() == 1) { + continue; + } + + // skip classes that aren't used in the mappings + if (!usedClassNames.contains(sourceClass.getClassEntry().getName())) { + continue; + } + + System.out.println("No exact match for source class " + sourceClass.getClassEntry()); + + // find the closest classes + Multimap scoredMatches = ArrayListMultimap.create(); + for (ClassIdentity c : destClasses) { + scoredMatches.put(sourceClass.getMatchScore(c), c); + } + List scores = new ArrayList(scoredMatches.keySet()); + Collections.sort(scores, Collections.reverseOrder()); + printScoredMatches(sourceClass.getMaxMatchScore(), scores, scoredMatches); + + // does the best match have a non-zero score and the same name? + int bestScore = scores.get(0); + Collection bestMatches = scoredMatches.get(bestScore); + if (bestScore > 0 && bestMatches.size() == 1) { + ClassIdentity bestMatch = bestMatches.iterator().next(); + if (bestMatch.getClassEntry().equals(sourceClass.getClassEntry())) { + // use it + System.out.println("\tAutomatically choosing likely match: " + bestMatch.getClassEntry().getName()); + destClasses.clear(); + destClasses.add(bestMatch); + } + } + } + + // group the matching into unique and non-unique matches + BiMap matchedClassNames = HashBiMap.create(); + Set unmatchedSourceClassNames = Sets.newHashSet(); + for (String className : usedClassNames) { + // is there a match for this class? + Map.Entry> entry = matchingIndex.get(className); + ClassIdentity sourceClass = entry.getKey(); + List matches = entry.getValue(); + + if (matches.size() == 1) { + // unique match! We're good to go! + matchedClassNames.put(sourceClass.getClassEntry().getName(), matches.get(0).getClassEntry().getName()); + } else { + // no match, check the fallback matching + String fallbackMatch = fallbackMatching.get(className); + if (fallbackMatch != null) { + matchedClassNames.put(sourceClass.getClassEntry().getName(), fallbackMatch); + } else { + unmatchedSourceClassNames.add(className); + } + } + } + + // report unmatched classes + if (!unmatchedSourceClassNames.isEmpty()) { + System.err.println("ERROR: there were unmatched classes!"); + for (String className : unmatchedSourceClassNames) { + System.err.println("\t" + className); + } + return; + } + + // get the class name changes from the matched class names + Map classChanges = Maps.newHashMap(); + for (Map.Entry entry : matchedClassNames.entrySet()) { + if (!entry.getKey().equals(entry.getValue())) { + classChanges.put(entry.getKey(), entry.getValue()); + System.out.println(String.format("Class change: %s -> %s", entry.getKey(), entry.getValue())); + /* DEBUG + System.out.println(String.format("\n%s\n%s", + new ClassIdentity(sourceLoader.loadClass(entry.getKey()), null, sourceIndex, false, false), + new ClassIdentity( destLoader.loadClass(entry.getValue()), null, destIndex, false, false) + )); + */ + } + } + + // sort the changes so classes are renamed in the correct order + // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b + LinkedHashMap orderedClassChanges = Maps.newLinkedHashMap(); + int numChangesLeft = classChanges.size(); + while (!classChanges.isEmpty()) { + Iterator> iter = classChanges.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + if (classChanges.get(entry.getValue()) == null) { + orderedClassChanges.put(entry.getKey(), entry.getValue()); + iter.remove(); + } + } + + // did we remove any changes? + if (numChangesLeft - classChanges.size() > 0) { + // keep going + numChangesLeft = classChanges.size(); + } else { + // can't sort anymore. There must be a loop + break; + } + } + if (classChanges.size() > 0) { + throw new Error(String.format("Unable to sort %d/%d class changes!", classChanges.size(), matchedClassNames.size())); + } + + // convert the mappings in the correct class order + for (Map.Entry entry : orderedClassChanges.entrySet()) { + mappings.renameObfClass(entry.getKey(), entry.getValue()); + } + + // check the method matches + System.out.println("Checking methods..."); + for (ClassMapping classMapping : mappings.classes()) { + ClassEntry classEntry = new ClassEntry(classMapping.getObfName()); + for (MethodMapping methodMapping : classMapping.methods()) { + + // skip constructors + if (methodMapping.getObfName().equals("")) { + continue; + } + + MethodEntry methodEntry = new MethodEntry( + classEntry, + methodMapping.getObfName(), + methodMapping.getObfSignature() + ); + if (!destIndex.containsObfBehavior(methodEntry)) { + System.err.println("WARNING: method doesn't match: " + methodEntry); + + // show the available methods + System.err.println("\tAvailable dest methods:"); + CtClass c = destLoader.loadClass(classMapping.getObfName()); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + System.err.println("\t\t" + JavassistUtil.getBehaviorEntry(behavior)); + } + + System.err.println("\tAvailable source methods:"); + c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfName())); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + System.err.println("\t\t" + JavassistUtil.getBehaviorEntry(behavior)); + } + } + } + } + + System.out.println("Done!"); + } + + public static ClassMatching computeMatching(JarIndex sourceIndex, TranslatingTypeLoader sourceLoader, JarIndex destIndex, TranslatingTypeLoader destLoader) { + + System.out.println("Matching classes..."); + + ClassMatching matching = null; + for (boolean useReferences : Arrays.asList(false, true)) { + int numMatches = 0; + do { + SidedClassNamer sourceNamer = null; + SidedClassNamer destNamer = null; + if (matching != null) { + // build a class namer + ClassNamer namer = new ClassNamer(matching.getUniqueMatches()); + sourceNamer = namer.getSourceNamer(); + destNamer = namer.getDestNamer(); + + // note the number of matches + numMatches = matching.getUniqueMatches().size(); + } + + // get the entries left to match + Set sourceClassEntries = Sets.newHashSet(); + Set destClassEntries = Sets.newHashSet(); + if (matching == null) { + sourceClassEntries.addAll(sourceIndex.getObfClassEntries()); + destClassEntries.addAll(destIndex.getObfClassEntries()); + matching = new ClassMatching(); + } else { + for (Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet()) { + for (ClassIdentity c : entry.getKey()) { + sourceClassEntries.add(c.getClassEntry()); + matching.removeSource(c); + } + for (ClassIdentity c : entry.getValue()) { + destClassEntries.add(c.getClassEntry()); + matching.removeDest(c); + } + } + for (ClassIdentity c : matching.getUnmatchedSourceClasses()) { + sourceClassEntries.add(c.getClassEntry()); + matching.removeSource(c); + } + for (ClassIdentity c : matching.getUnmatchedDestClasses()) { + destClassEntries.add(c.getClassEntry()); + matching.removeDest(c); + } + } + + // compute a matching for the classes + for (ClassEntry classEntry : sourceClassEntries) { + CtClass c = sourceLoader.loadClass(classEntry.getName()); + ClassIdentity sourceClass = new ClassIdentity(c, sourceNamer, sourceIndex, useReferences); + matching.addSource(sourceClass); + } + for (ClassEntry classEntry : destClassEntries) { + CtClass c = destLoader.loadClass(classEntry.getName()); + ClassIdentity destClass = new ClassIdentity(c, destNamer, destIndex, useReferences); + matching.matchDestClass(destClass); + } + + // TEMP + System.out.println(matching); + } while (matching.getUniqueMatches().size() - numMatches > 0); + } + + // check the class matches + System.out.println("Checking class matches..."); + ClassNamer namer = new ClassNamer(matching.getUniqueMatches()); + SidedClassNamer sourceNamer = namer.getSourceNamer(); + SidedClassNamer destNamer = namer.getDestNamer(); + for (Map.Entry entry : matching.getUniqueMatches().entrySet()) { + + // check source + ClassIdentity sourceClass = entry.getKey(); + CtClass sourceC = sourceLoader.loadClass(sourceClass.getClassEntry().getName()); + assert (sourceC != null) : "Unable to load source class " + sourceClass.getClassEntry(); + assert (sourceClass.matches(sourceC)) : "Source " + sourceClass + " doesn't match " + new ClassIdentity(sourceC, sourceNamer, sourceIndex, false); + + // check dest + ClassIdentity destClass = entry.getValue(); + CtClass destC = destLoader.loadClass(destClass.getClassEntry().getName()); + assert (destC != null) : "Unable to load dest class " + destClass.getClassEntry(); + assert (destClass.matches(destC)) : "Dest " + destClass + " doesn't match " + new ClassIdentity(destC, destNamer, destIndex, false); + } + + // warn about the ambiguous matchings + List,List>> ambiguousMatches = new ArrayList,List>>(matching.getAmbiguousMatches().entrySet()); + Collections.sort(ambiguousMatches, new Comparator,List>>() { + @Override + public int compare(Map.Entry,List> a, Map.Entry,List> b) { + String aName = a.getKey().get(0).getClassEntry().getName(); + String bName = b.getKey().get(0).getClassEntry().getName(); + return aName.compareTo(bName); + } + }); + for (Map.Entry,List> entry : ambiguousMatches) { + System.out.println("Ambiguous matching:"); + System.out.println("\tSource: " + getClassNames(entry.getKey())); + System.out.println("\tDest: " + getClassNames(entry.getValue())); + } + + /* DEBUG + Map.Entry,List> entry = ambiguousMatches.get( 7 ); + for (ClassIdentity c : entry.getKey()) { + System.out.println(c); + } + for(ClassIdentity c : entry.getKey()) { + System.out.println(decompile(sourceLoader, c.getClassEntry())); + } + */ + + return matching; + } + + private static void printScoredMatches(int maxScore, List scores, Multimap scoredMatches) { + int numScoredMatchesShown = 0; + for (int score : scores) { + for (ClassIdentity scoredMatch : scoredMatches.get(score)) { + System.out.println(String.format("\tScore: %3d %3.0f%% %s", score, 100.0 * score / maxScore, scoredMatch.getClassEntry().getName())); + if (numScoredMatchesShown++ > 10) { + return; + } + } + } + } + + private static List getClassNames(Collection classes) { + List out = Lists.newArrayList(); + for (ClassIdentity c : classes) { + out.add(c.getClassEntry().getName()); + } + Collections.sort(out); + return out; + } + + /* DEBUG + private static String decompile(TranslatingTypeLoader loader, ClassEntry classEntry) { + PlainTextOutput output = new PlainTextOutput(); + DecompilerSettings settings = DecompilerSettings.javaDefaults(); + settings.setForceExplicitImports(true); + settings.setShowSyntheticMembers(true); + settings.setTypeLoader(loader); + Decompiler.decompile(classEntry.getName(), output, settings); + return output.toString(); + } + */ +} diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java new file mode 100644 index 00000000..53b6f7f4 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +public class ClassMatching { + + private Multimap m_sourceClasses; + private Multimap m_matchedDestClasses; + private List m_unmatchedDestClasses; + + public ClassMatching() { + m_sourceClasses = ArrayListMultimap.create(); + m_matchedDestClasses = ArrayListMultimap.create(); + m_unmatchedDestClasses = Lists.newArrayList(); + } + + public void addSource(ClassIdentity c) { + m_sourceClasses.put(c, c); + } + + public void matchDestClass(ClassIdentity destClass) { + Collection matchedSourceClasses = m_sourceClasses.get(destClass); + if (matchedSourceClasses.isEmpty()) { + // no match + m_unmatchedDestClasses.add(destClass); + } else { + // found a match + m_matchedDestClasses.put(destClass, destClass); + + // DEBUG + ClassIdentity sourceClass = matchedSourceClasses.iterator().next(); + assert (sourceClass.hashCode() == destClass.hashCode()); + assert (sourceClass.equals(destClass)); + } + } + + public void removeSource(ClassIdentity sourceClass) { + m_sourceClasses.remove(sourceClass, sourceClass); + } + + public void removeDest(ClassIdentity destClass) { + m_matchedDestClasses.remove(destClass, destClass); + m_unmatchedDestClasses.remove(destClass); + } + + public List getSourceClasses() { + return new ArrayList(m_sourceClasses.values()); + } + + public List getDestClasses() { + List classes = Lists.newArrayList(); + classes.addAll(m_matchedDestClasses.values()); + classes.addAll(m_unmatchedDestClasses); + return classes; + } + + public BiMap getUniqueMatches() { + BiMap uniqueMatches = HashBiMap.create(); + for (ClassIdentity sourceClass : m_sourceClasses.keySet()) { + Collection matchedSourceClasses = m_sourceClasses.get(sourceClass); + Collection matchedDestClasses = m_matchedDestClasses.get(sourceClass); + if (matchedSourceClasses.size() == 1 && matchedDestClasses.size() == 1) { + ClassIdentity matchedSourceClass = matchedSourceClasses.iterator().next(); + ClassIdentity matchedDestClass = matchedDestClasses.iterator().next(); + uniqueMatches.put(matchedSourceClass, matchedDestClass); + } + } + return uniqueMatches; + } + + public BiMap,List> getAmbiguousMatches() { + BiMap,List> ambiguousMatches = HashBiMap.create(); + for (ClassIdentity sourceClass : m_sourceClasses.keySet()) { + Collection matchedSourceClasses = m_sourceClasses.get(sourceClass); + Collection matchedDestClasses = m_matchedDestClasses.get(sourceClass); + if (matchedSourceClasses.size() > 1 && matchedDestClasses.size() > 1) { + ambiguousMatches.put( + new ArrayList(matchedSourceClasses), + new ArrayList(matchedDestClasses) + ); + } + } + return ambiguousMatches; + } + + public int getNumAmbiguousSourceMatches() { + int num = 0; + for (Map.Entry,List> entry : getAmbiguousMatches().entrySet()) { + num += entry.getKey().size(); + } + return num; + } + + public int getNumAmbiguousDestMatches() { + int num = 0; + for (Map.Entry,List> entry : getAmbiguousMatches().entrySet()) { + num += entry.getValue().size(); + } + return num; + } + + public List getUnmatchedSourceClasses() { + List classes = Lists.newArrayList(); + for (ClassIdentity sourceClass : getSourceClasses()) { + if (m_matchedDestClasses.get(sourceClass).isEmpty()) { + classes.add(sourceClass); + } + } + return classes; + } + + public List getUnmatchedDestClasses() { + return new ArrayList(m_unmatchedDestClasses); + } + + public Map>> getIndex() { + Map>> conversion = Maps.newHashMap(); + for (Map.Entry entry : getUniqueMatches().entrySet()) { + conversion.put( + entry.getKey().getClassEntry().getName(), + new AbstractMap.SimpleEntry>(entry.getKey(), Arrays.asList(entry.getValue())) + ); + } + for (Map.Entry,List> entry : getAmbiguousMatches().entrySet()) { + for (ClassIdentity sourceClass : entry.getKey()) { + conversion.put( + sourceClass.getClassEntry().getName(), + new AbstractMap.SimpleEntry>(sourceClass, entry.getValue()) + ); + } + } + for (ClassIdentity sourceClass : getUnmatchedSourceClasses()) { + conversion.put( + sourceClass.getClassEntry().getName(), + new AbstractMap.SimpleEntry>(sourceClass, getUnmatchedDestClasses()) + ); + } + return conversion; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(String.format("%12s%8s%8s\n", "", "Source", "Dest")); + buf.append(String.format("%12s%8d%8d\n", "Classes", getSourceClasses().size(), getDestClasses().size())); + buf.append(String.format("%12s%8d%8d\n", "Unique", getUniqueMatches().size(), getUniqueMatches().size())); + buf.append(String.format("%12s%8d%8d\n", "Ambiguous", getNumAmbiguousSourceMatches(), getNumAmbiguousDestMatches())); + buf.append(String.format("%12s%8d%8d\n", "Unmatched", getUnmatchedSourceClasses().size(), getUnmatchedDestClasses().size())); + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/convert/ClassNamer.java b/src/cuchaz/enigma/convert/ClassNamer.java new file mode 100644 index 00000000..1b6e81c8 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassNamer.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.util.Map; + +import com.google.common.collect.BiMap; +import com.google.common.collect.Maps; + +public class ClassNamer { + + public interface SidedClassNamer { + String getName(String name); + } + + private Map m_sourceNames; + private Map m_destNames; + + public ClassNamer(BiMap mappings) { + // convert the identity mappings to name maps + m_sourceNames = Maps.newHashMap(); + m_destNames = Maps.newHashMap(); + int i = 0; + for (Map.Entry entry : mappings.entrySet()) { + String name = String.format("M%04d", i++); + m_sourceNames.put(entry.getKey().getClassEntry().getName(), name); + m_destNames.put(entry.getValue().getClassEntry().getName(), name); + } + } + + public String getSourceName(String name) { + return m_sourceNames.get(name); + } + + public String getDestName(String name) { + return m_destNames.get(name); + } + + public SidedClassNamer getSourceNamer() { + return new SidedClassNamer() { + @Override + public String getName(String name) { + return getSourceName(name); + } + }; + } + + public SidedClassNamer getDestNamer() { + return new SidedClassNamer() { + @Override + public String getName(String name) { + return getDestName(name); + } + }; + } +} diff --git a/src/cuchaz/enigma/gui/AboutDialog.java b/src/cuchaz/enigma/gui/AboutDialog.java new file mode 100644 index 00000000..2476b564 --- /dev/null +++ b/src/cuchaz/enigma/gui/AboutDialog.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.WindowConstants; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.Util; + +public class AboutDialog { + + public static void show(JFrame parent) { + // init frame + final JFrame frame = new JFrame(Constants.Name + " - About"); + final Container pane = frame.getContentPane(); + pane.setLayout(new FlowLayout()); + + // load the content + try { + String html = Util.readResourceToString("/about.html"); + html = String.format(html, Constants.Name, Constants.Version); + JLabel label = new JLabel(html); + label.setHorizontalAlignment(JLabel.CENTER); + pane.add(label); + } catch (IOException ex) { + throw new Error(ex); + } + + // show the link + String html = "%s"; + html = String.format(html, Constants.Url, Constants.Url); + JButton link = new JButton(html); + link.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + Util.openUrl(Constants.Url); + } + }); + link.setBorderPainted(false); + link.setOpaque(false); + link.setBackground(Color.WHITE); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.setFocusable(false); + JPanel linkPanel = new JPanel(); + linkPanel.add(link); + pane.add(linkPanel); + + // show ok button + JButton okButton = new JButton("Ok"); + pane.add(okButton); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + frame.dispose(); + } + }); + + // show the frame + pane.doLayout(); + frame.setSize(400, 220); + frame.setResizable(false); + frame.setLocationRelativeTo(parent); + frame.setVisible(true); + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + } +} diff --git a/src/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/cuchaz/enigma/gui/BoxHighlightPainter.java new file mode 100644 index 00000000..db7c85b4 --- /dev/null +++ b/src/cuchaz/enigma/gui/BoxHighlightPainter.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; + +public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter { + + private Color m_fillColor; + private Color m_borderColor; + + protected BoxHighlightPainter(Color fillColor, Color borderColor) { + m_fillColor = fillColor; + m_borderColor = borderColor; + } + + @Override + public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { + Rectangle bounds = getBounds(text, start, end); + + // fill the area + if (m_fillColor != null) { + g.setColor(m_fillColor); + g.fillRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); + } + + // draw a box around the area + g.setColor(m_borderColor); + g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); + } + + protected static Rectangle getBounds(JTextComponent text, int start, int end) { + try { + // determine the bounds of the text + Rectangle bounds = text.modelToView(start).union(text.modelToView(end)); + + // adjust the box so it looks nice + bounds.x -= 2; + bounds.width += 2; + bounds.y += 1; + bounds.height -= 2; + + return bounds; + } catch (BadLocationException ex) { + // don't care... just return something + return new Rectangle(0, 0, 0, 0); + } + } +} diff --git a/src/cuchaz/enigma/gui/BrowserCaret.java b/src/cuchaz/enigma/gui/BrowserCaret.java new file mode 100644 index 00000000..acee4833 --- /dev/null +++ b/src/cuchaz/enigma/gui/BrowserCaret.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Graphics; +import java.awt.Shape; + +import javax.swing.text.DefaultCaret; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; + +public class BrowserCaret extends DefaultCaret { + + private static final long serialVersionUID = 1158977422507969940L; + + private static final Highlighter.HighlightPainter m_selectionPainter = new Highlighter.HighlightPainter() { + @Override + public void paint(Graphics g, int p0, int p1, Shape bounds, JTextComponent c) { + // don't paint anything + } + }; + + @Override + public boolean isSelectionVisible() { + return false; + } + + @Override + public boolean isVisible() { + return true; + } + + @Override + public Highlighter.HighlightPainter getSelectionPainter() { + return m_selectionPainter; + } +} diff --git a/src/cuchaz/enigma/gui/ClassListCellRenderer.java b/src/cuchaz/enigma/gui/ClassListCellRenderer.java new file mode 100644 index 00000000..d0f01e6a --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassListCellRenderer.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Component; + +import javassist.bytecode.Descriptor; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +public class ClassListCellRenderer implements ListCellRenderer { + + private DefaultListCellRenderer m_defaultRenderer; + + public ClassListCellRenderer() { + m_defaultRenderer = new DefaultListCellRenderer(); + } + + @Override + public Component getListCellRendererComponent(JList list, String className, int index, boolean isSelected, boolean hasFocus) { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent(list, className, index, isSelected, hasFocus); + label.setText(Descriptor.toJavaName(className)); + return label; + } +} diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java new file mode 100644 index 00000000..654bfbed --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassSelector.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +import cuchaz.enigma.mapping.ClassEntry; + +public class ClassSelector extends JTree { + + private static final long serialVersionUID = -7632046902384775977L; + + public interface ClassSelectionListener { + void onSelectClass(ClassEntry classEntry); + } + + public static Comparator ObfuscatedClassEntryComparator; + public static Comparator DeobfuscatedClassEntryComparator; + + static { + ObfuscatedClassEntryComparator = new Comparator() { + @Override + public int compare(ClassEntry a, ClassEntry b) { + if (a.getName().length() != b.getName().length()) { + return a.getName().length() - b.getName().length(); + } + return a.getName().compareTo(b.getName()); + } + }; + + DeobfuscatedClassEntryComparator = new Comparator() { + @Override + public int compare(ClassEntry a, ClassEntry b) { + return a.getName().compareTo(b.getName()); + } + }; + } + + private ClassSelectionListener m_listener; + private Comparator m_comparator; + + public ClassSelector(Comparator comparator) { + m_comparator = comparator; + + // configure the tree control + setRootVisible(false); + setShowsRootHandles(false); + setModel(null); + + // hook events + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (m_listener != null && event.getClickCount() == 2) { + // get the selected node + TreePath path = getSelectionPath(); + if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { + ClassSelectorClassNode node = (ClassSelectorClassNode)path.getLastPathComponent(); + m_listener.onSelectClass(node.getClassEntry()); + } + } + } + }); + + // init defaults + m_listener = null; + } + + public void setListener(ClassSelectionListener val) { + m_listener = val; + } + + public void setClasses(Collection classEntries) { + if (classEntries == null) { + setModel(null); + return; + } + + // build the package names + Map packages = Maps.newHashMap(); + for (ClassEntry classEntry : classEntries) { + packages.put(classEntry.getPackageName(), null); + } + + // sort the packages + List sortedPackageNames = Lists.newArrayList(packages.keySet()); + Collections.sort(sortedPackageNames, new Comparator() { + @Override + public int compare(String a, String b) { + // I can never keep this rule straight when writing these damn things... + // a < b => -1, a == b => 0, a > b => +1 + + String[] aparts = a.split("/"); + String[] bparts = b.split("/"); + for (int i = 0; true; i++) { + if (i >= aparts.length) { + return -1; + } else if (i >= bparts.length) { + return 1; + } + + int result = aparts[i].compareTo(bparts[i]); + if (result != 0) { + return result; + } + } + } + }); + + // create the root node and the package nodes + DefaultMutableTreeNode root = new DefaultMutableTreeNode(); + for (String packageName : sortedPackageNames) { + ClassSelectorPackageNode node = new ClassSelectorPackageNode(packageName); + packages.put(packageName, node); + root.add(node); + } + + // put the classes into packages + Multimap packagedClassEntries = ArrayListMultimap.create(); + for (ClassEntry classEntry : classEntries) { + packagedClassEntries.put(classEntry.getPackageName(), classEntry); + } + + // build the class nodes + for (String packageName : packagedClassEntries.keySet()) { + // sort the class entries + List classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); + Collections.sort(classEntriesInPackage, m_comparator); + + // create the nodes in order + for (ClassEntry classEntry : classEntriesInPackage) { + ClassSelectorPackageNode node = packages.get(packageName); + node.add(new ClassSelectorClassNode(classEntry)); + } + } + + // finally, update the tree control + setModel(new DefaultTreeModel(root)); + } +} diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java new file mode 100644 index 00000000..66e931b4 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import javax.swing.tree.DefaultMutableTreeNode; + +import cuchaz.enigma.mapping.ClassEntry; + +public class ClassSelectorClassNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = -8956754339813257380L; + + private ClassEntry m_classEntry; + + public ClassSelectorClassNode(ClassEntry classEntry) { + m_classEntry = classEntry; + } + + public ClassEntry getClassEntry() { + return m_classEntry; + } + + @Override + public String toString() { + return m_classEntry.getSimpleName(); + } +} diff --git a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java new file mode 100644 index 00000000..451d3809 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import javax.swing.tree.DefaultMutableTreeNode; + +public class ClassSelectorPackageNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = -3730868701219548043L; + + private String m_packageName; + + public ClassSelectorPackageNode(String packageName) { + m_packageName = packageName; + } + + public String getPackageName() { + return m_packageName; + } + + @Override + public String toString() { + return m_packageName; + } +} diff --git a/src/cuchaz/enigma/gui/CrashDialog.java b/src/cuchaz/enigma/gui/CrashDialog.java new file mode 100644 index 00000000..360091ab --- /dev/null +++ b/src/cuchaz/enigma/gui/CrashDialog.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.WindowConstants; + +import cuchaz.enigma.Constants; + +public class CrashDialog { + + private static CrashDialog m_instance = null; + + private JFrame m_frame; + private JTextArea m_text; + + private CrashDialog(JFrame parent) { + // init frame + m_frame = new JFrame(Constants.Name + " - Crash Report"); + final Container pane = m_frame.getContentPane(); + pane.setLayout(new BorderLayout()); + + JLabel label = new JLabel(Constants.Name + " has crashed! =("); + label.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + pane.add(label, BorderLayout.NORTH); + + // report panel + m_text = new JTextArea(); + m_text.setTabSize(2); + pane.add(new JScrollPane(m_text), BorderLayout.CENTER); + + // buttons panel + JPanel buttonsPanel = new JPanel(); + FlowLayout buttonsLayout = new FlowLayout(); + buttonsLayout.setAlignment(FlowLayout.RIGHT); + buttonsPanel.setLayout(buttonsLayout); + buttonsPanel.add(GuiTricks.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work."))); + JButton ignoreButton = new JButton("Ignore"); + ignoreButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + // close (hide) the dialog + m_frame.setVisible(false); + } + }); + buttonsPanel.add(ignoreButton); + JButton exitButton = new JButton("Exit"); + exitButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + // exit enigma + System.exit(1); + } + }); + buttonsPanel.add(exitButton); + pane.add(buttonsPanel, BorderLayout.SOUTH); + + // show the frame + m_frame.setSize(600, 400); + m_frame.setLocationRelativeTo(parent); + m_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + } + + public static void init(JFrame parent) { + m_instance = new CrashDialog(parent); + } + + public static void show(Throwable ex) { + // get the error report + StringWriter buf = new StringWriter(); + ex.printStackTrace(new PrintWriter(buf)); + String report = buf.toString(); + + // show it! + m_instance.m_text.setText(report); + m_instance.m_frame.doLayout(); + m_instance.m_frame.setVisible(true); + } +} diff --git a/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java new file mode 100644 index 00000000..26a31639 --- /dev/null +++ b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; + +public class DeobfuscatedHighlightPainter extends BoxHighlightPainter { + + public DeobfuscatedHighlightPainter() { + // green ish + super(new Color(220, 255, 220), new Color(80, 160, 80)); + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java new file mode 100644 index 00000000..ca39c42a --- /dev/null +++ b/src/cuchaz/enigma/gui/Gui.java @@ -0,0 +1,1165 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridLayout; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.IOException; +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Vector; +import java.util.jar.JarFile; + +import javax.swing.BorderFactory; +import javax.swing.JEditorPane; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.KeyStroke; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.Timer; +import javax.swing.WindowConstants; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import jsyntaxpane.DefaultSyntaxKit; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.FieldReferenceTreeNode; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; +import cuchaz.enigma.analysis.ReferenceTreeNode; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.IllegalNameException; +import cuchaz.enigma.mapping.MappingParseException; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Signature; + +public class Gui { + + private GuiController m_controller; + + // controls + private JFrame m_frame; + private ClassSelector m_obfClasses; + private ClassSelector m_deobfClasses; + private JEditorPane m_editor; + private JPanel m_classesPanel; + private JSplitPane m_splitClasses; + private JPanel m_infoPanel; + private ObfuscatedHighlightPainter m_obfuscatedHighlightPainter; + private DeobfuscatedHighlightPainter m_deobfuscatedHighlightPainter; + private OtherHighlightPainter m_otherHighlightPainter; + private SelectionHighlightPainter m_selectionHighlightPainter; + private JTree m_inheritanceTree; + private JTree m_implementationsTree; + private JTree m_callsTree; + private JList m_tokens; + private JTabbedPane m_tabs; + + // dynamic menu items + private JMenuItem m_closeJarMenu; + private JMenuItem m_openMappingsMenu; + private JMenuItem m_saveMappingsMenu; + private JMenuItem m_saveMappingsAsMenu; + private JMenuItem m_closeMappingsMenu; + private JMenuItem m_renameMenu; + private JMenuItem m_showInheritanceMenu; + private JMenuItem m_openEntryMenu; + private JMenuItem m_openPreviousMenu; + private JMenuItem m_showCallsMenu; + private JMenuItem m_showImplementationsMenu; + private JMenuItem m_toggleMappingMenu; + private JMenuItem m_exportSourceMenu; + private JMenuItem m_exportJarMenu; + + // state + private EntryReference m_reference; + private JFileChooser m_jarFileChooser; + private JFileChooser m_mappingsFileChooser; + private JFileChooser m_exportSourceFileChooser; + private JFileChooser m_exportJarFileChooser; + + public Gui() { + + // init frame + m_frame = new JFrame(Constants.Name); + final Container pane = m_frame.getContentPane(); + pane.setLayout(new BorderLayout()); + + if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) { + // install a global exception handler to the event thread + CrashDialog.init(m_frame); + Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread thread, Throwable ex) { + ex.printStackTrace(System.err); + CrashDialog.show(ex); + } + }); + } + + m_controller = new GuiController(this); + + // init file choosers + m_jarFileChooser = new JFileChooser(); + m_mappingsFileChooser = new JFileChooser(); + m_exportSourceFileChooser = new JFileChooser(); + m_exportSourceFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + m_exportJarFileChooser = new JFileChooser(); + + // init obfuscated classes list + m_obfClasses = new ClassSelector(ClassSelector.ObfuscatedClassEntryComparator); + m_obfClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + navigateTo(classEntry); + } + }); + JScrollPane obfScroller = new JScrollPane(m_obfClasses); + JPanel obfPanel = new JPanel(); + obfPanel.setLayout(new BorderLayout()); + obfPanel.add(new JLabel("Obfuscated Classes"), BorderLayout.NORTH); + obfPanel.add(obfScroller, BorderLayout.CENTER); + + // init deobfuscated classes list + m_deobfClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); + m_deobfClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + navigateTo(classEntry); + } + }); + JScrollPane deobfScroller = new JScrollPane(m_deobfClasses); + JPanel deobfPanel = new JPanel(); + deobfPanel.setLayout(new BorderLayout()); + deobfPanel.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH); + deobfPanel.add(deobfScroller, BorderLayout.CENTER); + + // set up classes panel (don't add the splitter yet) + m_splitClasses = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel); + m_splitClasses.setResizeWeight(0.3); + m_classesPanel = new JPanel(); + m_classesPanel.setLayout(new BorderLayout()); + m_classesPanel.setPreferredSize(new Dimension(250, 0)); + + // init info panel + m_infoPanel = new JPanel(); + m_infoPanel.setLayout(new GridLayout(4, 1, 0, 0)); + m_infoPanel.setPreferredSize(new Dimension(0, 100)); + m_infoPanel.setBorder(BorderFactory.createTitledBorder("Identifier Info")); + clearReference(); + + // init editor + DefaultSyntaxKit.initKit(); + m_obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); + m_deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); + m_otherHighlightPainter = new OtherHighlightPainter(); + m_selectionHighlightPainter = new SelectionHighlightPainter(); + m_editor = new JEditorPane(); + m_editor.setEditable(false); + m_editor.setCaret(new BrowserCaret()); + JScrollPane sourceScroller = new JScrollPane(m_editor); + m_editor.setContentType("text/java"); + m_editor.addCaretListener(new CaretListener() { + @Override + public void caretUpdate(CaretEvent event) { + onCaretMove(event.getDot()); + } + }); + m_editor.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent event) { + switch (event.getKeyCode()) { + case KeyEvent.VK_R: + m_renameMenu.doClick(); + break; + + case KeyEvent.VK_I: + m_showInheritanceMenu.doClick(); + break; + + case KeyEvent.VK_M: + m_showImplementationsMenu.doClick(); + break; + + case KeyEvent.VK_N: + m_openEntryMenu.doClick(); + break; + + case KeyEvent.VK_P: + m_openPreviousMenu.doClick(); + break; + + case KeyEvent.VK_C: + m_showCallsMenu.doClick(); + break; + + case KeyEvent.VK_T: + m_toggleMappingMenu.doClick(); + break; + } + } + }); + + // turn off token highlighting (it's wrong most of the time anyway...) + DefaultSyntaxKit kit = (DefaultSyntaxKit)m_editor.getEditorKit(); + kit.toggleComponent(m_editor, "jsyntaxpane.components.TokenMarker"); + + // init editor popup menu + JPopupMenu popupMenu = new JPopupMenu(); + m_editor.setComponentPopupMenu(popupMenu); + { + JMenuItem menu = new JMenuItem("Rename"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + startRename(); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_renameMenu = menu; + } + { + JMenuItem menu = new JMenuItem("Show Inheritance"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + showInheritance(); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_showInheritanceMenu = menu; + } + { + JMenuItem menu = new JMenuItem("Show Implementations"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + showImplementations(); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_showImplementationsMenu = menu; + } + { + JMenuItem menu = new JMenuItem("Show Calls"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + showCalls(); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_showCallsMenu = menu; + } + { + JMenuItem menu = new JMenuItem("Go to Declaration"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + navigateTo(m_reference.entry); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_openEntryMenu = menu; + } + { + JMenuItem menu = new JMenuItem("Go to previous"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + m_controller.openPreviousReference(); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_openPreviousMenu = menu; + } + { + JMenuItem menu = new JMenuItem("Mark as deobfuscated"); + menu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + toggleMapping(); + } + }); + menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0)); + menu.setEnabled(false); + popupMenu.add(menu); + m_toggleMappingMenu = menu; + } + + // init inheritance panel + m_inheritanceTree = new JTree(); + m_inheritanceTree.setModel(null); + m_inheritanceTree.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + // get the selected node + TreePath path = m_inheritanceTree.getSelectionPath(); + if (path == null) { + return; + } + + Object node = path.getLastPathComponent(); + if (node instanceof ClassInheritanceTreeNode) { + ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode)node; + navigateTo(new ClassEntry(classNode.getObfClassName())); + } else if (node instanceof MethodInheritanceTreeNode) { + MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode)node; + if (methodNode.isImplemented()) { + navigateTo(methodNode.getMethodEntry()); + } + } + } + } + }); + JPanel inheritancePanel = new JPanel(); + inheritancePanel.setLayout(new BorderLayout()); + inheritancePanel.add(new JScrollPane(m_inheritanceTree)); + + // init implementations panel + m_implementationsTree = new JTree(); + m_implementationsTree.setModel(null); + m_implementationsTree.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + // get the selected node + TreePath path = m_implementationsTree.getSelectionPath(); + if (path == null) { + return; + } + + Object node = path.getLastPathComponent(); + if (node instanceof ClassImplementationsTreeNode) { + ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode)node; + navigateTo(classNode.getClassEntry()); + } else if (node instanceof MethodImplementationsTreeNode) { + MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode)node; + navigateTo(methodNode.getMethodEntry()); + } + } + } + }); + JPanel implementationsPanel = new JPanel(); + implementationsPanel.setLayout(new BorderLayout()); + implementationsPanel.add(new JScrollPane(m_implementationsTree)); + + // init call panel + m_callsTree = new JTree(); + m_callsTree.setModel(null); + m_callsTree.addMouseListener(new MouseAdapter() { + @SuppressWarnings("unchecked") + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + // get the selected node + TreePath path = m_callsTree.getSelectionPath(); + if (path == null) { + return; + } + + Object node = path.getLastPathComponent(); + if (node instanceof ReferenceTreeNode) { + ReferenceTreeNode referenceNode = ((ReferenceTreeNode)node); + if (referenceNode.getReference() != null) { + navigateTo(referenceNode.getReference()); + } else { + navigateTo(referenceNode.getEntry()); + } + } + } + } + }); + m_tokens = new JList(); + m_tokens.setCellRenderer(new TokenListCellRenderer(m_controller)); + m_tokens.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + m_tokens.setLayoutOrientation(JList.VERTICAL); + m_tokens.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + Token selected = m_tokens.getSelectedValue(); + if (selected != null) { + showToken(selected); + } + } + } + }); + m_tokens.setPreferredSize(new Dimension(0, 200)); + m_tokens.setMinimumSize(new Dimension(0, 200)); + JSplitPane callPanel = new JSplitPane( + JSplitPane.VERTICAL_SPLIT, + true, + new JScrollPane(m_callsTree), + new JScrollPane(m_tokens) + ); + callPanel.setResizeWeight(1); // let the top side take all the slack + callPanel.resetToPreferredSizes(); + + // layout controls + JPanel centerPanel = new JPanel(); + centerPanel.setLayout(new BorderLayout()); + centerPanel.add(m_infoPanel, BorderLayout.NORTH); + centerPanel.add(sourceScroller, BorderLayout.CENTER); + m_tabs = new JTabbedPane(); + m_tabs.setPreferredSize(new Dimension(250, 0)); + m_tabs.addTab("Inheritance", inheritancePanel); + m_tabs.addTab("Implementations", implementationsPanel); + m_tabs.addTab("Call Graph", callPanel); + JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, m_tabs); + splitRight.setResizeWeight(1); // let the left side take all the slack + splitRight.resetToPreferredSizes(); + JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, m_classesPanel, splitRight); + splitCenter.setResizeWeight(0); // let the right side take all the slack + pane.add(splitCenter, BorderLayout.CENTER); + + // init menus + JMenuBar menuBar = new JMenuBar(); + m_frame.setJMenuBar(menuBar); + { + JMenu menu = new JMenu("File"); + menuBar.add(menu); + { + JMenuItem item = new JMenuItem("Open Jar..."); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (m_jarFileChooser.showOpenDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + // load the jar in a separate thread + new Thread() { + @Override + public void run() { + try { + m_controller.openJar(new JarFile(m_jarFileChooser.getSelectedFile())); + } catch (IOException ex) { + throw new Error(ex); + } + } + }.start(); + } + } + }); + } + { + JMenuItem item = new JMenuItem("Close Jar"); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + m_controller.closeJar(); + } + }); + m_closeJarMenu = item; + } + menu.addSeparator(); + { + JMenuItem item = new JMenuItem("Open Mappings..."); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (m_mappingsFileChooser.showOpenDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + try { + m_controller.openMappings(m_mappingsFileChooser.getSelectedFile()); + } catch (IOException ex) { + throw new Error(ex); + } catch (MappingParseException ex) { + JOptionPane.showMessageDialog(m_frame, ex.getMessage()); + } + } + } + }); + m_openMappingsMenu = item; + } + { + JMenuItem item = new JMenuItem("Save Mappings"); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + try { + m_controller.saveMappings(m_mappingsFileChooser.getSelectedFile()); + } catch (IOException ex) { + throw new Error(ex); + } + } + }); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); + m_saveMappingsMenu = item; + } + { + JMenuItem item = new JMenuItem("Save Mappings As..."); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (m_mappingsFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + try { + m_controller.saveMappings(m_mappingsFileChooser.getSelectedFile()); + m_saveMappingsMenu.setEnabled(true); + } catch (IOException ex) { + throw new Error(ex); + } + } + } + }); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); + m_saveMappingsAsMenu = item; + } + { + JMenuItem item = new JMenuItem("Close Mappings"); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + m_controller.closeMappings(); + } + }); + m_closeMappingsMenu = item; + } + menu.addSeparator(); + { + JMenuItem item = new JMenuItem("Export Source..."); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (m_exportSourceFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + m_controller.exportSource(m_exportSourceFileChooser.getSelectedFile()); + } + } + }); + m_exportSourceMenu = item; + } + { + JMenuItem item = new JMenuItem("Export Jar..."); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (m_exportJarFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + m_controller.exportJar(m_exportJarFileChooser.getSelectedFile()); + } + } + }); + m_exportJarMenu = item; + } + menu.addSeparator(); + { + JMenuItem item = new JMenuItem("Exit"); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + close(); + } + }); + } + } + { + JMenu menu = new JMenu("Help"); + menuBar.add(menu); + { + JMenuItem item = new JMenuItem("About"); + menu.add(item); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + AboutDialog.show(m_frame); + } + }); + } + } + + // init state + onCloseJar(); + + m_frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent event) { + close(); + } + }); + + // show the frame + pane.doLayout(); + m_frame.setSize(1024, 576); + m_frame.setMinimumSize(new Dimension(640, 480)); + m_frame.setVisible(true); + m_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + } + + public JFrame getFrame() { + return m_frame; + } + + public GuiController getController() { + return m_controller; + } + + public void onStartOpenJar() { + m_classesPanel.removeAll(); + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(new JLabel("Loading...")); + m_classesPanel.add(panel); + redraw(); + } + + public void onFinishOpenJar(String jarName) { + // update gui + m_frame.setTitle(Constants.Name + " - " + jarName); + m_classesPanel.removeAll(); + m_classesPanel.add(m_splitClasses); + setSource(null); + + // update menu + m_closeJarMenu.setEnabled(true); + m_openMappingsMenu.setEnabled(true); + m_saveMappingsMenu.setEnabled(false); + m_saveMappingsAsMenu.setEnabled(true); + m_closeMappingsMenu.setEnabled(true); + m_exportSourceMenu.setEnabled(true); + m_exportJarMenu.setEnabled(true); + + redraw(); + } + + public void onCloseJar() { + // update gui + m_frame.setTitle(Constants.Name); + setObfClasses(null); + setDeobfClasses(null); + setSource(null); + m_classesPanel.removeAll(); + + // update menu + m_closeJarMenu.setEnabled(false); + m_openMappingsMenu.setEnabled(false); + m_saveMappingsMenu.setEnabled(false); + m_saveMappingsAsMenu.setEnabled(false); + m_closeMappingsMenu.setEnabled(false); + m_exportSourceMenu.setEnabled(false); + m_exportJarMenu.setEnabled(false); + + redraw(); + } + + public void setObfClasses(Collection obfClasses) { + m_obfClasses.setClasses(obfClasses); + } + + public void setDeobfClasses(Collection deobfClasses) { + m_deobfClasses.setClasses(deobfClasses); + } + + public void setMappingsFile(File file) { + m_mappingsFileChooser.setSelectedFile(file); + m_saveMappingsMenu.setEnabled(file != null); + } + + public void setSource(String source) { + m_editor.getHighlighter().removeAllHighlights(); + m_editor.setText(source); + } + + public void showToken(final Token token) { + if (token == null) { + throw new IllegalArgumentException("Token cannot be null!"); + } + + // set the caret position to the token + m_editor.setCaretPosition(token.start); + m_editor.grabFocus(); + + try { + // make sure the token is visible in the scroll window + Rectangle start = m_editor.modelToView(token.start); + Rectangle end = m_editor.modelToView(token.end); + final Rectangle show = start.union(end); + show.grow(start.width * 10, start.height * 6); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + m_editor.scrollRectToVisible(show); + } + }); + } catch (BadLocationException ex) { + throw new Error(ex); + } + + // highlight the token momentarily + final Timer timer = new Timer(200, new ActionListener() { + private int m_counter = 0; + private Object m_highlight = null; + + @Override + public void actionPerformed(ActionEvent event) { + if (m_counter % 2 == 0) { + try { + m_highlight = m_editor.getHighlighter().addHighlight(token.start, token.end, m_selectionHighlightPainter); + } catch (BadLocationException ex) { + // don't care + } + } else if (m_highlight != null) { + m_editor.getHighlighter().removeHighlight(m_highlight); + } + + if (m_counter++ > 6) { + Timer timer = (Timer)event.getSource(); + timer.stop(); + } + } + }); + timer.start(); + + redraw(); + } + + public void showTokens(Collection tokens) { + Vector sortedTokens = new Vector(tokens); + Collections.sort(sortedTokens); + if (sortedTokens.size() > 1) { + // sort the tokens and update the tokens panel + m_tokens.setListData(sortedTokens); + m_tokens.setSelectedIndex(0); + } else { + m_tokens.setListData(new Vector()); + } + + // show the first token + showToken(sortedTokens.get(0)); + } + + public void setHighlightedTokens(Iterable obfuscatedTokens, Iterable deobfuscatedTokens, Iterable otherTokens) { + + // remove any old highlighters + m_editor.getHighlighter().removeAllHighlights(); + + // color things based on the index + if (obfuscatedTokens != null) { + setHighlightedTokens(obfuscatedTokens, m_obfuscatedHighlightPainter); + } + if (deobfuscatedTokens != null) { + setHighlightedTokens(deobfuscatedTokens, m_deobfuscatedHighlightPainter); + } + if (otherTokens != null) { + setHighlightedTokens(otherTokens, m_otherHighlightPainter); + } + + redraw(); + } + + private void setHighlightedTokens(Iterable tokens, Highlighter.HighlightPainter painter) { + for (Token token : tokens) { + try { + m_editor.getHighlighter().addHighlight(token.start, token.end, painter); + } catch (BadLocationException ex) { + throw new IllegalArgumentException(ex); + } + } + } + + private void clearReference() { + m_infoPanel.removeAll(); + JLabel label = new JLabel("No identifier selected"); + GuiTricks.unboldLabel(label); + label.setHorizontalAlignment(JLabel.CENTER); + m_infoPanel.add(label); + + redraw(); + } + + private void showReference(EntryReference reference) { + if (reference == null) { + clearReference(); + return; + } + + m_reference = reference; + + m_infoPanel.removeAll(); + if (reference.entry instanceof ClassEntry) { + showClassEntry((ClassEntry)m_reference.entry); + } else if (m_reference.entry instanceof FieldEntry) { + showFieldEntry((FieldEntry)m_reference.entry); + } else if (m_reference.entry instanceof MethodEntry) { + showMethodEntry((MethodEntry)m_reference.entry); + } else if (m_reference.entry instanceof ConstructorEntry) { + showConstructorEntry((ConstructorEntry)m_reference.entry); + } else if (m_reference.entry instanceof ArgumentEntry) { + showArgumentEntry((ArgumentEntry)m_reference.entry); + } else { + throw new Error("Unknown entry type: " + m_reference.entry.getClass().getName()); + } + + redraw(); + } + + private void showClassEntry(ClassEntry entry) { + addNameValue(m_infoPanel, "Class", entry.getName()); + } + + private void showFieldEntry(FieldEntry entry) { + addNameValue(m_infoPanel, "Field", entry.getName()); + addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); + } + + private void showMethodEntry(MethodEntry entry) { + addNameValue(m_infoPanel, "Method", entry.getName()); + addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); + addNameValue(m_infoPanel, "Signature", entry.getSignature().toString()); + } + + private void showConstructorEntry(ConstructorEntry entry) { + addNameValue(m_infoPanel, "Constructor", entry.getClassEntry().getName()); + addNameValue(m_infoPanel, "Signature", entry.getSignature().toString()); + } + + private void showArgumentEntry(ArgumentEntry entry) { + addNameValue(m_infoPanel, "Argument", entry.getName()); + addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); + addNameValue(m_infoPanel, "Method", entry.getBehaviorEntry().getName()); + addNameValue(m_infoPanel, "Index", Integer.toString(entry.getIndex())); + } + + private void addNameValue(JPanel container, String name, String value) { + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); + container.add(panel); + + JLabel label = new JLabel(name + ":", JLabel.RIGHT); + label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); + panel.add(label); + + panel.add(GuiTricks.unboldLabel(new JLabel(value, JLabel.LEFT))); + } + + private void onCaretMove(int pos) { + + Token token = m_controller.getToken(pos); + boolean isToken = token != null; + + m_reference = m_controller.getDeobfReference(token); + boolean isClassEntry = isToken && m_reference.entry instanceof ClassEntry; + boolean isFieldEntry = isToken && m_reference.entry instanceof FieldEntry; + boolean isMethodEntry = isToken && m_reference.entry instanceof MethodEntry; + boolean isConstructorEntry = isToken && m_reference.entry instanceof ConstructorEntry; + boolean isInJar = isToken && m_controller.entryIsInJar(m_reference.entry); + boolean isRenameable = isToken && m_controller.referenceIsRenameable(m_reference); + + if (isToken) { + showReference(m_reference); + } else { + clearReference(); + } + + m_renameMenu.setEnabled(isRenameable && isToken); + m_showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); + m_showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); + m_showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); + m_openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); + m_openPreviousMenu.setEnabled(m_controller.hasPreviousLocation()); + m_toggleMappingMenu.setEnabled(isRenameable && isToken); + + if (isToken && m_controller.entryHasDeobfuscatedName(m_reference.entry)) { + m_toggleMappingMenu.setText("Reset to obfuscated"); + } else { + m_toggleMappingMenu.setText("Mark as deobfuscated"); + } + } + + private void navigateTo(Entry entry) { + if (!m_controller.entryIsInJar(entry)) { + // entry is not in the jar. Ignore it + return; + } + if (m_reference != null) { + m_controller.savePreviousReference(m_reference); + } + m_controller.openDeclaration(entry); + } + + private void navigateTo(EntryReference reference) { + if (!m_controller.entryIsInJar(reference.getLocationClassEntry())) { + // reference is not in the jar. Ignore it + return; + } + if (m_reference != null) { + m_controller.savePreviousReference(m_reference); + } + m_controller.openReference(reference); + } + + private void startRename() { + + // init the text box + final JTextField text = new JTextField(); + text.setText(m_reference.getNamableName()); + text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); + text.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent event) { + switch (event.getKeyCode()) { + case KeyEvent.VK_ENTER: + finishRename(text, true); + break; + + case KeyEvent.VK_ESCAPE: + finishRename(text, false); + break; + } + } + }); + + // find the label with the name and replace it with the text box + JPanel panel = (JPanel)m_infoPanel.getComponent(0); + panel.remove(panel.getComponentCount() - 1); + panel.add(text); + text.grabFocus(); + text.selectAll(); + + redraw(); + } + + private void finishRename(JTextField text, boolean saveName) { + String newName = text.getText(); + if (saveName && newName != null && newName.length() > 0) { + try { + m_controller.rename(m_reference, newName); + } catch (IllegalNameException ex) { + text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); + text.setToolTipText(ex.getReason()); + GuiTricks.showToolTipNow(text); + } + return; + } + + // abort the rename + JPanel panel = (JPanel)m_infoPanel.getComponent(0); + panel.remove(panel.getComponentCount() - 1); + panel.add(GuiTricks.unboldLabel(new JLabel(m_reference.getNamableName(), JLabel.LEFT))); + + m_editor.grabFocus(); + + redraw(); + } + + private void showInheritance() { + + if (m_reference == null) { + return; + } + + m_inheritanceTree.setModel(null); + + if (m_reference.entry instanceof ClassEntry) { + // get the class inheritance + ClassInheritanceTreeNode classNode = m_controller.getClassInheritance((ClassEntry)m_reference.entry); + + // show the tree at the root + TreePath path = getPathToRoot(classNode); + m_inheritanceTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0))); + m_inheritanceTree.expandPath(path); + m_inheritanceTree.setSelectionRow(m_inheritanceTree.getRowForPath(path)); + } else if (m_reference.entry instanceof MethodEntry) { + // get the method inheritance + MethodInheritanceTreeNode classNode = m_controller.getMethodInheritance((MethodEntry)m_reference.entry); + + // show the tree at the root + TreePath path = getPathToRoot(classNode); + m_inheritanceTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0))); + m_inheritanceTree.expandPath(path); + m_inheritanceTree.setSelectionRow(m_inheritanceTree.getRowForPath(path)); + } + + m_tabs.setSelectedIndex(0); + redraw(); + } + + private void showImplementations() { + + if (m_reference == null) { + return; + } + + m_implementationsTree.setModel(null); + + if (m_reference.entry instanceof ClassEntry) { + // get the class implementations + ClassImplementationsTreeNode node = m_controller.getClassImplementations((ClassEntry)m_reference.entry); + if (node != null) { + // show the tree at the root + TreePath path = getPathToRoot(node); + m_implementationsTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0))); + m_implementationsTree.expandPath(path); + m_implementationsTree.setSelectionRow(m_implementationsTree.getRowForPath(path)); + } + } else if (m_reference.entry instanceof MethodEntry) { + // get the method implementations + MethodImplementationsTreeNode node = m_controller.getMethodImplementations((MethodEntry)m_reference.entry); + if (node != null) { + // show the tree at the root + TreePath path = getPathToRoot(node); + m_implementationsTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0))); + m_implementationsTree.expandPath(path); + m_implementationsTree.setSelectionRow(m_implementationsTree.getRowForPath(path)); + } + } + + m_tabs.setSelectedIndex(1); + redraw(); + } + + private void showCalls() { + + if (m_reference == null) { + return; + } + + if (m_reference.entry instanceof ClassEntry) { + // look for calls to the default constructor + // TODO: get a list of all the constructors and find calls to all of them + BehaviorReferenceTreeNode node = m_controller.getMethodReferences(new ConstructorEntry((ClassEntry)m_reference.entry, new Signature("()V"))); + m_callsTree.setModel(new DefaultTreeModel(node)); + } else if (m_reference.entry instanceof FieldEntry) { + FieldReferenceTreeNode node = m_controller.getFieldReferences((FieldEntry)m_reference.entry); + m_callsTree.setModel(new DefaultTreeModel(node)); + } else if (m_reference.entry instanceof MethodEntry) { + BehaviorReferenceTreeNode node = m_controller.getMethodReferences((MethodEntry)m_reference.entry); + m_callsTree.setModel(new DefaultTreeModel(node)); + } else if (m_reference.entry instanceof ConstructorEntry) { + BehaviorReferenceTreeNode node = m_controller.getMethodReferences((ConstructorEntry)m_reference.entry); + m_callsTree.setModel(new DefaultTreeModel(node)); + } + + m_tabs.setSelectedIndex(2); + redraw(); + } + + private void toggleMapping() { + if (m_controller.entryHasDeobfuscatedName(m_reference.entry)) { + m_controller.removeMapping(m_reference); + } else { + m_controller.markAsDeobfuscated(m_reference); + } + } + + private TreePath getPathToRoot(TreeNode node) { + List nodes = Lists.newArrayList(); + TreeNode n = node; + do { + nodes.add(n); + n = n.getParent(); + } while (n != null); + Collections.reverse(nodes); + return new TreePath(nodes.toArray()); + } + + private void close() { + if (!m_controller.isDirty()) { + // everything is saved, we can exit safely + m_frame.dispose(); + } else { + // ask to save before closing + String[] options = { "Save and exit", "Discard changes", "Cancel" }; + int response = JOptionPane.showOptionDialog(m_frame, "Your mappings have not been saved yet. Do you want to save?", "Save your changes?", JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, null, options, options[2]); + switch (response) { + case JOptionPane.YES_OPTION: // save and exit + if (m_mappingsFileChooser.getSelectedFile() != null || m_mappingsFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) { + try { + m_controller.saveMappings(m_mappingsFileChooser.getSelectedFile()); + m_frame.dispose(); + } catch (IOException ex) { + throw new Error(ex); + } + } + break; + + case JOptionPane.NO_OPTION: + // don't save, exit + m_frame.dispose(); + break; + + // cancel means do nothing + } + } + } + + private void redraw() { + m_frame.validate(); + m_frame.repaint(); + } +} diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java new file mode 100644 index 00000000..61fea9c0 --- /dev/null +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -0,0 +1,355 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Collection; +import java.util.Deque; +import java.util.List; +import java.util.jar.JarFile; + +import com.google.common.collect.Lists; +import com.google.common.collect.Queues; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; + +import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.Deobfuscator.ProgressListener; +import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.FieldReferenceTreeNode; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.gui.ProgressDialog.ProgressRunnable; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MappingParseException; +import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.MappingsWriter; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.TranslationDirection; + +public class GuiController { + + private Deobfuscator m_deobfuscator; + private Gui m_gui; + private SourceIndex m_index; + private ClassEntry m_currentObfClass; + private boolean m_isDirty; + private Deque> m_referenceStack; + + public GuiController(Gui gui) { + m_gui = gui; + m_deobfuscator = null; + m_index = null; + m_currentObfClass = null; + m_isDirty = false; + m_referenceStack = Queues.newArrayDeque(); + } + + public boolean isDirty() { + return m_isDirty; + } + + public void openJar(final JarFile jar) throws IOException { + m_gui.onStartOpenJar(); + m_deobfuscator = new Deobfuscator(jar); + m_gui.onFinishOpenJar(m_deobfuscator.getJarName()); + refreshClasses(); + } + + public void closeJar() { + m_deobfuscator = null; + m_gui.onCloseJar(); + } + + public void openMappings(File file) throws IOException, MappingParseException { + FileReader in = new FileReader(file); + m_deobfuscator.setMappings(new MappingsReader().read(in)); + in.close(); + m_isDirty = false; + m_gui.setMappingsFile(file); + refreshClasses(); + refreshCurrentClass(); + } + + public void saveMappings(File file) throws IOException { + FileWriter out = new FileWriter(file); + new MappingsWriter().write(out, m_deobfuscator.getMappings()); + out.close(); + m_isDirty = false; + } + + public void closeMappings() { + m_deobfuscator.setMappings(null); + m_gui.setMappingsFile(null); + refreshClasses(); + refreshCurrentClass(); + } + + public void exportSource(final File dirOut) { + ProgressDialog.runInThread(m_gui.getFrame(), new ProgressRunnable() { + @Override + public void run(ProgressListener progress) throws Exception { + m_deobfuscator.writeSources(dirOut, progress); + } + }); + } + + public void exportJar(final File fileOut) { + ProgressDialog.runInThread(m_gui.getFrame(), new ProgressRunnable() { + @Override + public void run(ProgressListener progress) { + m_deobfuscator.writeJar(fileOut, progress); + } + }); + } + + public Token getToken(int pos) { + if (m_index == null) { + return null; + } + return m_index.getReferenceToken(pos); + } + + public EntryReference getDeobfReference(Token token) { + if (m_index == null) { + return null; + } + return m_index.getDeobfReference(token); + } + + public ReadableToken getReadableToken(Token token) { + if (m_index == null) { + return null; + } + return new ReadableToken( + m_index.getLineNumber(token.start), + m_index.getColumnNumber(token.start), + m_index.getColumnNumber(token.end) + ); + } + + public boolean entryHasDeobfuscatedName(Entry deobfEntry) { + return m_deobfuscator.hasDeobfuscatedName(m_deobfuscator.obfuscateEntry(deobfEntry)); + } + + public boolean entryIsInJar(Entry deobfEntry) { + return m_deobfuscator.isObfuscatedIdentifier(m_deobfuscator.obfuscateEntry(deobfEntry)); + } + + public boolean referenceIsRenameable(EntryReference deobfReference) { + return m_deobfuscator.isRenameable(m_deobfuscator.obfuscateReference(deobfReference)); + } + + public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { + ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry(deobfClassEntry); + ClassInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getClassInheritance( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfClassEntry + ); + return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); + } + + public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { + ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry(deobfClassEntry); + return m_deobfuscator.getJarIndex().getClassImplementations( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfClassEntry + ); + } + + public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { + MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry(deobfMethodEntry); + MethodInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodInheritance( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfMethodEntry + ); + return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); + } + + public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { + MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry(deobfMethodEntry); + MethodImplementationsTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodImplementations( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfMethodEntry + ); + if (rootNode == null) { + return null; + } + return MethodImplementationsTreeNode.findNode(rootNode, obfMethodEntry); + } + + public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { + FieldEntry obfFieldEntry = m_deobfuscator.obfuscateEntry(deobfFieldEntry); + FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfFieldEntry + ); + rootNode.load(m_deobfuscator.getJarIndex(), true); + return rootNode; + } + + public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) { + BehaviorEntry obfBehaviorEntry = m_deobfuscator.obfuscateEntry(deobfBehaviorEntry); + BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfBehaviorEntry + ); + rootNode.load(m_deobfuscator.getJarIndex(), true); + return rootNode; + } + + public void rename(EntryReference deobfReference, String newName) { + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + m_deobfuscator.rename(obfReference.getNameableEntry(), newName); + m_isDirty = true; + refreshClasses(); + refreshCurrentClass(obfReference); + } + + public void removeMapping(EntryReference deobfReference) { + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + m_deobfuscator.removeMapping(obfReference.getNameableEntry()); + m_isDirty = true; + refreshClasses(); + refreshCurrentClass(obfReference); + } + + public void markAsDeobfuscated(EntryReference deobfReference) { + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + m_deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); + m_isDirty = true; + refreshClasses(); + refreshCurrentClass(obfReference); + } + + public void openDeclaration(Entry deobfEntry) { + if (deobfEntry == null) { + throw new IllegalArgumentException("Entry cannot be null!"); + } + openReference(new EntryReference(deobfEntry, deobfEntry.getName())); + } + + public void openReference(EntryReference deobfReference) { + if (deobfReference == null) { + throw new IllegalArgumentException("Reference cannot be null!"); + } + + // get the reference target class + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOuterClassEntry(); + if (!m_deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { + throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); + } + if (m_currentObfClass == null || !m_currentObfClass.equals(obfClassEntry)) { + // deobfuscate the class, then navigate to the reference + m_currentObfClass = obfClassEntry; + deobfuscate(m_currentObfClass, obfReference); + } else { + showReference(obfReference); + } + } + + private void showReference(EntryReference obfReference) { + EntryReference deobfReference = m_deobfuscator.deobfuscateReference(obfReference); + Collection tokens = m_index.getReferenceTokens(deobfReference); + if (tokens.isEmpty()) { + // DEBUG + System.err.println(String.format("WARNING: no tokens found for %s in %s", deobfReference, m_currentObfClass)); + } else { + m_gui.showTokens(tokens); + } + } + + public void savePreviousReference(EntryReference deobfReference) { + m_referenceStack.push(m_deobfuscator.obfuscateReference(deobfReference)); + } + + public void openPreviousReference() { + if (hasPreviousLocation()) { + openReference(m_deobfuscator.deobfuscateReference(m_referenceStack.pop())); + } + } + + public boolean hasPreviousLocation() { + return !m_referenceStack.isEmpty(); + } + + private void refreshClasses() { + List obfClasses = Lists.newArrayList(); + List deobfClasses = Lists.newArrayList(); + m_deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); + m_gui.setObfClasses(obfClasses); + m_gui.setDeobfClasses(deobfClasses); + } + + private void refreshCurrentClass() { + refreshCurrentClass(null); + } + + private void refreshCurrentClass(EntryReference obfReference) { + if (m_currentObfClass != null) { + deobfuscate(m_currentObfClass, obfReference); + } + } + + private void deobfuscate(final ClassEntry classEntry, final EntryReference obfReference) { + + m_gui.setSource("(deobfuscating...)"); + + // run the deobfuscator in a separate thread so we don't block the GUI event queue + new Thread() { + @Override + public void run() { + // decompile,deobfuscate the bytecode + CompilationUnit sourceTree = m_deobfuscator.getSourceTree(classEntry.getClassName()); + if (sourceTree == null) { + // decompilation of this class is not supported + m_gui.setSource("Unable to find class: " + classEntry); + return; + } + String source = m_deobfuscator.getSource(sourceTree); + m_index = m_deobfuscator.getSourceIndex(sourceTree, source); + m_gui.setSource(m_index.getSource()); + if (obfReference != null) { + showReference(obfReference); + } + + // set the highlighted tokens + List obfuscatedTokens = Lists.newArrayList(); + List deobfuscatedTokens = Lists.newArrayList(); + List otherTokens = Lists.newArrayList(); + for (Token token : m_index.referenceTokens()) { + EntryReference reference = m_index.getDeobfReference(token); + if (referenceIsRenameable(reference)) { + if (entryHasDeobfuscatedName(reference.getNameableEntry())) { + deobfuscatedTokens.add(token); + } else { + obfuscatedTokens.add(token); + } + } else { + otherTokens.add(token); + } + } + m_gui.setHighlightedTokens(obfuscatedTokens, deobfuscatedTokens, otherTokens); + } + }.start(); + } +} diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java new file mode 100644 index 00000000..df9e2215 --- /dev/null +++ b/src/cuchaz/enigma/gui/GuiTricks.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Font; +import java.awt.event.MouseEvent; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.ToolTipManager; + +public class GuiTricks { + + public static JLabel unboldLabel(JLabel label) { + Font font = label.getFont(); + label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); + return label; + } + + public static void showToolTipNow(JComponent component) { + // HACKHACK: trick the tooltip manager into showing the tooltip right now + ToolTipManager manager = ToolTipManager.sharedInstance(); + int oldDelay = manager.getInitialDelay(); + manager.setInitialDelay(0); + manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); + manager.setInitialDelay(oldDelay); + } +} diff --git a/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java new file mode 100644 index 00000000..177835f4 --- /dev/null +++ b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; + +public class ObfuscatedHighlightPainter extends BoxHighlightPainter { + + public ObfuscatedHighlightPainter() { + // red ish + super(new Color(255, 220, 220), new Color(160, 80, 80)); + } +} diff --git a/src/cuchaz/enigma/gui/OtherHighlightPainter.java b/src/cuchaz/enigma/gui/OtherHighlightPainter.java new file mode 100644 index 00000000..4e9c8709 --- /dev/null +++ b/src/cuchaz/enigma/gui/OtherHighlightPainter.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Color; + +public class OtherHighlightPainter extends BoxHighlightPainter { + + public OtherHighlightPainter() { + // grey + super(null, new Color(180, 180, 180)); + } +} diff --git a/src/cuchaz/enigma/gui/ProgressDialog.java b/src/cuchaz/enigma/gui/ProgressDialog.java new file mode 100644 index 00000000..b864fdbf --- /dev/null +++ b/src/cuchaz/enigma/gui/ProgressDialog.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; + +import javax.swing.BorderFactory; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.WindowConstants; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.Deobfuscator.ProgressListener; + +public class ProgressDialog implements ProgressListener, AutoCloseable { + + private JFrame m_frame; + private JLabel m_title; + private JLabel m_text; + private JProgressBar m_progress; + + public ProgressDialog(JFrame parent) { + + // init frame + m_frame = new JFrame(Constants.Name + " - Operation in progress"); + final Container pane = m_frame.getContentPane(); + FlowLayout layout = new FlowLayout(); + layout.setAlignment(FlowLayout.LEFT); + pane.setLayout(layout); + + m_title = new JLabel(); + pane.add(m_title); + + // set up the progress bar + JPanel panel = new JPanel(); + pane.add(panel); + panel.setLayout(new BorderLayout()); + m_text = GuiTricks.unboldLabel(new JLabel()); + m_progress = new JProgressBar(); + m_text.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); + panel.add(m_text, BorderLayout.NORTH); + panel.add(m_progress, BorderLayout.CENTER); + panel.setPreferredSize(new Dimension(360, 50)); + + // show the frame + pane.doLayout(); + m_frame.setSize(400, 120); + m_frame.setResizable(false); + m_frame.setLocationRelativeTo(parent); + m_frame.setVisible(true); + m_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + } + + public void close() { + m_frame.dispose(); + } + + @Override + public void init(int totalWork, String title) { + m_title.setText(title); + m_progress.setMinimum(0); + m_progress.setMaximum(totalWork); + m_progress.setValue(0); + } + + @Override + public void onProgress(int numDone, String message) { + m_text.setText(message); + m_progress.setValue(numDone); + + // update the frame + m_frame.validate(); + m_frame.repaint(); + } + + public static interface ProgressRunnable { + void run(ProgressListener listener) throws Exception; + } + + public static void runInThread(final JFrame parent, final ProgressRunnable runnable) { + new Thread() { + @Override + public void run() { + try (ProgressDialog progress = new ProgressDialog(parent)) { + runnable.run(progress); + } catch (Exception ex) { + throw new Error(ex); + } + } + }.start(); + } +} diff --git a/src/cuchaz/enigma/gui/ReadableToken.java b/src/cuchaz/enigma/gui/ReadableToken.java new file mode 100644 index 00000000..66bcbc2a --- /dev/null +++ b/src/cuchaz/enigma/gui/ReadableToken.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +public class ReadableToken { + + public int line; + public int startColumn; + public int endColumn; + + public ReadableToken(int line, int startColumn, int endColumn) { + this.line = line; + this.startColumn = startColumn; + this.endColumn = endColumn; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("line "); + buf.append(line); + buf.append(" columns "); + buf.append(startColumn); + buf.append("-"); + buf.append(endColumn); + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/gui/RenameListener.java b/src/cuchaz/enigma/gui/RenameListener.java new file mode 100644 index 00000000..abeda0ce --- /dev/null +++ b/src/cuchaz/enigma/gui/RenameListener.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import cuchaz.enigma.mapping.Entry; + +public interface RenameListener { + void rename(Entry obfEntry, String newName); +} diff --git a/src/cuchaz/enigma/gui/SelectionHighlightPainter.java b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java new file mode 100644 index 00000000..5e189d2e --- /dev/null +++ b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; + +public class SelectionHighlightPainter implements Highlighter.HighlightPainter { + + @Override + public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { + // draw a thick border + Graphics2D g2d = (Graphics2D)g; + Rectangle bounds = BoxHighlightPainter.getBounds(text, start, end); + g2d.setColor(Color.black); + g2d.setStroke(new BasicStroke(2.0f)); + g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); + } +} diff --git a/src/cuchaz/enigma/gui/TokenListCellRenderer.java b/src/cuchaz/enigma/gui/TokenListCellRenderer.java new file mode 100644 index 00000000..a49be37b --- /dev/null +++ b/src/cuchaz/enigma/gui/TokenListCellRenderer.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.awt.Component; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +import cuchaz.enigma.analysis.Token; + +public class TokenListCellRenderer implements ListCellRenderer { + + private GuiController m_controller; + private DefaultListCellRenderer m_defaultRenderer; + + public TokenListCellRenderer(GuiController controller) { + m_controller = controller; + m_defaultRenderer = new DefaultListCellRenderer(); + } + + @Override + public Component getListCellRendererComponent(JList list, Token token, int index, boolean isSelected, boolean hasFocus) { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent(list, token, index, isSelected, hasFocus); + label.setText(m_controller.getReadableToken(token).toString()); + return label; + } +} diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java new file mode 100644 index 00000000..aa222652 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +import cuchaz.enigma.Util; + +public class ArgumentEntry implements Entry, Serializable { + + private static final long serialVersionUID = 4472172468162696006L; + + private BehaviorEntry m_behaviorEntry; + private int m_index; + private String m_name; + + public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) { + if (behaviorEntry == null) { + throw new IllegalArgumentException("Behavior cannot be null!"); + } + if (index < 0) { + throw new IllegalArgumentException("Index must be non-negative!"); + } + if (name == null) { + throw new IllegalArgumentException("Argument name cannot be null!"); + } + + m_behaviorEntry = behaviorEntry; + m_index = index; + m_name = name; + } + + public ArgumentEntry(ArgumentEntry other) { + m_behaviorEntry = (BehaviorEntry)m_behaviorEntry.cloneToNewClass(getClassEntry()); + m_index = other.m_index; + m_name = other.m_name; + } + + public ArgumentEntry(ArgumentEntry other, String newClassName) { + m_behaviorEntry = (BehaviorEntry)other.m_behaviorEntry.cloneToNewClass(new ClassEntry(newClassName)); + m_index = other.m_index; + m_name = other.m_name; + } + + public BehaviorEntry getBehaviorEntry() { + return m_behaviorEntry; + } + + public int getIndex() { + return m_index; + } + + @Override + public String getName() { + return m_name; + } + + @Override + public ClassEntry getClassEntry() { + return m_behaviorEntry.getClassEntry(); + } + + @Override + public String getClassName() { + return m_behaviorEntry.getClassName(); + } + + @Override + public ArgumentEntry cloneToNewClass(ClassEntry classEntry) { + return new ArgumentEntry(this, classEntry.getName()); + } + + public String getMethodName() { + return m_behaviorEntry.getName(); + } + + public Signature getMethodSignature() { + return m_behaviorEntry.getSignature(); + } + + @Override + public int hashCode() { + return Util.combineHashesOrdered( + m_behaviorEntry, + Integer.valueOf(m_index).hashCode(), + m_name.hashCode() + ); + } + + @Override + public boolean equals(Object other) { + if (other instanceof ArgumentEntry) { + return equals((ArgumentEntry)other); + } + return false; + } + + public boolean equals(ArgumentEntry other) { + return m_behaviorEntry.equals(other.m_behaviorEntry) + && m_index == other.m_index + && m_name.equals(other.m_name); + } + + @Override + public String toString() { + return m_behaviorEntry.toString() + "(" + m_index + ":" + m_name + ")"; + } +} diff --git a/src/cuchaz/enigma/mapping/ArgumentMapping.java b/src/cuchaz/enigma/mapping/ArgumentMapping.java new file mode 100644 index 00000000..f4d8e774 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +public class ArgumentMapping implements Serializable, Comparable { + + private static final long serialVersionUID = 8610742471440861315L; + + private int m_index; + private String m_name; + + // NOTE: this argument order is important for the MethodReader/MethodWriter + public ArgumentMapping(int index, String name) { + m_index = index; + m_name = NameValidator.validateArgumentName(name); + } + + public int getIndex() { + return m_index; + } + + public String getName() { + return m_name; + } + + public void setName(String val) { + m_name = NameValidator.validateArgumentName(val); + } + + @Override + public int compareTo(ArgumentMapping other) { + return Integer.compare(m_index, other.m_index); + } +} diff --git a/src/cuchaz/enigma/mapping/BehaviorEntry.java b/src/cuchaz/enigma/mapping/BehaviorEntry.java new file mode 100644 index 00000000..535788f2 --- /dev/null +++ b/src/cuchaz/enigma/mapping/BehaviorEntry.java @@ -0,0 +1,15 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public interface BehaviorEntry extends Entry { + Signature getSignature(); +} diff --git a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java new file mode 100644 index 00000000..61e501b7 --- /dev/null +++ b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import javassist.CtBehavior; +import javassist.CtConstructor; +import javassist.CtMethod; +import javassist.bytecode.Descriptor; + +public class BehaviorEntryFactory { + + public static BehaviorEntry create(String className, String name, String signature) { + return create(new ClassEntry(className), name, signature); + } + + public static BehaviorEntry create(ClassEntry classEntry, String name, String signature) { + if (name.equals("")) { + return new ConstructorEntry(classEntry, new Signature(signature)); + } else if (name.equals("")) { + return new ConstructorEntry(classEntry); + } else { + return new MethodEntry(classEntry, name, new Signature(signature)); + } + } + + public static BehaviorEntry create(CtBehavior behavior) { + String className = Descriptor.toJvmName(behavior.getDeclaringClass().getName()); + if (behavior instanceof CtMethod) { + return create(className, behavior.getName(), behavior.getSignature()); + } else if (behavior instanceof CtConstructor) { + CtConstructor constructor = (CtConstructor)behavior; + if (constructor.isClassInitializer()) { + return create(className, "", null); + } else { + return create(className, "", constructor.getSignature()); + } + } else { + throw new IllegalArgumentException("Unable to create BehaviorEntry from " + behavior); + } + } + + public static BehaviorEntry createObf(ClassEntry classEntry, MethodMapping methodMapping) { + return create(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature().toString()); + } + + public static BehaviorEntry createDeobf(ClassEntry classEntry, MethodMapping methodMapping) { + return create(classEntry, methodMapping.getDeobfName(), methodMapping.getObfSignature().toString()); + } +} diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java new file mode 100644 index 00000000..cf410012 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +public class ClassEntry implements Entry, Serializable { + + private static final long serialVersionUID = 4235460580973955811L; + + private String m_name; + + public ClassEntry(String className) { + if (className == null) { + throw new IllegalArgumentException("Class name cannot be null!"); + } + if (className.indexOf('.') >= 0) { + throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); + } + + m_name = className; + + if (isInnerClass() && getInnerClassName().indexOf('/') >= 0) { + throw new IllegalArgumentException("Inner class must not have a package: " + className); + } + } + + public ClassEntry(ClassEntry other) { + m_name = other.m_name; + } + + @Override + public String getName() { + return m_name; + } + + @Override + public String getClassName() { + return m_name; + } + + @Override + public ClassEntry getClassEntry() { + return this; + } + + @Override + public ClassEntry cloneToNewClass(ClassEntry classEntry) { + return classEntry; + } + + @Override + public int hashCode() { + return m_name.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof ClassEntry) { + return equals((ClassEntry)other); + } + return false; + } + + public boolean equals(ClassEntry other) { + return m_name.equals(other.m_name); + } + + @Override + public String toString() { + return m_name; + } + + public boolean isInnerClass() { + return m_name.lastIndexOf('$') >= 0; + } + + public String getOuterClassName() { + if (isInnerClass()) { + return m_name.substring(0, m_name.lastIndexOf('$')); + } + return m_name; + } + + public String getInnerClassName() { + if (!isInnerClass()) { + throw new Error("This is not an inner class!"); + } + return m_name.substring(m_name.lastIndexOf('$') + 1); + } + + public ClassEntry getOuterClassEntry() { + return new ClassEntry(getOuterClassName()); + } + + public boolean isInDefaultPackage() { + return m_name.indexOf('/') < 0; + } + + public String getPackageName() { + int pos = m_name.lastIndexOf('/'); + if (pos > 0) { + return m_name.substring(0, pos); + } + return null; + } + + public String getSimpleName() { + int pos = m_name.lastIndexOf('/'); + if (pos > 0) { + return m_name.substring(pos + 1); + } + return m_name; + } +} diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java new file mode 100644 index 00000000..e2c3d56f --- /dev/null +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -0,0 +1,405 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Map; + +import com.google.common.collect.Maps; + +public class ClassMapping implements Serializable, Comparable { + + private static final long serialVersionUID = -5148491146902340107L; + + private String m_obfName; + private String m_deobfName; + private Map m_innerClassesByObf; + private Map m_innerClassesByDeobf; + private Map m_fieldsByObf; + private Map m_fieldsByDeobf; + private Map m_methodsByObf; + private Map m_methodsByDeobf; + + public ClassMapping(String obfName) { + this(obfName, null); + } + + public ClassMapping(String obfName, String deobfName) { + m_obfName = obfName; + m_deobfName = NameValidator.validateClassName(deobfName, false); + m_innerClassesByObf = Maps.newHashMap(); + m_innerClassesByDeobf = Maps.newHashMap(); + m_fieldsByObf = Maps.newHashMap(); + m_fieldsByDeobf = Maps.newHashMap(); + m_methodsByObf = Maps.newHashMap(); + m_methodsByDeobf = Maps.newHashMap(); + } + + public String getObfName() { + return m_obfName; + } + + public String getDeobfName() { + return m_deobfName; + } + + public void setDeobfName(String val) { + m_deobfName = NameValidator.validateClassName(val, false); + } + + //// INNER CLASSES //////// + + public Iterable innerClasses() { + assert (m_innerClassesByObf.size() >= m_innerClassesByDeobf.size()); + return m_innerClassesByObf.values(); + } + + public void addInnerClassMapping(ClassMapping classMapping) { + assert (isSimpleClassName(classMapping.getObfName())); + boolean obfWasAdded = m_innerClassesByObf.put(classMapping.getObfName(), classMapping) == null; + assert (obfWasAdded); + if (classMapping.getDeobfName() != null) { + assert (isSimpleClassName(classMapping.getDeobfName())); + boolean deobfWasAdded = m_innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping) == null; + assert (deobfWasAdded); + } + } + + public void removeInnerClassMapping(ClassMapping classMapping) { + boolean obfWasRemoved = m_innerClassesByObf.remove(classMapping.getObfName()) != null; + assert (obfWasRemoved); + if (classMapping.getDeobfName() != null) { + boolean deobfWasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (deobfWasRemoved); + } + } + + public ClassMapping getOrCreateInnerClass(String obfName) { + assert (isSimpleClassName(obfName)); + ClassMapping classMapping = m_innerClassesByObf.get(obfName); + if (classMapping == null) { + classMapping = new ClassMapping(obfName); + boolean wasAdded = m_innerClassesByObf.put(obfName, classMapping) == null; + assert (wasAdded); + } + return classMapping; + } + + public ClassMapping getInnerClassByObf(String obfName) { + assert (isSimpleClassName(obfName)); + return m_innerClassesByObf.get(obfName); + } + + public ClassMapping getInnerClassByDeobf(String deobfName) { + assert (isSimpleClassName(deobfName)); + return m_innerClassesByDeobf.get(deobfName); + } + + public ClassMapping getInnerClassByDeobfThenObf(String name) { + ClassMapping classMapping = getInnerClassByDeobf(name); + if (classMapping == null) { + classMapping = getInnerClassByObf(name); + } + return classMapping; + } + + public String getObfInnerClassName(String deobfName) { + assert (isSimpleClassName(deobfName)); + ClassMapping classMapping = m_innerClassesByDeobf.get(deobfName); + if (classMapping != null) { + return classMapping.getObfName(); + } + return null; + } + + public String getDeobfInnerClassName(String obfName) { + assert (isSimpleClassName(obfName)); + ClassMapping classMapping = m_innerClassesByObf.get(obfName); + if (classMapping != null) { + return classMapping.getDeobfName(); + } + return null; + } + + public void setInnerClassName(String obfName, String deobfName) { + assert (isSimpleClassName(obfName)); + ClassMapping classMapping = getOrCreateInnerClass(obfName); + if (classMapping.getDeobfName() != null) { + boolean wasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (wasRemoved); + } + classMapping.setDeobfName(deobfName); + if (deobfName != null) { + assert (isSimpleClassName(deobfName)); + boolean wasAdded = m_innerClassesByDeobf.put(deobfName, classMapping) == null; + assert (wasAdded); + } + } + + //// FIELDS //////// + + public Iterable fields() { + assert (m_fieldsByObf.size() == m_fieldsByDeobf.size()); + return m_fieldsByObf.values(); + } + + public boolean containsObfField(String obfName) { + return m_fieldsByObf.containsKey(obfName); + } + + public boolean containsDeobfField(String deobfName) { + return m_fieldsByDeobf.containsKey(deobfName); + } + + public void addFieldMapping(FieldMapping fieldMapping) { + if (m_fieldsByObf.containsKey(fieldMapping.getObfName())) { + throw new Error("Already have mapping for " + m_obfName + "." + fieldMapping.getObfName()); + } + if (m_fieldsByDeobf.containsKey(fieldMapping.getDeobfName())) { + throw new Error("Already have mapping for " + m_deobfName + "." + fieldMapping.getDeobfName()); + } + boolean obfWasAdded = m_fieldsByObf.put(fieldMapping.getObfName(), fieldMapping) == null; + assert (obfWasAdded); + boolean deobfWasAdded = m_fieldsByDeobf.put(fieldMapping.getDeobfName(), fieldMapping) == null; + assert (deobfWasAdded); + assert (m_fieldsByObf.size() == m_fieldsByDeobf.size()); + } + + public void removeFieldMapping(FieldMapping fieldMapping) { + boolean obfWasRemoved = m_fieldsByObf.remove(fieldMapping.getObfName()) != null; + assert (obfWasRemoved); + if (fieldMapping.getDeobfName() != null) { + boolean deobfWasRemoved = m_fieldsByDeobf.remove(fieldMapping.getDeobfName()) != null; + assert (deobfWasRemoved); + } + } + + public FieldMapping getFieldByObf(String obfName) { + return m_fieldsByObf.get(obfName); + } + + public FieldMapping getFieldByDeobf(String deobfName) { + return m_fieldsByDeobf.get(deobfName); + } + + public String getObfFieldName(String deobfName) { + FieldMapping fieldMapping = m_fieldsByDeobf.get(deobfName); + if (fieldMapping != null) { + return fieldMapping.getObfName(); + } + return null; + } + + public String getDeobfFieldName(String obfName) { + FieldMapping fieldMapping = m_fieldsByObf.get(obfName); + if (fieldMapping != null) { + return fieldMapping.getDeobfName(); + } + return null; + } + + public void setFieldName(String obfName, String deobfName) { + FieldMapping fieldMapping = m_fieldsByObf.get(obfName); + if (fieldMapping == null) { + fieldMapping = new FieldMapping(obfName, deobfName); + boolean obfWasAdded = m_fieldsByObf.put(obfName, fieldMapping) == null; + assert (obfWasAdded); + } else { + boolean wasRemoved = m_fieldsByDeobf.remove(fieldMapping.getDeobfName()) != null; + assert (wasRemoved); + } + fieldMapping.setDeobfName(deobfName); + if (deobfName != null) { + boolean wasAdded = m_fieldsByDeobf.put(deobfName, fieldMapping) == null; + assert (wasAdded); + } + } + + //// METHODS //////// + + public Iterable methods() { + assert (m_methodsByObf.size() >= m_methodsByDeobf.size()); + return m_methodsByObf.values(); + } + + public boolean containsObfMethod(String obfName, Signature obfSignature) { + return m_methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); + } + + public boolean containsDeobfMethod(String deobfName, Signature deobfSignature) { + return m_methodsByDeobf.containsKey(getMethodKey(deobfName, deobfSignature)); + } + + public void addMethodMapping(MethodMapping methodMapping) { + String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); + if (m_methodsByObf.containsKey(obfKey)) { + throw new Error("Already have mapping for " + m_obfName + "." + obfKey); + } + boolean wasAdded = m_methodsByObf.put(obfKey, methodMapping) == null; + assert (wasAdded); + if (methodMapping.getDeobfName() != null) { + String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); + if (m_methodsByDeobf.containsKey(deobfKey)) { + throw new Error("Already have mapping for " + m_deobfName + "." + deobfKey); + } + boolean deobfWasAdded = m_methodsByDeobf.put(deobfKey, methodMapping) == null; + assert (deobfWasAdded); + } + assert (m_methodsByObf.size() >= m_methodsByDeobf.size()); + } + + public void removeMethodMapping(MethodMapping methodMapping) { + boolean obfWasRemoved = m_methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; + assert (obfWasRemoved); + if (methodMapping.getDeobfName() != null) { + boolean deobfWasRemoved = m_methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; + assert (deobfWasRemoved); + } + } + + public MethodMapping getMethodByObf(String obfName, Signature signature) { + return m_methodsByObf.get(getMethodKey(obfName, signature)); + } + + public MethodMapping getMethodByDeobf(String deobfName, Signature signature) { + return m_methodsByDeobf.get(getMethodKey(deobfName, signature)); + } + + private String getMethodKey(String name, Signature signature) { + if (name == null) { + throw new IllegalArgumentException("name cannot be null!"); + } + if (signature == null) { + throw new IllegalArgumentException("signature cannot be null!"); + } + return name + signature; + } + + public void setMethodName(String obfName, Signature obfSignature, String deobfName) { + MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfName, obfSignature)); + if (methodMapping == null) { + methodMapping = createMethodMapping(obfName, obfSignature); + } else if (methodMapping.getDeobfName() != null) { + boolean wasRemoved = m_methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; + assert (wasRemoved); + } + methodMapping.setDeobfName(deobfName); + if (deobfName != null) { + boolean wasAdded = m_methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; + assert (wasAdded); + } + } + + //// ARGUMENTS //////// + + public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { + MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); + if (methodMapping == null) { + methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); + } + methodMapping.setArgumentName(argumentIndex, argumentName); + } + + public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { + m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); + } + + private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { + MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); + boolean wasAdded = m_methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; + assert (wasAdded); + return methodMapping; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(m_obfName); + buf.append(" <-> "); + buf.append(m_deobfName); + buf.append("\n"); + buf.append("Fields:\n"); + for (FieldMapping fieldMapping : fields()) { + buf.append("\t"); + buf.append(fieldMapping.getObfName()); + buf.append(" <-> "); + buf.append(fieldMapping.getDeobfName()); + buf.append("\n"); + } + buf.append("Methods:\n"); + for (MethodMapping methodMapping : m_methodsByObf.values()) { + buf.append(methodMapping.toString()); + buf.append("\n"); + } + buf.append("Inner Classes:\n"); + for (ClassMapping classMapping : m_innerClassesByObf.values()) { + buf.append("\t"); + buf.append(classMapping.getObfName()); + buf.append(" <-> "); + buf.append(classMapping.getDeobfName()); + buf.append("\n"); + } + return buf.toString(); + } + + @Override + public int compareTo(ClassMapping other) { + // sort by a, b, c, ... aa, ab, etc + if (m_obfName.length() != other.m_obfName.length()) { + return m_obfName.length() - other.m_obfName.length(); + } + return m_obfName.compareTo(other.m_obfName); + } + + public boolean renameObfClass(String oldObfClassName, String newObfClassName) { + + // rename inner classes + for (ClassMapping innerClassMapping : new ArrayList(m_innerClassesByObf.values())) { + if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) { + boolean wasRemoved = m_innerClassesByObf.remove(oldObfClassName) != null; + assert (wasRemoved); + boolean wasAdded = m_innerClassesByObf.put(newObfClassName, innerClassMapping) == null; + assert (wasAdded); + } + } + + // rename method signatures + for (MethodMapping methodMapping : new ArrayList(m_methodsByObf.values())) { + String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); + if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { + boolean wasRemoved = m_methodsByObf.remove(oldMethodKey) != null; + assert (wasRemoved); + boolean wasAdded = m_methodsByObf.put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; + assert (wasAdded); + } + } + + if (m_obfName.equals(oldObfClassName)) { + // rename this class + m_obfName = newObfClassName; + return true; + } + return false; + } + + public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { + MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); + if (methodMapping != null) { + return methodMapping.containsArgument(name); + } + return false; + } + + public static boolean isSimpleClassName(String name) { + return name.indexOf('/') < 0 && name.indexOf('$') < 0; + } +} diff --git a/src/cuchaz/enigma/mapping/ClassNameReplacer.java b/src/cuchaz/enigma/mapping/ClassNameReplacer.java new file mode 100644 index 00000000..bf984fd3 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ClassNameReplacer.java @@ -0,0 +1,5 @@ +package cuchaz.enigma.mapping; + +public interface ClassNameReplacer { + String replace(String className); +} diff --git a/src/cuchaz/enigma/mapping/ConstructorEntry.java b/src/cuchaz/enigma/mapping/ConstructorEntry.java new file mode 100644 index 00000000..5f3760f3 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ConstructorEntry.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +import cuchaz.enigma.Util; + +public class ConstructorEntry implements BehaviorEntry, Serializable { + + private static final long serialVersionUID = -868346075317366758L; + + private ClassEntry m_classEntry; + private Signature m_signature; + + public ConstructorEntry(ClassEntry classEntry) { + this(classEntry, null); + } + + public ConstructorEntry(ClassEntry classEntry, Signature signature) { + if (classEntry == null) { + throw new IllegalArgumentException("Class cannot be null!"); + } + + m_classEntry = classEntry; + m_signature = signature; + } + + public ConstructorEntry(ConstructorEntry other) { + m_classEntry = new ClassEntry(other.m_classEntry); + m_signature = other.m_signature; + } + + public ConstructorEntry(ConstructorEntry other, String newClassName) { + m_classEntry = new ClassEntry(newClassName); + m_signature = other.m_signature; + } + + @Override + public ClassEntry getClassEntry() { + return m_classEntry; + } + + @Override + public String getName() { + if (isStatic()) { + return ""; + } + return ""; + } + + public boolean isStatic() { + return m_signature == null; + } + + @Override + public Signature getSignature() { + return m_signature; + } + + @Override + public String getClassName() { + return m_classEntry.getName(); + } + + @Override + public ConstructorEntry cloneToNewClass(ClassEntry classEntry) { + return new ConstructorEntry(this, classEntry.getName()); + } + + @Override + public int hashCode() { + if (isStatic()) { + return Util.combineHashesOrdered(m_classEntry); + } else { + return Util.combineHashesOrdered(m_classEntry, m_signature); + } + } + + @Override + public boolean equals(Object other) { + if (other instanceof ConstructorEntry) { + return equals((ConstructorEntry)other); + } + return false; + } + + public boolean equals(ConstructorEntry other) { + if (isStatic() != other.isStatic()) { + return false; + } + + if (isStatic()) { + return m_classEntry.equals(other.m_classEntry); + } else { + return m_classEntry.equals(other.m_classEntry) && m_signature.equals(other.m_signature); + } + } + + @Override + public String toString() { + if (isStatic()) { + return m_classEntry.getName() + "." + getName(); + } else { + return m_classEntry.getName() + "." + getName() + m_signature; + } + } +} diff --git a/src/cuchaz/enigma/mapping/Entry.java b/src/cuchaz/enigma/mapping/Entry.java new file mode 100644 index 00000000..39e1507d --- /dev/null +++ b/src/cuchaz/enigma/mapping/Entry.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public interface Entry { + String getName(); + String getClassName(); + ClassEntry getClassEntry(); + Entry cloneToNewClass(ClassEntry classEntry); +} diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java new file mode 100644 index 00000000..60411c40 --- /dev/null +++ b/src/cuchaz/enigma/mapping/EntryPair.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public class EntryPair { + + public T obf; + public T deobf; + + public EntryPair(T obf, T deobf) { + this.obf = obf; + this.deobf = deobf; + } +} diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java new file mode 100644 index 00000000..6cc9eb78 --- /dev/null +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +import cuchaz.enigma.Util; + +public class FieldEntry implements Entry, Serializable { + + private static final long serialVersionUID = 3004663582802885451L; + + private ClassEntry m_classEntry; + private String m_name; + + // NOTE: this argument order is important for the MethodReader/MethodWriter + public FieldEntry(ClassEntry classEntry, String name) { + if (classEntry == null) { + throw new IllegalArgumentException("Class cannot be null!"); + } + if (name == null) { + throw new IllegalArgumentException("Field name cannot be null!"); + } + + m_classEntry = classEntry; + m_name = name; + } + + public FieldEntry(FieldEntry other) { + m_classEntry = new ClassEntry(other.m_classEntry); + m_name = other.m_name; + } + + public FieldEntry(FieldEntry other, String newClassName) { + m_classEntry = new ClassEntry(newClassName); + m_name = other.m_name; + } + + @Override + public ClassEntry getClassEntry() { + return m_classEntry; + } + + @Override + public String getName() { + return m_name; + } + + @Override + public String getClassName() { + return m_classEntry.getName(); + } + + @Override + public FieldEntry cloneToNewClass(ClassEntry classEntry) { + return new FieldEntry(this, classEntry.getName()); + } + + @Override + public int hashCode() { + return Util.combineHashesOrdered(m_classEntry, m_name); + } + + @Override + public boolean equals(Object other) { + if (other instanceof FieldEntry) { + return equals((FieldEntry)other); + } + return false; + } + + public boolean equals(FieldEntry other) { + return m_classEntry.equals(other.m_classEntry) && m_name.equals(other.m_name); + } + + @Override + public String toString() { + return m_classEntry.getName() + "." + m_name; + } +} diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java new file mode 100644 index 00000000..5f5c270d --- /dev/null +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +public class FieldMapping implements Serializable, Comparable { + + private static final long serialVersionUID = 8610742471440861315L; + + private String m_obfName; + private String m_deobfName; + + public FieldMapping(String obfName, String deobfName) { + m_obfName = obfName; + m_deobfName = NameValidator.validateFieldName(deobfName); + } + + public String getObfName() { + return m_obfName; + } + + public String getDeobfName() { + return m_deobfName; + } + + public void setDeobfName(String val) { + m_deobfName = NameValidator.validateFieldName(val); + } + + @Override + public int compareTo(FieldMapping other) { + return m_obfName.compareTo(other.m_obfName); + } +} diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java new file mode 100644 index 00000000..aacaf3b0 --- /dev/null +++ b/src/cuchaz/enigma/mapping/IllegalNameException.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public class IllegalNameException extends RuntimeException { + + private static final long serialVersionUID = -2279910052561114323L; + + private String m_name; + private String m_reason; + + public IllegalNameException(String name) { + this(name, null); + } + + public IllegalNameException(String name, String reason) { + m_name = name; + m_reason = reason; + } + + public String getReason() { + return m_reason; + } + + @Override + public String getMessage() { + StringBuilder buf = new StringBuilder(); + buf.append("Illegal name: "); + buf.append(m_name); + if (m_reason != null) { + buf.append(" because "); + buf.append(m_reason); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/mapping/JavassistUtil.java b/src/cuchaz/enigma/mapping/JavassistUtil.java new file mode 100644 index 00000000..0c446c4a --- /dev/null +++ b/src/cuchaz/enigma/mapping/JavassistUtil.java @@ -0,0 +1,83 @@ +package cuchaz.enigma.mapping; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.CtMethod; +import javassist.bytecode.Descriptor; +import javassist.expr.ConstructorCall; +import javassist.expr.FieldAccess; +import javassist.expr.MethodCall; +import javassist.expr.NewExpr; + +public class JavassistUtil { + + public static ClassEntry getClassEntry(CtClass c) { + return new ClassEntry(Descriptor.toJvmName(c.getName())); + } + + public static ClassEntry getSuperclassEntry(CtClass c) { + return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); + } + + public static MethodEntry getMethodEntry(CtMethod method) { + return new MethodEntry( + getClassEntry(method.getDeclaringClass()), + method.getName(), + new Signature(method.getMethodInfo().getDescriptor()) + ); + } + + public static MethodEntry getMethodEntry(MethodCall call) { + return new MethodEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + call.getMethodName(), + new Signature(call.getSignature()) + ); + } + + public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { + return new ConstructorEntry( + getClassEntry(constructor.getDeclaringClass()), + new Signature(constructor.getMethodInfo().getDescriptor()) + ); + } + + public static ConstructorEntry getConstructorEntry(ConstructorCall call) { + return new ConstructorEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + new Signature(call.getSignature()) + ); + } + + public static ConstructorEntry getConstructorEntry(NewExpr call) { + return new ConstructorEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + new Signature(call.getSignature()) + ); + } + + public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { + if (behavior instanceof CtMethod) { + return getMethodEntry((CtMethod)behavior); + } else if (behavior instanceof CtConstructor) { + return getConstructorEntry((CtConstructor)behavior); + } + throw new Error("behavior is neither Method nor Constructor!"); + } + + public static FieldEntry getFieldEntry(CtField field) { + return new FieldEntry( + getClassEntry(field.getDeclaringClass()), + field.getName() + ); + } + + public static FieldEntry getFieldEntry(FieldAccess call) { + return new FieldEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + call.getFieldName() + ); + } +} diff --git a/src/cuchaz/enigma/mapping/MappingParseException.java b/src/cuchaz/enigma/mapping/MappingParseException.java new file mode 100644 index 00000000..1974c222 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingParseException.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public class MappingParseException extends Exception { + + private static final long serialVersionUID = -5487280332892507236L; + + private int m_line; + private String m_message; + + public MappingParseException(int line, String message) { + m_line = line; + m_message = message; + } + + @Override + public String getMessage() { + return "Line " + m_line + ": " + m_message; + } +} diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java new file mode 100644 index 00000000..57d80013 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import cuchaz.enigma.analysis.TranslationIndex; + +public class Mappings implements Serializable { + + private static final long serialVersionUID = 4649790259460259026L; + + protected Map m_classesByObf; + protected Map m_classesByDeobf; + + public Mappings() { + m_classesByObf = Maps.newHashMap(); + m_classesByDeobf = Maps.newHashMap(); + } + + public Mappings(Iterable classes) { + this(); + + for (ClassMapping classMapping : classes) { + m_classesByObf.put(classMapping.getObfName(), classMapping); + if (classMapping.getDeobfName() != null) { + m_classesByDeobf.put(classMapping.getDeobfName(), classMapping); + } + } + } + + public Collection classes() { + assert (m_classesByObf.size() >= m_classesByDeobf.size()); + return m_classesByObf.values(); + } + + public void addClassMapping(ClassMapping classMapping) { + if (m_classesByObf.containsKey(classMapping.getObfName())) { + throw new Error("Already have mapping for " + classMapping.getObfName()); + } + boolean obfWasAdded = m_classesByObf.put(classMapping.getObfName(), classMapping) == null; + assert (obfWasAdded); + if (classMapping.getDeobfName() != null) { + if (m_classesByDeobf.containsKey(classMapping.getDeobfName())) { + throw new Error("Already have mapping for " + classMapping.getDeobfName()); + } + boolean deobfWasAdded = m_classesByDeobf.put(classMapping.getDeobfName(), classMapping) == null; + assert (deobfWasAdded); + } + } + + public void removeClassMapping(ClassMapping classMapping) { + boolean obfWasRemoved = m_classesByObf.remove(classMapping.getObfName()) != null; + assert (obfWasRemoved); + if (classMapping.getDeobfName() != null) { + boolean deobfWasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (deobfWasRemoved); + } + } + + public ClassMapping getClassByObf(ClassEntry entry) { + return getClassByObf(entry.getName()); + } + + public ClassMapping getClassByObf(String obfName) { + return m_classesByObf.get(obfName); + } + + public ClassMapping getClassByDeobf(ClassEntry entry) { + return getClassByDeobf(entry.getName()); + } + + public ClassMapping getClassByDeobf(String deobfName) { + return m_classesByDeobf.get(deobfName); + } + + public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { + switch (direction) { + case Deobfuscating: + + return new Translator(direction, m_classesByObf, index); + + case Obfuscating: + + // fill in the missing deobf class entries with obf entries + Map classes = Maps.newHashMap(); + for (ClassMapping classMapping : classes()) { + if (classMapping.getDeobfName() != null) { + classes.put(classMapping.getDeobfName(), classMapping); + } else { + classes.put(classMapping.getObfName(), classMapping); + } + } + + // translate the translation index + // NOTE: this isn't actually recursive + TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); + + return new Translator(direction, classes, deobfIndex); + + default: + throw new Error("Invalid translation direction!"); + } + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + for (ClassMapping classMapping : m_classesByObf.values()) { + buf.append(classMapping.toString()); + buf.append("\n"); + } + return buf.toString(); + } + + public void renameObfClass(String oldObfName, String newObfName) { + for (ClassMapping classMapping : new ArrayList(classes())) { + if (classMapping.renameObfClass(oldObfName, newObfName)) { + boolean wasRemoved = m_classesByObf.remove(oldObfName) != null; + assert (wasRemoved); + boolean wasAdded = m_classesByObf.put(newObfName, classMapping) == null; + assert (wasAdded); + } + } + } + + public Set getAllObfClassNames() { + final Set classNames = Sets.newHashSet(); + for (ClassMapping classMapping : classes()) { + + // add the class name + classNames.add(classMapping.getObfName()); + + // add classes from method signatures + for (MethodMapping methodMapping : classMapping.methods()) { + for (Type type : methodMapping.getObfSignature().types()) { + if (type.hasClass()) { + classNames.add(type.getClassEntry().getClassName()); + } + } + } + } + return classNames; + } + + public boolean containsDeobfClass(String deobfName) { + return m_classesByDeobf.containsKey(deobfName); + } + + public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { + ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName()); + if (classMapping != null) { + return classMapping.containsDeobfField(deobfName); + } + return false; + } + + public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature deobfSignature) { + ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName()); + if (classMapping != null) { + return classMapping.containsDeobfMethod(deobfName, deobfSignature); + } + return false; + } + + public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { + ClassMapping classMapping = m_classesByObf.get(obfBehaviorEntry.getClassName()); + if (classMapping != null) { + return classMapping.containsArgument(obfBehaviorEntry, name); + } + return false; + } +} diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java new file mode 100644 index 00000000..adf460e6 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.Deque; + +import com.google.common.collect.Queues; + +import cuchaz.enigma.Constants; + +public class MappingsReader { + + public Mappings read(Reader in) throws IOException, MappingParseException { + return read(new BufferedReader(in)); + } + + public Mappings read(BufferedReader in) throws IOException, MappingParseException { + Mappings mappings = new Mappings(); + Deque mappingStack = Queues.newArrayDeque(); + + int lineNumber = 0; + String line = null; + while ( (line = in.readLine()) != null) { + lineNumber++; + + // strip comments + int commentPos = line.indexOf('#'); + if (commentPos >= 0) { + line = line.substring(0, commentPos); + } + + // skip blank lines + if (line.trim().length() <= 0) { + continue; + } + + // get the indent of this line + int indent = 0; + for (int i = 0; i < line.length(); i++) { + if (line.charAt(i) != '\t') { + break; + } + indent++; + } + + // handle stack pops + while (indent < mappingStack.size()) { + mappingStack.pop(); + } + + String[] parts = line.trim().split("\\s"); + try { + // read the first token + String token = parts[0]; + + if (token.equalsIgnoreCase("CLASS")) { + ClassMapping classMapping; + if (indent == 0) { + // outer class + classMapping = readClass(parts, false); + mappings.addClassMapping(classMapping); + } else if (indent == 1) { + // inner class + if (! (mappingStack.getFirst() instanceof ClassMapping)) { + throw new MappingParseException(lineNumber, "Unexpected CLASS entry here!"); + } + + classMapping = readClass(parts, true); + ((ClassMapping)mappingStack.getFirst()).addInnerClassMapping(classMapping); + } else { + throw new MappingParseException(lineNumber, "Unexpected CLASS entry nesting!"); + } + mappingStack.push(classMapping); + } else if (token.equalsIgnoreCase("FIELD")) { + if (mappingStack.isEmpty() || ! (mappingStack.getFirst() instanceof ClassMapping)) { + throw new MappingParseException(lineNumber, "Unexpected FIELD entry here!"); + } + ((ClassMapping)mappingStack.getFirst()).addFieldMapping(readField(parts)); + } else if (token.equalsIgnoreCase("METHOD")) { + if (mappingStack.isEmpty() || ! (mappingStack.getFirst() instanceof ClassMapping)) { + throw new MappingParseException(lineNumber, "Unexpected METHOD entry here!"); + } + MethodMapping methodMapping = readMethod(parts); + ((ClassMapping)mappingStack.getFirst()).addMethodMapping(methodMapping); + mappingStack.push(methodMapping); + } else if (token.equalsIgnoreCase("ARG")) { + if (mappingStack.isEmpty() || ! (mappingStack.getFirst() instanceof MethodMapping)) { + throw new MappingParseException(lineNumber, "Unexpected ARG entry here!"); + } + ((MethodMapping)mappingStack.getFirst()).addArgumentMapping(readArgument(parts)); + } + } catch (ArrayIndexOutOfBoundsException | NumberFormatException ex) { + throw new MappingParseException(lineNumber, "Malformed line!"); + } + } + + return mappings; + } + + private ArgumentMapping readArgument(String[] parts) { + return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); + } + + private ClassMapping readClass(String[] parts, boolean makeSimple) { + if (parts.length == 2) { + String obfName = processName(parts[1], makeSimple); + return new ClassMapping(obfName); + } else { + String obfName = processName(parts[1], makeSimple); + String deobfName = processName(parts[2], makeSimple); + return new ClassMapping(obfName, deobfName); + } + } + + private String processName(String name, boolean makeSimple) { + if (makeSimple) { + return new ClassEntry(name).getSimpleName(); + } else { + return moveClassOutOfDefaultPackage(name, Constants.NonePackage); + } + } + + private String moveClassOutOfDefaultPackage(String className, String newPackageName) { + ClassEntry classEntry = new ClassEntry(className); + if (classEntry.isInDefaultPackage()) { + return newPackageName + "/" + classEntry.getName(); + } + return className; + } + + private FieldMapping readField(String[] parts) { + return new FieldMapping(parts[1], parts[2]); + } + + private MethodMapping readMethod(String[] parts) { + if (parts.length == 3) { + String obfName = parts[1]; + Signature obfSignature = moveSignatureOutOfDefaultPackage(new Signature(parts[2]), Constants.NonePackage); + return new MethodMapping(obfName, obfSignature); + } else { + String obfName = parts[1]; + String deobfName = parts[2]; + Signature obfSignature = moveSignatureOutOfDefaultPackage(new Signature(parts[3]), Constants.NonePackage); + if (obfName.equals(deobfName)) { + return new MethodMapping(obfName, obfSignature); + } else { + return new MethodMapping(obfName, obfSignature, deobfName); + } + } + } + + private Signature moveSignatureOutOfDefaultPackage(Signature signature, final String newPackageName) { + return new Signature(signature, new ClassNameReplacer() { + @Override + public String replace(String className) { + ClassEntry classEntry = new ClassEntry(className); + if (classEntry.isInDefaultPackage()) { + return newPackageName + "/" + className; + } + return null; + } + }); + } +} diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java new file mode 100644 index 00000000..0a41c2b8 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -0,0 +1,237 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.util.Set; +import java.util.zip.GZIPOutputStream; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.analysis.JarIndex; + +public class MappingsRenamer { + + private JarIndex m_index; + private Mappings m_mappings; + + public MappingsRenamer(JarIndex index, Mappings mappings) { + m_index = index; + m_mappings = mappings; + } + + public void setClassName(ClassEntry obf, String deobfName) { + deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass()); + ClassEntry targetEntry = new ClassEntry(deobfName); + if (m_mappings.containsDeobfClass(deobfName) || m_index.containsObfClass(targetEntry)) { + throw new IllegalNameException(deobfName, "There is already a class with that name"); + } + + ClassMapping classMapping = getOrCreateClassMapping(obf); + + if (obf.isInnerClass()) { + classMapping.setInnerClassName(obf.getInnerClassName(), deobfName); + } else { + if (classMapping.getDeobfName() != null) { + boolean wasRemoved = m_mappings.m_classesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (wasRemoved); + } + classMapping.setDeobfName(deobfName); + boolean wasAdded = m_mappings.m_classesByDeobf.put(deobfName, classMapping) == null; + assert (wasAdded); + } + } + + public void removeClassMapping(ClassEntry obf) { + ClassMapping classMapping = getClassMapping(obf); + if (obf.isInnerClass()) { + classMapping.setInnerClassName(obf.getName(), null); + } else { + boolean wasRemoved = m_mappings.m_classesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (wasRemoved); + classMapping.setDeobfName(null); + } + } + + public void markClassAsDeobfuscated(ClassEntry obf) { + ClassMapping classMapping = getOrCreateClassMapping(obf); + if (obf.isInnerClass()) { + String innerClassName = Constants.NonePackage + "/" + obf.getInnerClassName(); + classMapping.setInnerClassName(innerClassName, innerClassName); + } else { + classMapping.setDeobfName(obf.getName()); + boolean wasAdded = m_mappings.m_classesByDeobf.put(obf.getName(), classMapping) == null; + assert (wasAdded); + } + } + + public void setFieldName(FieldEntry obf, String deobfName) { + deobfName = NameValidator.validateFieldName(deobfName); + FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName); + if (m_mappings.containsDeobfField(obf.getClassEntry(), deobfName) || m_index.containsObfField(targetEntry)) { + throw new IllegalNameException(deobfName, "There is already a field with that name"); + } + + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setFieldName(obf.getName(), deobfName); + } + + public void removeFieldMapping(FieldEntry obf) { + ClassMapping classMapping = getClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setFieldName(obf.getName(), null); + } + + public void markFieldAsDeobfuscated(FieldEntry obf) { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setFieldName(obf.getName(), obf.getName()); + } + + public void setMethodTreeName(MethodEntry obf, String deobfName) { + Set implementations = m_index.getRelatedMethodImplementations(obf); + + deobfName = NameValidator.validateMethodName(deobfName); + for (MethodEntry entry : implementations) { + Signature deobfSignature = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateSignature(obf.getSignature()); + MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, deobfSignature); + if (m_mappings.containsDeobfMethod(entry.getClassEntry(), deobfName, entry.getSignature()) || m_index.containsObfBehavior(targetEntry)) { + String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(entry.getClassName()); + throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); + } + } + + for (MethodEntry entry : implementations) { + setMethodName(entry, deobfName); + } + } + + public void setMethodName(MethodEntry obf, String deobfName) { + deobfName = NameValidator.validateMethodName(deobfName); + MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); + if (m_mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) || m_index.containsObfBehavior(targetEntry)) { + String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(obf.getClassName()); + throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); + } + + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); + } + + public void removeMethodTreeMapping(MethodEntry obf) { + for (MethodEntry implementation : m_index.getRelatedMethodImplementations(obf)) { + removeMethodMapping(implementation); + } + } + + public void removeMethodMapping(MethodEntry obf) { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getSignature(), null); + } + + public void markMethodTreeAsDeobfuscated(MethodEntry obf) { + for (MethodEntry implementation : m_index.getRelatedMethodImplementations(obf)) { + markMethodAsDeobfuscated(implementation); + } + } + + public void markMethodAsDeobfuscated(MethodEntry obf) { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); + } + + public void setArgumentName(ArgumentEntry obf, String deobfName) { + deobfName = NameValidator.validateArgumentName(deobfName); + // NOTE: don't need to check arguments for name collisions with names determined by Procyon + if (m_mappings.containsArgument(obf.getBehaviorEntry(), deobfName)) { + throw new IllegalNameException(deobfName, "There is already an argument with that name"); + } + + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); + } + + public void removeArgumentMapping(ArgumentEntry obf) { + ClassMapping classMapping = getClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); + } + + public void markArgumentAsDeobfuscated(ArgumentEntry obf) { + ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); + } + + public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { + classMapping.removeFieldMapping(fieldMapping); + ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); + if (!targetClassMapping.containsObfField(fieldMapping.getObfName())) { + if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName())) { + targetClassMapping.addFieldMapping(fieldMapping); + return true; + } else { + System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName()); + } + } + return false; + } + + public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { + classMapping.removeMethodMapping(methodMapping); + ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); + if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { + if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { + targetClassMapping.addMethodMapping(methodMapping); + return true; + } else { + System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); + } + } + return false; + } + + public void write(OutputStream out) throws IOException { + // TEMP: just use the object output for now. We can find a more efficient storage format later + GZIPOutputStream gzipout = new GZIPOutputStream(out); + ObjectOutputStream oout = new ObjectOutputStream(gzipout); + oout.writeObject(this); + gzipout.finish(); + } + + private ClassMapping getClassMapping(ClassEntry obfClassEntry) { + return m_mappings.m_classesByObf.get(obfClassEntry.getOuterClassName()); + } + + private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { + String obfClassName = obfClassEntry.getOuterClassName(); + ClassMapping classMapping = m_mappings.m_classesByObf.get(obfClassName); + if (classMapping == null) { + classMapping = new ClassMapping(obfClassName); + boolean obfWasAdded = m_mappings.m_classesByObf.put(classMapping.getObfName(), classMapping) == null; + assert (obfWasAdded); + } + return classMapping; + } + + private ClassMapping getClassMappingOrInnerClassMapping(ClassEntry obfClassEntry) { + ClassMapping classMapping = getClassMapping(obfClassEntry); + if (obfClassEntry.isInDefaultPackage()) { + classMapping = classMapping.getInnerClassByObf(obfClassEntry.getInnerClassName()); + } + return classMapping; + } + + private ClassMapping getOrCreateClassMappingOrInnerClassMapping(ClassEntry obfClassEntry) { + ClassMapping classMapping = getOrCreateClassMapping(obfClassEntry); + if (obfClassEntry.isInnerClass()) { + classMapping = classMapping.getOrCreateInnerClass(obfClassEntry.getInnerClassName()); + } + return classMapping; + } +} diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java new file mode 100644 index 00000000..5ac409fc --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class MappingsWriter { + + public void write(Writer out, Mappings mappings) throws IOException { + write(new PrintWriter(out), mappings); + } + + public void write(PrintWriter out, Mappings mappings) throws IOException { + for (ClassMapping classMapping : sorted(mappings.classes())) { + write(out, classMapping, 0); + } + } + + private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { + if (classMapping.getDeobfName() == null) { + out.format("%sCLASS %s\n", getIndent(depth), classMapping.getObfName()); + } else { + out.format("%sCLASS %s %s\n", getIndent(depth), classMapping.getObfName(), classMapping.getDeobfName()); + } + + for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { + write(out, innerClassMapping, depth + 1); + } + + for (FieldMapping fieldMapping : sorted(classMapping.fields())) { + write(out, fieldMapping, depth + 1); + } + + for (MethodMapping methodMapping : sorted(classMapping.methods())) { + write(out, methodMapping, depth + 1); + } + } + + private void write(PrintWriter out, FieldMapping fieldMapping, int depth) throws IOException { + out.format("%sFIELD %s %s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName()); + } + + private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { + if (methodMapping.getDeobfName() == null) { + out.format("%sMETHOD %s %s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature()); + } else { + out.format("%sMETHOD %s %s %s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature()); + } + + for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { + write(out, argumentMapping, depth + 1); + } + } + + private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) throws IOException { + out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName()); + } + + private > List sorted(Iterable classes) { + List out = new ArrayList(); + for (T t : classes) { + out.add(t); + } + Collections.sort(out); + return out; + } + + private String getIndent(int depth) { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < depth; i++) { + buf.append("\t"); + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java new file mode 100644 index 00000000..057e02b9 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; + +import cuchaz.enigma.Util; + +public class MethodEntry implements BehaviorEntry, Serializable { + + private static final long serialVersionUID = 4770915224467247458L; + + private ClassEntry m_classEntry; + private String m_name; + private Signature m_signature; + + public MethodEntry(ClassEntry classEntry, String name, Signature signature) { + if (classEntry == null) { + throw new IllegalArgumentException("Class cannot be null!"); + } + if (name == null) { + throw new IllegalArgumentException("Method name cannot be null!"); + } + if (signature == null) { + throw new IllegalArgumentException("Method signature cannot be null!"); + } + if (name.startsWith("<")) { + throw new IllegalArgumentException("Don't use MethodEntry for a constructor!"); + } + + m_classEntry = classEntry; + m_name = name; + m_signature = signature; + } + + public MethodEntry(MethodEntry other) { + m_classEntry = new ClassEntry(other.m_classEntry); + m_name = other.m_name; + m_signature = other.m_signature; + } + + public MethodEntry(MethodEntry other, String newClassName) { + m_classEntry = new ClassEntry(newClassName); + m_name = other.m_name; + m_signature = other.m_signature; + } + + @Override + public ClassEntry getClassEntry() { + return m_classEntry; + } + + @Override + public String getName() { + return m_name; + } + + @Override + public Signature getSignature() { + return m_signature; + } + + @Override + public String getClassName() { + return m_classEntry.getName(); + } + + @Override + public MethodEntry cloneToNewClass(ClassEntry classEntry) { + return new MethodEntry(this, classEntry.getName()); + } + + @Override + public int hashCode() { + return Util.combineHashesOrdered(m_classEntry, m_name, m_signature); + } + + @Override + public boolean equals(Object other) { + if (other instanceof MethodEntry) { + return equals((MethodEntry)other); + } + return false; + } + + public boolean equals(MethodEntry other) { + return m_classEntry.equals(other.m_classEntry) + && m_name.equals(other.m_name) + && m_signature.equals(other.m_signature); + } + + @Override + public String toString() { + return m_classEntry.getName() + "." + m_name + m_signature; + } +} diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java new file mode 100644 index 00000000..1704428a --- /dev/null +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.Serializable; +import java.util.Map; +import java.util.TreeMap; + +public class MethodMapping implements Serializable, Comparable { + + private static final long serialVersionUID = -4409570216084263978L; + + private String m_obfName; + private String m_deobfName; + private Signature m_obfSignature; + private Map m_arguments; + + public MethodMapping(String obfName, Signature obfSignature) { + this(obfName, obfSignature, null); + } + + public MethodMapping(String obfName, Signature obfSignature, String deobfName) { + if (obfName == null) { + throw new IllegalArgumentException("obf name cannot be null!"); + } + if (obfSignature == null) { + throw new IllegalArgumentException("obf signature cannot be null!"); + } + m_obfName = obfName; + m_deobfName = NameValidator.validateMethodName(deobfName); + m_obfSignature = obfSignature; + m_arguments = new TreeMap(); + } + + public String getObfName() { + return m_obfName; + } + + public String getDeobfName() { + return m_deobfName; + } + + public void setDeobfName(String val) { + m_deobfName = NameValidator.validateMethodName(val); + } + + public Signature getObfSignature() { + return m_obfSignature; + } + + public Iterable arguments() { + return m_arguments.values(); + } + + public boolean isConstructor() { + return m_obfName.startsWith("<"); + } + + public void addArgumentMapping(ArgumentMapping argumentMapping) { + boolean wasAdded = m_arguments.put(argumentMapping.getIndex(), argumentMapping) == null; + assert (wasAdded); + } + + public String getObfArgumentName(int index) { + ArgumentMapping argumentMapping = m_arguments.get(index); + if (argumentMapping != null) { + return argumentMapping.getName(); + } + + return null; + } + + public String getDeobfArgumentName(int index) { + ArgumentMapping argumentMapping = m_arguments.get(index); + if (argumentMapping != null) { + return argumentMapping.getName(); + } + + return null; + } + + public void setArgumentName(int index, String name) { + ArgumentMapping argumentMapping = m_arguments.get(index); + if (argumentMapping == null) { + argumentMapping = new ArgumentMapping(index, name); + boolean wasAdded = m_arguments.put(index, argumentMapping) == null; + assert (wasAdded); + } else { + argumentMapping.setName(name); + } + } + + public void removeArgumentName(int index) { + boolean wasRemoved = m_arguments.remove(index) != null; + assert (wasRemoved); + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("\t"); + buf.append(m_obfName); + buf.append(" <-> "); + buf.append(m_deobfName); + buf.append("\n"); + buf.append("\t"); + buf.append(m_obfSignature); + buf.append("\n"); + buf.append("\tArguments:\n"); + for (ArgumentMapping argumentMapping : m_arguments.values()) { + buf.append("\t\t"); + buf.append(argumentMapping.getIndex()); + buf.append(" -> "); + buf.append(argumentMapping.getName()); + buf.append("\n"); + } + return buf.toString(); + } + + @Override + public int compareTo(MethodMapping other) { + return (m_obfName + m_obfSignature).compareTo(other.m_obfName + other.m_obfSignature); + } + + public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { + + // rename obf classes in the signature + Signature newSignature = new Signature(m_obfSignature, new ClassNameReplacer() { + @Override + public String replace(String className) { + if (className.equals(oldObfClassName)) { + return newObfClassName; + } + return null; + } + }); + + if (!newSignature.equals(m_obfSignature)) { + m_obfSignature = newSignature; + return true; + } + return false; + } + + public boolean containsArgument(String name) { + for (ArgumentMapping argumentMapping : m_arguments.values()) { + if (argumentMapping.getName().equals(name)) { + return true; + } + } + return false; + } +} diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java new file mode 100644 index 00000000..35a17f90 --- /dev/null +++ b/src/cuchaz/enigma/mapping/NameValidator.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +import javassist.bytecode.Descriptor; + +public class NameValidator { + + private static final Pattern IdentifierPattern; + private static final Pattern ClassPattern; + private static final List ReservedWords = Arrays.asList( + "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", + "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", + "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", + "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", + "long", "strictfp", "volatile", "const", "float", "native", "super", "while" + ); + + static { + + // java allows all kinds of weird characters... + StringBuilder startChars = new StringBuilder(); + StringBuilder partChars = new StringBuilder(); + for (int i = Character.MIN_CODE_POINT; i <= Character.MAX_CODE_POINT; i++) { + if (Character.isJavaIdentifierStart(i)) { + startChars.appendCodePoint(i); + } + if (Character.isJavaIdentifierPart(i)) { + partChars.appendCodePoint(i); + } + } + + String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; + IdentifierPattern = Pattern.compile(identifierRegex); + ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex)); + } + + public static String validateClassName(String name, boolean packageRequired) { + if (name == null) { + return null; + } + if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { + throw new IllegalNameException(name, "This doesn't look like a legal class name"); + } + if (packageRequired && new ClassEntry(name).getPackageName() == null) { + throw new IllegalNameException(name, "Class must be in a package"); + } + return Descriptor.toJvmName(name); + } + + public static String validateFieldName(String name) { + if (name == null) { + return null; + } + if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) { + throw new IllegalNameException(name, "This doesn't look like a legal identifier"); + } + return name; + } + + public static String validateMethodName(String name) { + return validateFieldName(name); + } + + public static String validateArgumentName(String name) { + return validateFieldName(name); + } +} diff --git a/src/cuchaz/enigma/mapping/Signature.java b/src/cuchaz/enigma/mapping/Signature.java new file mode 100644 index 00000000..ff7f8070 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Signature.java @@ -0,0 +1,109 @@ +package cuchaz.enigma.mapping; + +import java.util.List; + +import com.beust.jcommander.internal.Lists; + +import cuchaz.enigma.Util; + +public class Signature { + + private List m_argumentTypes; + private Type m_returnType; + + public Signature(String signature) { + try { + m_argumentTypes = Lists.newArrayList(); + int i=0; + while (i getArgumentTypes() { + return m_argumentTypes; + } + + public Type getReturnType() { + return m_returnType; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("("); + for (Type type : m_argumentTypes) { + buf.append(type.toString()); + } + buf.append(")"); + buf.append(m_returnType.toString()); + return buf.toString(); + } + + public Iterable types() { + List types = Lists.newArrayList(); + types.addAll(m_argumentTypes); + types.add(m_returnType); + return types; + } + + public Iterable classes() { + List out = Lists.newArrayList(); + for (Type type : types()) { + if (type.isClass()) { + out.add(type.getClassEntry()); + } + } + return out; + } + + @Override + public boolean equals(Object other) { + if (other instanceof Signature) { + return equals((Signature)other); + } + return false; + } + + public boolean equals(Signature other) { + return m_argumentTypes.equals(other.m_argumentTypes) && m_returnType.equals(other.m_returnType); + } + + @Override + public int hashCode() { + return Util.combineHashesOrdered(m_argumentTypes.hashCode(), m_returnType.hashCode()); + } + + public boolean hasClass(ClassEntry classEntry) { + for (Type type : types()) { + if (type.hasClass() && type.getClassEntry().equals(classEntry)) { + return true; + } + } + return false; + } +} diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java new file mode 100644 index 00000000..3477cd56 --- /dev/null +++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +import com.google.common.collect.Lists; + +public class SignatureUpdater { + + public interface ClassNameUpdater { + String update(String className); + } + + public static String update(String signature, ClassNameUpdater updater) { + try { + StringBuilder buf = new StringBuilder(); + + // read the signature character-by-character + StringReader reader = new StringReader(signature); + int i = -1; + while ( (i = reader.read()) != -1) { + char c = (char)i; + + // does this character start a class name? + if (c == 'L') { + // update the class name and add it to the buffer + buf.append('L'); + String className = readClass(reader); + if (className == null) { + throw new IllegalArgumentException("Malformed signature: " + signature); + } + buf.append(updater.update(className)); + buf.append(';'); + } else { + // copy the character into the buffer + buf.append(c); + } + } + + return buf.toString(); + } catch (IOException ex) { + // I'm pretty sure a StringReader will never throw one of these + throw new Error(ex); + } + } + + private static String readClass(StringReader reader) throws IOException { + // read all the characters in the buffer until we hit a ';' + // remember to treat generics correctly + StringBuilder buf = new StringBuilder(); + int depth = 0; + int i = -1; + while ( (i = reader.read()) != -1) { + char c = (char)i; + + if (c == '<') { + depth++; + } else if (c == '>') { + depth--; + } else if (depth == 0) { + if (c == ';') { + return buf.toString(); + } else { + buf.append(c); + } + } + } + + return null; + } + + public static List getClasses(String signature) { + final List classNames = Lists.newArrayList(); + update(signature, new ClassNameUpdater() { + @Override + public String update(String className) { + classNames.add(className); + return className; + } + }); + return classNames; + } +} diff --git a/src/cuchaz/enigma/mapping/TranslationDirection.java b/src/cuchaz/enigma/mapping/TranslationDirection.java new file mode 100644 index 00000000..d1b14cd5 --- /dev/null +++ b/src/cuchaz/enigma/mapping/TranslationDirection.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public enum TranslationDirection { + + Deobfuscating { + @Override + public T choose(T deobfChoice, T obfChoice) { + return deobfChoice; + } + }, + Obfuscating { + @Override + public T choose(T deobfChoice, T obfChoice) { + return obfChoice; + } + }; + + public abstract T choose(T deobfChoice, T obfChoice); +} diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java new file mode 100644 index 00000000..5eba18ce --- /dev/null +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +import java.util.Map; + +import com.google.common.collect.Maps; + +import cuchaz.enigma.analysis.TranslationIndex; + +public class Translator { + + private TranslationDirection m_direction; + private Map m_classes; + private TranslationIndex m_index; + + public Translator() { + m_direction = null; + m_classes = Maps.newHashMap(); + } + + public Translator(TranslationDirection direction, Map classes, TranslationIndex index) { + m_direction = direction; + m_classes = classes; + m_index = index; + } + + @SuppressWarnings("unchecked") + public T translateEntry(T entry) { + if (entry instanceof ClassEntry) { + return (T)translateEntry((ClassEntry)entry); + } else if (entry instanceof FieldEntry) { + return (T)translateEntry((FieldEntry)entry); + } else if (entry instanceof MethodEntry) { + return (T)translateEntry((MethodEntry)entry); + } else if (entry instanceof ConstructorEntry) { + return (T)translateEntry((ConstructorEntry)entry); + } else if (entry instanceof ArgumentEntry) { + return (T)translateEntry((ArgumentEntry)entry); + } else { + throw new Error("Unknown entry type: " + entry.getClass().getName()); + } + } + + public String translateClass(String className) { + return translate(new ClassEntry(className)); + } + + public String translate(ClassEntry in) { + ClassMapping classMapping = m_classes.get(in.getOuterClassName()); + if (classMapping != null) { + if (in.isInnerClass()) { + // translate the inner class + String translatedInnerClassName = m_direction.choose( + classMapping.getDeobfInnerClassName(in.getInnerClassName()), + classMapping.getObfInnerClassName(in.getInnerClassName()) + ); + if (translatedInnerClassName != null) { + // try to translate the outer name + String translatedOuterClassName = m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName()); + if (translatedOuterClassName != null) { + return translatedOuterClassName + "$" + translatedInnerClassName; + } else { + return in.getOuterClassName() + "$" + translatedInnerClassName; + } + } + } else { + // just return outer + return m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName()); + } + } + return null; + } + + public ClassEntry translateEntry(ClassEntry in) { + + // can we translate the inner class? + String name = translate(in); + if (name != null) { + return new ClassEntry(name); + } + + if (in.isInnerClass()) { + + // guess not. just translate the outer class name then + String outerClassName = translate(in.getOuterClassEntry()); + if (outerClassName != null) { + return new ClassEntry(outerClassName + "$" + in.getInnerClassName()); + } + } + + return in; + } + + public String translate(FieldEntry in) { + + // resolve the class entry + ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in); + if (resolvedClassEntry != null) { + + // look for the class + ClassMapping classMapping = findClassMapping(resolvedClassEntry); + if (classMapping != null) { + + // look for the field + String translatedName = m_direction.choose( + classMapping.getDeobfFieldName(in.getName()), + classMapping.getObfFieldName(in.getName()) + ); + if (translatedName != null) { + return translatedName; + } + } + } + return null; + } + + public FieldEntry translateEntry(FieldEntry in) { + String name = translate(in); + if (name == null) { + name = in.getName(); + } + return new FieldEntry(translateEntry(in.getClassEntry()), name); + } + + public String translate(MethodEntry in) { + + // resolve the class entry + ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in); + if (resolvedClassEntry != null) { + + // look for class + ClassMapping classMapping = findClassMapping(resolvedClassEntry); + if (classMapping != null) { + + // look for the method + MethodMapping methodMapping = m_direction.choose( + classMapping.getMethodByObf(in.getName(), in.getSignature()), + classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature())) + ); + if (methodMapping != null) { + return m_direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); + } + } + } + return null; + } + + public MethodEntry translateEntry(MethodEntry in) { + String name = translate(in); + if (name == null) { + name = in.getName(); + } + return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature())); + } + + public ConstructorEntry translateEntry(ConstructorEntry in) { + if (in.isStatic()) { + return new ConstructorEntry(translateEntry(in.getClassEntry())); + } else { + return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature())); + } + } + + public BehaviorEntry translateEntry(BehaviorEntry in) { + if (in instanceof MethodEntry) { + return translateEntry((MethodEntry)in); + } else if (in instanceof ConstructorEntry) { + return translateEntry((ConstructorEntry)in); + } + throw new Error("Wrong entry type!"); + } + + public String translate(ArgumentEntry in) { + + // look for the class + ClassMapping classMapping = findClassMapping(in.getClassEntry()); + if (classMapping != null) { + + // look for the method + MethodMapping methodMapping = m_direction.choose( + classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()), + classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature())) + ); + if (methodMapping != null) { + return m_direction.choose( + methodMapping.getDeobfArgumentName(in.getIndex()), + methodMapping.getObfArgumentName(in.getIndex()) + ); + } + } + return null; + } + + public ArgumentEntry translateEntry(ArgumentEntry in) { + String name = translate(in); + if (name == null) { + name = in.getName(); + } + return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name); + } + + public Type translateType(Type type) { + return new Type(type, new ClassNameReplacer() { + @Override + public String replace(String className) { + return translateClass(className); + } + }); + } + + public Signature translateSignature(Signature signature) { + return new Signature(signature, new ClassNameReplacer() { + @Override + public String replace(String className) { + return translateClass(className); + } + }); + } + + private ClassMapping findClassMapping(ClassEntry classEntry) { + ClassMapping classMapping = m_classes.get(classEntry.getOuterClassName()); + if (classMapping != null && classEntry.isInnerClass()) { + classMapping = m_direction.choose( + classMapping.getInnerClassByObf(classEntry.getInnerClassName()), + classMapping.getInnerClassByDeobfThenObf(classEntry.getInnerClassName()) + ); + } + return classMapping; + } +} diff --git a/src/cuchaz/enigma/mapping/Type.java b/src/cuchaz/enigma/mapping/Type.java new file mode 100644 index 00000000..9f5d52f0 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Type.java @@ -0,0 +1,218 @@ +package cuchaz.enigma.mapping; + +import java.util.Map; + +import com.google.common.collect.Maps; + +public class Type { + + public enum Primitive { + Byte('B'), + Character('C'), + Short('S'), + Integer('I'), + Long('J'), + Float('F'), + Double('D'), + Boolean('Z'); + + private static final Map m_lookup; + + static { + m_lookup = Maps.newTreeMap(); + for (Primitive val : values()) { + m_lookup.put(val.getCode(), val); + } + } + + public static Primitive get(char code) { + return m_lookup.get(code); + } + + private char m_code; + + private Primitive(char code) { + m_code = code; + } + + public char getCode() { + return m_code; + } + } + + public static String parseFirst(String in) { + + if (in == null || in.length() <= 0) { + throw new IllegalArgumentException("No type to parse, input is empty!"); + } + + // read one type from the input + + char c = in.charAt(0); + + // first check for void + if (c == 'V') { + return "V"; + } + + // then check for primitives + Primitive primitive = Primitive.get(c); + if (primitive != null) { + return in.substring(0, 1); + } + + // then check for classes + if (c == 'L') { + return readClass(in); + } + + // then check for arrays + int dim = countArrayDimension(in); + if (dim > 0) { + String arrayType = Type.parseFirst(in.substring(dim)); + return in.substring(0, dim + arrayType.length()); + } + + throw new IllegalArgumentException("don't know how to parse: " + in); + } + + private String m_name; + + public Type(String name) { + m_name = name; + } + + public Type(ClassEntry classEntry) { + m_name = "L" + classEntry.getClassName() + ";"; + } + + public Type(Type type, ClassNameReplacer replacer) { + m_name = type.m_name; + if (type.isClass()) { + String replacedName = replacer.replace(type.getClassEntry().getClassName()); + if (replacedName != null) { + m_name = "L" + replacedName + ";"; + } + } else if (type.isArray() && type.hasClass()) { + String replacedName = replacer.replace(type.getClassEntry().getClassName()); + if (replacedName != null) { + m_name = Type.getArrayPrefix(type.getArrayDimension()) + "L" + replacedName + ";"; + } + } + } + + @Override + public String toString() { + return m_name; + } + + public boolean isVoid() { + return m_name.length() == 1 && m_name.charAt(0) == 'V'; + } + + public boolean isPrimitive() { + return m_name.length() == 1 && Primitive.get(m_name.charAt(0)) != null; + } + + public Primitive getPrimitive() { + if (!isPrimitive()) { + throw new IllegalStateException("not a primitive"); + } + return Primitive.get(m_name.charAt(0)); + } + + public boolean isClass() { + return m_name.charAt(0) == 'L' && m_name.charAt(m_name.length() - 1) == ';'; + } + + public ClassEntry getClassEntry() { + if (isClass()) { + String name = m_name.substring(1, m_name.length() - 1); + + int pos = name.indexOf('<'); + if (pos >= 0) { + // remove the parameters from the class name + name = name.substring(0, pos); + } + + return new ClassEntry(name); + + } else if (isArray() && getArrayType().isClass()) { + return getArrayType().getClassEntry(); + } else { + throw new IllegalStateException("type doesn't have a class"); + } + } + + public boolean isArray() { + return m_name.charAt(0) == '['; + } + + public int getArrayDimension() { + if (!isArray()) { + throw new IllegalStateException("not an array"); + } + return countArrayDimension(m_name); + } + + public Type getArrayType() { + if (!isArray()) { + throw new IllegalStateException("not an array"); + } + return new Type(m_name.substring(getArrayDimension(), m_name.length())); + } + + private static String getArrayPrefix(int dimension) { + StringBuilder buf = new StringBuilder(); + for (int i=0; i') { + depth--; + } else if (depth == 0 && c == ';') { + return buf.toString(); + } + } + return null; + } +} diff --git a/test/cuchaz/enigma/EntryFactory.java b/test/cuchaz/enigma/EntryFactory.java new file mode 100644 index 00000000..fa90779d --- /dev/null +++ b/test/cuchaz/enigma/EntryFactory.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Signature; + +public class EntryFactory { + + public static ClassEntry newClass(String name) { + return new ClassEntry(name); + } + + public static FieldEntry newField(String className, String fieldName) { + return newField(newClass(className), fieldName); + } + + public static FieldEntry newField(ClassEntry classEntry, String fieldName) { + return new FieldEntry(classEntry, fieldName); + } + + public static MethodEntry newMethod(String className, String methodName, String methodSignature) { + return newMethod(newClass(className), methodName, methodSignature); + } + + public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) { + return new MethodEntry(classEntry, methodName, new Signature(methodSignature)); + } + + public static ConstructorEntry newConstructor(String className, String signature) { + return newConstructor(newClass(className), signature); + } + + public static ConstructorEntry newConstructor(ClassEntry classEntry, String signature) { + return new ConstructorEntry(classEntry, new Signature(signature)); + } + + public static EntryReference newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) { + return new EntryReference(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature)); + } + + public static EntryReference newFieldReferenceByConstructor(FieldEntry fieldEntry, String callerClassName, String callerSignature) { + return new EntryReference(fieldEntry, "", newConstructor(callerClassName, callerSignature)); + } + + public static EntryReference newBehaviorReferenceByMethod(BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature) { + return new EntryReference(behaviorEntry, "", newMethod(callerClassName, callerName, callerSignature)); + } + + public static EntryReference newBehaviorReferenceByConstructor(BehaviorEntry behaviorEntry, String callerClassName, String callerSignature) { + return new EntryReference(behaviorEntry, "", newConstructor(callerClassName, callerSignature)); + } +} diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java new file mode 100644 index 00000000..26d492d8 --- /dev/null +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.List; +import java.util.jar.JarFile; + +import org.junit.Test; + +import com.google.common.collect.Lists; + +import cuchaz.enigma.mapping.ClassEntry; + +public class TestDeobfuscator { + + private Deobfuscator getDeobfuscator() + throws IOException { + return new Deobfuscator(new JarFile("build/testLoneClass.obf.jar")); + } + + @Test + public void loadJar() + throws Exception { + getDeobfuscator(); + } + + @Test + public void getClasses() + throws Exception { + Deobfuscator deobfuscator = getDeobfuscator(); + List obfClasses = Lists.newArrayList(); + List deobfClasses = Lists.newArrayList(); + deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); + assertEquals(1, obfClasses.size()); + assertEquals("none/a", obfClasses.get(0).getName()); + assertEquals(1, deobfClasses.size()); + assertEquals("cuchaz/enigma/inputs/Keep", deobfClasses.get(0).getName()); + } + + @Test + public void decompileClass() + throws Exception { + Deobfuscator deobfuscator = getDeobfuscator(); + deobfuscator.getSource(deobfuscator.getSourceTree("none/a")); + } +} diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java new file mode 100644 index 00000000..2e16a330 --- /dev/null +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.JarIndex; + +public class TestInnerClasses { + + private JarIndex m_index; + private Deobfuscator m_deobfuscator; + + private static final String AnonymousOuter = "none/a"; + private static final String AnonymousInner = "b"; + private static final String SimpleOuter = "none/g"; + private static final String SimpleInner = "h"; + private static final String ConstructorArgsOuter = "none/e"; + private static final String ConstructorArgsInner = "f"; + private static final String AnonymousWithScopeArgsOuter = "none/c"; + private static final String AnonymousWithScopeArgsInner = "d"; + private static final String AnonymousWithOuterAccessOuter = "none/i"; + private static final String AnonymousWithOuterAccessInner = "j"; + + public TestInnerClasses() + throws Exception { + m_index = new JarIndex(); + JarFile jar = new JarFile("build/testInnerClasses.obf.jar"); + m_index.indexJar(jar, true); + m_deobfuscator = new Deobfuscator(jar); + } + + @Test + public void simple() { + assertThat(m_index.getOuterClass(SimpleInner), is(SimpleOuter)); + assertThat(m_index.getInnerClasses(SimpleOuter), containsInAnyOrder(SimpleInner)); + assertThat(m_index.isAnonymousClass(SimpleInner), is(false)); + decompile(SimpleOuter); + } + + @Test + public void anonymous() { + assertThat(m_index.getOuterClass(AnonymousInner), is(AnonymousOuter)); + assertThat(m_index.getInnerClasses(AnonymousOuter), containsInAnyOrder(AnonymousInner)); + assertThat(m_index.isAnonymousClass(AnonymousInner), is(true)); + decompile(AnonymousOuter); + } + + @Test + public void constructorArgs() { + assertThat(m_index.getOuterClass(ConstructorArgsInner), is(ConstructorArgsOuter)); + assertThat(m_index.getInnerClasses(ConstructorArgsOuter), containsInAnyOrder(ConstructorArgsInner)); + assertThat(m_index.isAnonymousClass(ConstructorArgsInner), is(false)); + decompile(ConstructorArgsOuter); + } + + @Test + public void anonymousWithScopeArgs() { + assertThat(m_index.getOuterClass(AnonymousWithScopeArgsInner), is(AnonymousWithScopeArgsOuter)); + assertThat(m_index.getInnerClasses(AnonymousWithScopeArgsOuter), containsInAnyOrder(AnonymousWithScopeArgsInner)); + assertThat(m_index.isAnonymousClass(AnonymousWithScopeArgsInner), is(true)); + decompile(AnonymousWithScopeArgsOuter); + } + + @Test + public void anonymousWithOuterAccess() { + assertThat(m_index.getOuterClass(AnonymousWithOuterAccessInner), is(AnonymousWithOuterAccessOuter)); + assertThat(m_index.getInnerClasses(AnonymousWithOuterAccessOuter), containsInAnyOrder(AnonymousWithOuterAccessInner)); + assertThat(m_index.isAnonymousClass(AnonymousWithOuterAccessInner), is(true)); + decompile(AnonymousWithOuterAccessOuter); + } + + private void decompile(String name) { + m_deobfuscator.getSourceTree(name); + } +} diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java new file mode 100644 index 00000000..22812fea --- /dev/null +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.io.File; +import java.util.Collection; +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; + +public class TestJarIndexConstructorReferences { + + private JarIndex m_index; + + private ClassEntry m_baseClass = newClass("none/a"); + private ClassEntry m_subClass = newClass("none/d"); + private ClassEntry m_subsubClass = newClass("none/e"); + private ClassEntry m_defaultClass = newClass("none/c"); + private ClassEntry m_callerClass = newClass("none/b"); + + public TestJarIndexConstructorReferences() + throws Exception { + File jarFile = new File("build/testConstructors.obf.jar"); + m_index = new JarIndex(); + m_index.indexJar(new JarFile(jarFile), false); + } + + @Test + public void obfEntries() { + assertThat(m_index.getObfClassEntries(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), m_baseClass, m_subClass, m_subsubClass, m_defaultClass, m_callerClass)); + } + + @Test + @SuppressWarnings("unchecked") + public void baseDefault() { + BehaviorEntry source = newConstructor(m_baseClass, "()V"); + Collection> references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "a", "()V"), + newBehaviorReferenceByConstructor(source, m_subClass.getName(), "()V"), + newBehaviorReferenceByConstructor(source, m_subClass.getName(), "(III)V") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void baseInt() { + BehaviorEntry source = newConstructor(m_baseClass, "(I)V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "b", "()V") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void subDefault() { + BehaviorEntry source = newConstructor(m_subClass, "()V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "c", "()V"), + newBehaviorReferenceByConstructor(source, m_subClass.getName(), "(I)V") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void subInt() { + BehaviorEntry source = newConstructor(m_subClass, "(I)V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "d", "()V"), + newBehaviorReferenceByConstructor(source, m_subClass.getName(), "(II)V"), + newBehaviorReferenceByConstructor(source, m_subsubClass.getName(), "(I)V") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void subIntInt() { + BehaviorEntry source = newConstructor(m_subClass, "(II)V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "e", "()V") + )); + } + + @Test + public void subIntIntInt() { + BehaviorEntry source = newConstructor(m_subClass, "(III)V"); + assertThat(m_index.getBehaviorReferences(source), is(empty())); + } + + @Test + @SuppressWarnings("unchecked") + public void subsubInt() { + BehaviorEntry source = newConstructor(m_subsubClass, "(I)V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "f", "()V") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void defaultConstructable() { + BehaviorEntry source = newConstructor(m_defaultClass, "()V"); + assertThat(m_index.getBehaviorReferences(source), containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_callerClass.getName(), "g", "()V") + )); + } +} diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java new file mode 100644 index 00000000..1d6e5a55 --- /dev/null +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -0,0 +1,228 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.util.Collection; +import java.util.Set; +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.Access; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.analysis.TranslationIndex; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class TestJarIndexInheritanceTree { + + private JarIndex m_index; + + private ClassEntry m_baseClass = newClass("none/a"); + private ClassEntry m_subClassA = newClass("none/b"); + private ClassEntry m_subClassAA = newClass("none/d"); + private ClassEntry m_subClassB = newClass("none/c"); + private FieldEntry m_nameField = new FieldEntry(m_baseClass, "a"); + private FieldEntry m_numThingsField = new FieldEntry(m_subClassB, "a"); + + public TestJarIndexInheritanceTree() + throws Exception { + m_index = new JarIndex(); + m_index.indexJar(new JarFile("build/testInheritanceTree.obf.jar"), false); + } + + @Test + public void obfEntries() { + assertThat(m_index.getObfClassEntries(), containsInAnyOrder( + newClass("cuchaz/enigma/inputs/Keep"), + m_baseClass, + m_subClassA, + m_subClassAA, + m_subClassB + )); + } + + @Test + public void translationIndex() { + + TranslationIndex index = m_index.getTranslationIndex(); + + // base class + assertThat(index.getSuperclass(m_baseClass), is(nullValue())); + assertThat(index.getAncestry(m_baseClass), is(empty())); + assertThat(index.getSubclass(m_baseClass), containsInAnyOrder( + m_subClassA, + m_subClassB + )); + + // subclass a + assertThat(index.getSuperclass(m_subClassA), is(m_baseClass)); + assertThat(index.getAncestry(m_subClassA), contains(m_baseClass)); + assertThat(index.getSubclass(m_subClassA), contains(m_subClassAA)); + + // subclass aa + assertThat(index.getSuperclass(m_subClassAA), is(m_subClassA)); + assertThat(index.getAncestry(m_subClassAA), contains(m_subClassA, m_baseClass)); + assertThat(index.getSubclass(m_subClassAA), is(empty())); + + // subclass b + assertThat(index.getSuperclass(m_subClassB), is(m_baseClass)); + assertThat(index.getAncestry(m_subClassB), contains(m_baseClass)); + assertThat(index.getSubclass(m_subClassB), is(empty())); + } + + @Test + public void access() { + assertThat(m_index.getAccess(m_nameField), is(Access.Private)); + assertThat(m_index.getAccess(m_numThingsField), is(Access.Private)); + } + + @Test + public void relatedMethodImplementations() { + + Set entries; + + // getName() + entries = m_index.getRelatedMethodImplementations(newMethod(m_baseClass, "a", "()Ljava/lang/String;")); + assertThat(entries, containsInAnyOrder( + newMethod(m_baseClass, "a", "()Ljava/lang/String;"), + newMethod(m_subClassAA, "a", "()Ljava/lang/String;") + )); + entries = m_index.getRelatedMethodImplementations(newMethod(m_subClassAA, "a", "()Ljava/lang/String;")); + assertThat(entries, containsInAnyOrder( + newMethod(m_baseClass, "a", "()Ljava/lang/String;"), + newMethod(m_subClassAA, "a", "()Ljava/lang/String;") + )); + + // doBaseThings() + entries = m_index.getRelatedMethodImplementations(newMethod(m_baseClass, "a", "()V")); + assertThat(entries, containsInAnyOrder( + newMethod(m_baseClass, "a", "()V"), + newMethod(m_subClassAA, "a", "()V"), + newMethod(m_subClassB, "a", "()V") + )); + entries = m_index.getRelatedMethodImplementations(newMethod(m_subClassAA, "a", "()V")); + assertThat(entries, containsInAnyOrder( + newMethod(m_baseClass, "a", "()V"), + newMethod(m_subClassAA, "a", "()V"), + newMethod(m_subClassB, "a", "()V") + )); + entries = m_index.getRelatedMethodImplementations(newMethod(m_subClassB, "a", "()V")); + assertThat(entries, containsInAnyOrder( + newMethod(m_baseClass, "a", "()V"), + newMethod(m_subClassAA, "a", "()V"), + newMethod(m_subClassB, "a", "()V") + )); + + // doBThings + entries = m_index.getRelatedMethodImplementations(newMethod(m_subClassB, "b", "()V")); + assertThat(entries, containsInAnyOrder(newMethod(m_subClassB, "b", "()V"))); + } + + @Test + @SuppressWarnings("unchecked") + public void fieldReferences() { + Collection> references; + + // name + references = m_index.getFieldReferences(m_nameField); + assertThat(references, containsInAnyOrder( + newFieldReferenceByConstructor(m_nameField, m_baseClass.getName(), "(Ljava/lang/String;)V"), + newFieldReferenceByMethod(m_nameField, m_baseClass.getName(), "a", "()Ljava/lang/String;") + )); + + // numThings + references = m_index.getFieldReferences(m_numThingsField); + assertThat(references, containsInAnyOrder( + newFieldReferenceByConstructor(m_numThingsField, m_subClassB.getName(), "()V"), + newFieldReferenceByMethod(m_numThingsField, m_subClassB.getName(), "b", "()V") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void behaviorReferences() { + + BehaviorEntry source; + Collection> references; + + // baseClass constructor + source = newConstructor(m_baseClass, "(Ljava/lang/String;)V"); + references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByConstructor(source, m_subClassA.getName(), "(Ljava/lang/String;)V"), + newBehaviorReferenceByConstructor(source, m_subClassB.getName(), "()V") + )); + + // subClassA constructor + source = newConstructor(m_subClassA, "(Ljava/lang/String;)V"); + references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByConstructor(source, m_subClassAA.getName(), "()V") + )); + + // baseClass.getName() + source = newMethod(m_baseClass, "a", "()Ljava/lang/String;"); + references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_subClassAA.getName(), "a", "()Ljava/lang/String;"), + newBehaviorReferenceByMethod(source, m_subClassB.getName(), "a", "()V") + )); + + // subclassAA.getName() + source = newMethod(m_subClassAA, "a", "()Ljava/lang/String;"); + references = m_index.getBehaviorReferences(source); + assertThat(references, containsInAnyOrder( + newBehaviorReferenceByMethod(source, m_subClassAA.getName(), "a", "()V") + )); + } + + @Test + public void containsEntries() { + + // classes + assertThat(m_index.containsObfClass(m_baseClass), is(true)); + assertThat(m_index.containsObfClass(m_subClassA), is(true)); + assertThat(m_index.containsObfClass(m_subClassAA), is(true)); + assertThat(m_index.containsObfClass(m_subClassB), is(true)); + + // fields + assertThat(m_index.containsObfField(m_nameField), is(true)); + assertThat(m_index.containsObfField(m_numThingsField), is(true)); + + // methods + // getName() + assertThat(m_index.containsObfBehavior(newMethod(m_baseClass, "a", "()Ljava/lang/String;")), is(true)); + assertThat(m_index.containsObfBehavior(newMethod(m_subClassA, "a", "()Ljava/lang/String;")), is(false)); + assertThat(m_index.containsObfBehavior(newMethod(m_subClassAA, "a", "()Ljava/lang/String;")), is(true)); + assertThat(m_index.containsObfBehavior(newMethod(m_subClassB, "a", "()Ljava/lang/String;")), is(false)); + + // doBaseThings() + assertThat(m_index.containsObfBehavior(newMethod(m_baseClass, "a", "()V")), is(true)); + assertThat(m_index.containsObfBehavior(newMethod(m_subClassA, "a", "()V")), is(false)); + assertThat(m_index.containsObfBehavior(newMethod(m_subClassAA, "a", "()V")), is(true)); + assertThat(m_index.containsObfBehavior(newMethod(m_subClassB, "a", "()V")), is(true)); + + // doBThings() + assertThat(m_index.containsObfBehavior(newMethod(m_baseClass, "b", "()V")), is(false)); + assertThat(m_index.containsObfBehavior(newMethod(m_subClassA, "b", "()V")), is(false)); + assertThat(m_index.containsObfBehavior(newMethod(m_subClassAA, "b", "()V")), is(false)); + assertThat(m_index.containsObfBehavior(newMethod(m_subClassB, "b", "()V")), is(true)); + + } +} diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java new file mode 100644 index 00000000..c6a9e550 --- /dev/null +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.util.Collection; +import java.util.Set; +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.Access; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class TestJarIndexLoneClass { + + private JarIndex m_index; + + public TestJarIndexLoneClass() + throws Exception { + m_index = new JarIndex(); + m_index.indexJar(new JarFile("build/testLoneClass.obf.jar"), false); + } + + @Test + public void obfEntries() { + assertThat(m_index.getObfClassEntries(), containsInAnyOrder( + newClass("cuchaz/enigma/inputs/Keep"), + newClass("none/a") + )); + } + + @Test + public void translationIndex() { + assertThat(m_index.getTranslationIndex().getSuperclass(new ClassEntry("none/a")), is(nullValue())); + assertThat(m_index.getTranslationIndex().getSuperclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(nullValue())); + assertThat(m_index.getTranslationIndex().getAncestry(new ClassEntry("none/a")), is(empty())); + assertThat(m_index.getTranslationIndex().getAncestry(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty())); + assertThat(m_index.getTranslationIndex().getSubclass(new ClassEntry("none/a")), is(empty())); + assertThat(m_index.getTranslationIndex().getSubclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty())); + } + + @Test + public void access() { + assertThat(m_index.getAccess(newField("none/a", "a")), is(Access.Private)); + assertThat(m_index.getAccess(newMethod("none/a", "a", "()Ljava/lang/String;")), is(Access.Public)); + assertThat(m_index.getAccess(newField("none/a", "b")), is(nullValue())); + } + + @Test + public void classInheritance() { + ClassInheritanceTreeNode node = m_index.getClassInheritance(new Translator(), newClass("none/a")); + assertThat(node, is(not(nullValue()))); + assertThat(node.getObfClassName(), is("none/a")); + assertThat(node.getChildCount(), is(0)); + } + + @Test + public void methodInheritance() { + MethodEntry source = newMethod("none/a", "a", "()Ljava/lang/String;"); + MethodInheritanceTreeNode node = m_index.getMethodInheritance(new Translator(), source); + assertThat(node, is(not(nullValue()))); + assertThat(node.getMethodEntry(), is(source)); + assertThat(node.getChildCount(), is(0)); + } + + @Test + public void classImplementations() { + ClassImplementationsTreeNode node = m_index.getClassImplementations(new Translator(), newClass("none/a")); + assertThat(node, is(nullValue())); + } + + @Test + public void methodImplementations() { + MethodEntry source = newMethod("none/a", "a", "()Ljava/lang/String;"); + MethodImplementationsTreeNode node = m_index.getMethodImplementations(new Translator(), source); + assertThat(node, is(nullValue())); + } + + @Test + public void relatedMethodImplementations() { + Set entries = m_index.getRelatedMethodImplementations(newMethod("none/a", "a", "()Ljava/lang/String;")); + assertThat(entries, containsInAnyOrder( + newMethod("none/a", "a", "()Ljava/lang/String;") + )); + } + + @Test + @SuppressWarnings("unchecked") + public void fieldReferences() { + FieldEntry source = newField("none/a", "a"); + Collection> references = m_index.getFieldReferences(source); + assertThat(references, containsInAnyOrder( + newFieldReferenceByConstructor(source, "none/a", "(Ljava/lang/String;)V"), + newFieldReferenceByMethod(source, "none/a", "a", "()Ljava/lang/String;") + )); + } + + @Test + public void behaviorReferences() { + assertThat(m_index.getBehaviorReferences(newMethod("none/a", "a", "()Ljava/lang/String;")), is(empty())); + } + + @Test + public void innerClasses() { + assertThat(m_index.getInnerClasses("none/a"), is(empty())); + } + + @Test + public void outerClass() { + assertThat(m_index.getOuterClass("a"), is(nullValue())); + } + + @Test + public void isAnonymousClass() { + assertThat(m_index.isAnonymousClass("none/a"), is(false)); + } + + @Test + public void interfaces() { + assertThat(m_index.getInterfaces("none/a"), is(empty())); + } + + @Test + public void implementingClasses() { + assertThat(m_index.getImplementingClasses("none/a"), is(empty())); + } + + @Test + public void isInterface() { + assertThat(m_index.isInterface("none/a"), is(false)); + } + + @Test + public void contains() { + assertThat(m_index.containsObfClass(newClass("none/a")), is(true)); + assertThat(m_index.containsObfClass(newClass("none/b")), is(false)); + assertThat(m_index.containsObfField(newField("none/a", "a")), is(true)); + assertThat(m_index.containsObfField(newField("none/a", "b")), is(false)); + assertThat(m_index.containsObfBehavior(newMethod("none/a", "a", "()Ljava/lang/String;")), is(true)); + assertThat(m_index.containsObfBehavior(newMethod("none/a", "b", "()Ljava/lang/String;")), is(false)); + } +} diff --git a/test/cuchaz/enigma/TestSignature.java b/test/cuchaz/enigma/TestSignature.java new file mode 100644 index 00000000..183a4fd4 --- /dev/null +++ b/test/cuchaz/enigma/TestSignature.java @@ -0,0 +1,266 @@ +package cuchaz.enigma; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import org.junit.Test; + +import cuchaz.enigma.mapping.ClassNameReplacer; +import cuchaz.enigma.mapping.Signature; +import cuchaz.enigma.mapping.Type; + + +public class TestSignature { + + @Test + public void easiest() { + final Signature sig = new Signature("()V"); + assertThat(sig.getArgumentTypes(), is(empty())); + assertThat(sig.getReturnType(), is(new Type("V"))); + } + + @Test + public void primitives() { + { + final Signature sig = new Signature("(I)V"); + assertThat(sig.getArgumentTypes(), contains( + new Type("I") + )); + assertThat(sig.getReturnType(), is(new Type("V"))); + } + { + final Signature sig = new Signature("(I)I"); + assertThat(sig.getArgumentTypes(), contains( + new Type("I") + )); + assertThat(sig.getReturnType(), is(new Type("I"))); + } + { + final Signature sig = new Signature("(IBCJ)Z"); + assertThat(sig.getArgumentTypes(), contains( + new Type("I"), + new Type("B"), + new Type("C"), + new Type("J") + )); + assertThat(sig.getReturnType(), is(new Type("Z"))); + } + } + + @Test + public void classes() { + { + final Signature sig = new Signature("([LFoo;)V"); + assertThat(sig.getArgumentTypes().size(), is(1)); + assertThat(sig.getArgumentTypes().get(0), is(new Type("[LFoo;"))); + assertThat(sig.getReturnType(), is(new Type("V"))); + } + { + final Signature sig = new Signature("(LFoo;)LBar;"); + assertThat(sig.getArgumentTypes(), contains( + new Type("LFoo;") + )); + assertThat(sig.getReturnType(), is(new Type("LBar;"))); + } + { + final Signature sig = new Signature("(LFoo;LMoo;LZoo;)LBar;"); + assertThat(sig.getArgumentTypes(), contains( + new Type("LFoo;"), + new Type("LMoo;"), + new Type("LZoo;") + )); + assertThat(sig.getReturnType(), is(new Type("LBar;"))); + } + { + final Signature sig = new Signature("(LFoo;LMoo;)LBar;"); + assertThat(sig.getArgumentTypes(), contains( + new Type("LFoo;"), + new Type("LMoo;") + )); + assertThat(sig.getReturnType(), is(new Type("LBar;"))); + } + } + + @Test + public void arrays() { + { + final Signature sig = new Signature("([I)V"); + assertThat(sig.getArgumentTypes(), contains( + new Type("[I") + )); + assertThat(sig.getReturnType(), is(new Type("V"))); + } + { + final Signature sig = new Signature("([I)[J"); + assertThat(sig.getArgumentTypes(), contains( + new Type("[I") + )); + assertThat(sig.getReturnType(), is(new Type("[J"))); + } + { + final Signature sig = new Signature("([I[Z[F)[D"); + assertThat(sig.getArgumentTypes(), contains( + new Type("[I"), + new Type("[Z"), + new Type("[F") + )); + assertThat(sig.getReturnType(), is(new Type("[D"))); + } + } + + @Test + public void mixed() { + { + final Signature sig = new Signature("(I[JLFoo;)Z"); + assertThat(sig.getArgumentTypes(), contains( + new Type("I"), + new Type("[J"), + new Type("LFoo;") + )); + assertThat(sig.getReturnType(), is(new Type("Z"))); + } + { + final Signature sig = new Signature("(III)[LFoo;"); + assertThat(sig.getArgumentTypes(), contains( + new Type("I"), + new Type("I"), + new Type("I") + )); + assertThat(sig.getReturnType(), is(new Type("[LFoo;"))); + } + } + + @Test + public void replaceClasses() { + { + final Signature oldSig = new Signature("()V"); + final Signature sig = new Signature(oldSig, new ClassNameReplacer() { + @Override + public String replace(String val) { + return null; + } + }); + assertThat(sig.getArgumentTypes(), is(empty())); + assertThat(sig.getReturnType(), is(new Type("V"))); + } + { + final Signature oldSig = new Signature("(IJLFoo;)V"); + final Signature sig = new Signature(oldSig, new ClassNameReplacer() { + @Override + public String replace(String val) { + return null; + } + }); + assertThat(sig.getArgumentTypes(), contains( + new Type("I"), + new Type("J"), + new Type("LFoo;") + )); + assertThat(sig.getReturnType(), is(new Type("V"))); + } + { + final Signature oldSig = new Signature("(LFoo;LBar;)LMoo;"); + final Signature sig = new Signature(oldSig, new ClassNameReplacer() { + @Override + public String replace(String val) { + if (val.equals("Foo")) { + return "Bar"; + } + return null; + } + }); + assertThat(sig.getArgumentTypes(), contains( + new Type("LBar;"), + new Type("LBar;") + )); + assertThat(sig.getReturnType(), is(new Type("LMoo;"))); + } + { + final Signature oldSig = new Signature("(LFoo;LBar;)LMoo;"); + final Signature sig = new Signature(oldSig, new ClassNameReplacer() { + @Override + public String replace(String val) { + if (val.equals("Moo")) { + return "Cow"; + } + return null; + } + }); + assertThat(sig.getArgumentTypes(), contains( + new Type("LFoo;"), + new Type("LBar;") + )); + assertThat(sig.getReturnType(), is(new Type("LCow;"))); + } + } + + @Test + public void replaceArrayClasses() { + { + final Signature oldSig = new Signature("([LFoo;)[[[LBar;"); + final Signature sig = new Signature(oldSig, new ClassNameReplacer() { + @Override + public String replace(String val) { + if (val.equals("Foo")) { + return "Food"; + } else if (val.equals("Bar")) { + return "Beer"; + } + return null; + } + }); + assertThat(sig.getArgumentTypes(), contains( + new Type("[LFood;") + )); + assertThat(sig.getReturnType(), is(new Type("[[[LBeer;"))); + } + } + + @Test + public void equals() { + + // base + assertThat(new Signature("()V"), is(new Signature("()V"))); + + // arguments + assertThat(new Signature("(I)V"), is(new Signature("(I)V"))); + assertThat(new Signature("(ZIZ)V"), is(new Signature("(ZIZ)V"))); + assertThat(new Signature("(LFoo;)V"), is(new Signature("(LFoo;)V"))); + assertThat(new Signature("(LFoo;LBar;)V"), is(new Signature("(LFoo;LBar;)V"))); + assertThat(new Signature("([I)V"), is(new Signature("([I)V"))); + assertThat(new Signature("([[D[[[J)V"), is(new Signature("([[D[[[J)V"))); + + assertThat(new Signature("()V"), is(not(new Signature("(I)V")))); + assertThat(new Signature("(I)V"), is(not(new Signature("()V")))); + assertThat(new Signature("(IJ)V"), is(not(new Signature("(JI)V")))); + assertThat(new Signature("([[Z)V"), is(not(new Signature("([[LFoo;)V")))); + assertThat(new Signature("(LFoo;LBar;)V"), is(not(new Signature("(LFoo;LCow;)V")))); + assertThat(new Signature("([LFoo;LBar;)V"), is(not(new Signature("(LFoo;LCow;)V")))); + + // return type + assertThat(new Signature("()I"), is(new Signature("()I"))); + assertThat(new Signature("()Z"), is(new Signature("()Z"))); + assertThat(new Signature("()[D"), is(new Signature("()[D"))); + assertThat(new Signature("()[[[Z"), is(new Signature("()[[[Z"))); + assertThat(new Signature("()LFoo;"), is(new Signature("()LFoo;"))); + assertThat(new Signature("()[LFoo;"), is(new Signature("()[LFoo;"))); + + assertThat(new Signature("()I"), is(not(new Signature("()Z")))); + assertThat(new Signature("()Z"), is(not(new Signature("()I")))); + assertThat(new Signature("()[D"), is(not(new Signature("()[J")))); + assertThat(new Signature("()[[[Z"), is(not(new Signature("()[[Z")))); + assertThat(new Signature("()LFoo;"), is(not(new Signature("()LBar;")))); + assertThat(new Signature("()[LFoo;"), is(not(new Signature("()[LBar;")))); + } + + @Test + public void testToString() { + assertThat(new Signature("()V").toString(), is("()V")); + assertThat(new Signature("(I)V").toString(), is("(I)V")); + assertThat(new Signature("(ZIZ)V").toString(), is("(ZIZ)V")); + assertThat(new Signature("(LFoo;)V").toString(), is("(LFoo;)V")); + assertThat(new Signature("(LFoo;LBar;)V").toString(), is("(LFoo;LBar;)V")); + assertThat(new Signature("([I)V").toString(), is("([I)V")); + assertThat(new Signature("([[D[[[J)V").toString(), is("([[D[[[J)V")); + } +} diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java new file mode 100644 index 00000000..357acb62 --- /dev/null +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.util.Set; +import java.util.jar.JarFile; + +import org.junit.Test; + +import com.google.common.collect.Sets; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; + +import cuchaz.enigma.mapping.ClassEntry; + +public class TestSourceIndex { + + // TEMP + //@Test + public void indexEverything() + throws Exception { + Deobfuscator deobfuscator = new Deobfuscator(new JarFile("input/1.8.jar")); + + // get all classes that aren't inner classes + Set classEntries = Sets.newHashSet(); + for (ClassEntry obfClassEntry : deobfuscator.getJarIndex().getObfClassEntries()) { + if (!obfClassEntry.isInnerClass()) { + classEntries.add(obfClassEntry); + } + } + + for (ClassEntry obfClassEntry : classEntries) { + try { + CompilationUnit tree = deobfuscator.getSourceTree(obfClassEntry.getName()); + String source = deobfuscator.getSource(tree); + deobfuscator.getSourceIndex(tree, source); + } catch (Throwable t) { + throw new Error("Unable to index " + obfClassEntry, t); + } + } + } +} diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java new file mode 100644 index 00000000..6758d2a7 --- /dev/null +++ b/test/cuchaz/enigma/TestTokensConstructors.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.mapping.BehaviorEntry; + +public class TestTokensConstructors extends TokenChecker { + + public TestTokensConstructors() + throws Exception { + super(new JarFile("build/testConstructors.obf.jar")); + } + + @Test + public void baseDeclarations() { + assertThat(getDeclarationToken(newConstructor("none/a", "()V")), is("a")); + assertThat(getDeclarationToken(newConstructor("none/a", "(I)V")), is("a")); + } + + @Test + public void subDeclarations() { + assertThat(getDeclarationToken(newConstructor("none/d", "()V")), is("d")); + assertThat(getDeclarationToken(newConstructor("none/d", "(I)V")), is("d")); + assertThat(getDeclarationToken(newConstructor("none/d", "(II)V")), is("d")); + assertThat(getDeclarationToken(newConstructor("none/d", "(III)V")), is("d")); + } + + @Test + public void subsubDeclarations() { + assertThat(getDeclarationToken(newConstructor("none/e", "(I)V")), is("e")); + } + + @Test + public void defaultDeclarations() { + assertThat(getDeclarationToken(newConstructor("none/c", "()V")), nullValue()); + } + + @Test + public void baseDefaultReferences() { + BehaviorEntry source = newConstructor("none/a", "()V"); + assertThat( + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "a", "()V")), + containsInAnyOrder("a") + ); + assertThat( + getReferenceTokens(newBehaviorReferenceByConstructor(source, "none/d", "()V")), + is(empty()) // implicit call, not decompiled to token + ); + assertThat( + getReferenceTokens(newBehaviorReferenceByConstructor(source, "none/d", "(III)V")), + is(empty()) // implicit call, not decompiled to token + ); + } + + @Test + public void baseIntReferences() { + BehaviorEntry source = newConstructor("none/a", "(I)V"); + assertThat( + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "b", "()V")), + containsInAnyOrder("a") + ); + } + + @Test + public void subDefaultReferences() { + BehaviorEntry source = newConstructor("none/d", "()V"); + assertThat( + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "c", "()V")), + containsInAnyOrder("d") + ); + assertThat( + getReferenceTokens(newBehaviorReferenceByConstructor(source, "none/d", "(I)V")), + containsInAnyOrder("this") + ); + } + + @Test + public void subIntReferences() { + BehaviorEntry source = newConstructor("none/d", "(I)V"); + assertThat(getReferenceTokens( + newBehaviorReferenceByMethod(source, "none/b", "d", "()V")), + containsInAnyOrder("d") + ); + assertThat(getReferenceTokens( + newBehaviorReferenceByConstructor(source, "none/d", "(II)V")), + containsInAnyOrder("this") + ); + assertThat(getReferenceTokens( + newBehaviorReferenceByConstructor(source, "none/e", "(I)V")), + containsInAnyOrder("super") + ); + } + + @Test + public void subIntIntReferences() { + BehaviorEntry source = newConstructor("none/d", "(II)V"); + assertThat( + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "e", "()V")), + containsInAnyOrder("d") + ); + } + + @Test + public void subsubIntReferences() { + BehaviorEntry source = newConstructor("none/e", "(I)V"); + assertThat( + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "f", "()V")), + containsInAnyOrder("e") + ); + } + + @Test + public void defaultConstructableReferences() { + BehaviorEntry source = newConstructor("none/c", "()V"); + assertThat( + getReferenceTokens(newBehaviorReferenceByMethod(source, "none/b", "g", "()V")), + containsInAnyOrder("c") + ); + } +} diff --git a/test/cuchaz/enigma/TestTranslator.java b/test/cuchaz/enigma/TestTranslator.java new file mode 100644 index 00000000..290f6f04 --- /dev/null +++ b/test/cuchaz/enigma/TestTranslator.java @@ -0,0 +1,39 @@ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.TranslationDirection; +import cuchaz.enigma.mapping.Translator; + + +public class TestTranslator { + + private Deobfuscator m_deobfuscator; + private Mappings m_mappings; + + public TestTranslator() + throws Exception { + m_deobfuscator = new Deobfuscator(new JarFile("build/testTranslation.obf.jar")); + try (InputStream in = getClass().getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) { + m_mappings = new MappingsReader().read(new InputStreamReader(in)); + m_deobfuscator.setMappings(m_mappings); + } + } + + @Test + public void deobfuscatingTranslations() + throws Exception { + Translator translator = m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating); + assertThat(translator.translateEntry(newClass("none/a")), is(newClass("deobf/A"))); + } +} diff --git a/test/cuchaz/enigma/TestType.java b/test/cuchaz/enigma/TestType.java new file mode 100644 index 00000000..7c3cebe2 --- /dev/null +++ b/test/cuchaz/enigma/TestType.java @@ -0,0 +1,229 @@ +package cuchaz.enigma; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import cuchaz.enigma.mapping.Type; + +import static cuchaz.enigma.EntryFactory.*; + + +public class TestType { + + @Test + public void isVoid() { + assertThat(new Type("V").isVoid(), is(true)); + assertThat(new Type("Z").isVoid(), is(false)); + assertThat(new Type("B").isVoid(), is(false)); + assertThat(new Type("C").isVoid(), is(false)); + assertThat(new Type("I").isVoid(), is(false)); + assertThat(new Type("J").isVoid(), is(false)); + assertThat(new Type("F").isVoid(), is(false)); + assertThat(new Type("D").isVoid(), is(false)); + assertThat(new Type("LFoo;").isVoid(), is(false)); + assertThat(new Type("[I").isVoid(), is(false)); + } + + @Test + public void isPrimitive() { + assertThat(new Type("V").isPrimitive(), is(false)); + assertThat(new Type("Z").isPrimitive(), is(true)); + assertThat(new Type("B").isPrimitive(), is(true)); + assertThat(new Type("C").isPrimitive(), is(true)); + assertThat(new Type("I").isPrimitive(), is(true)); + assertThat(new Type("J").isPrimitive(), is(true)); + assertThat(new Type("F").isPrimitive(), is(true)); + assertThat(new Type("D").isPrimitive(), is(true)); + assertThat(new Type("LFoo;").isPrimitive(), is(false)); + assertThat(new Type("[I").isPrimitive(), is(false)); + } + + @Test + public void getPrimitive() { + assertThat(new Type("Z").getPrimitive(), is(Type.Primitive.Boolean)); + assertThat(new Type("B").getPrimitive(), is(Type.Primitive.Byte)); + assertThat(new Type("C").getPrimitive(), is(Type.Primitive.Character)); + assertThat(new Type("I").getPrimitive(), is(Type.Primitive.Integer)); + assertThat(new Type("J").getPrimitive(), is(Type.Primitive.Long)); + assertThat(new Type("F").getPrimitive(), is(Type.Primitive.Float)); + assertThat(new Type("D").getPrimitive(), is(Type.Primitive.Double)); + } + + @Test + public void isClass() { + assertThat(new Type("V").isClass(), is(false)); + assertThat(new Type("Z").isClass(), is(false)); + assertThat(new Type("B").isClass(), is(false)); + assertThat(new Type("C").isClass(), is(false)); + assertThat(new Type("I").isClass(), is(false)); + assertThat(new Type("J").isClass(), is(false)); + assertThat(new Type("F").isClass(), is(false)); + assertThat(new Type("D").isClass(), is(false)); + assertThat(new Type("LFoo;").isClass(), is(true)); + assertThat(new Type("[I").isClass(), is(false)); + } + + @Test + public void getClassEntry() { + assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); + assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); + } + + @Test + public void getArrayClassEntry() { + assertThat(new Type("[LFoo;").getClassEntry(), is(newClass("Foo"))); + assertThat(new Type("[[[LFoo;").getClassEntry(), is(newClass("Foo"))); + } + + @Test + public void isArray() { + assertThat(new Type("V").isArray(), is(false)); + assertThat(new Type("Z").isArray(), is(false)); + assertThat(new Type("B").isArray(), is(false)); + assertThat(new Type("C").isArray(), is(false)); + assertThat(new Type("I").isArray(), is(false)); + assertThat(new Type("J").isArray(), is(false)); + assertThat(new Type("F").isArray(), is(false)); + assertThat(new Type("D").isArray(), is(false)); + assertThat(new Type("LFoo;").isArray(), is(false)); + assertThat(new Type("[I").isArray(), is(true)); + } + + @Test + public void getArrayDimension() { + assertThat(new Type("[I").getArrayDimension(), is(1)); + assertThat(new Type("[[I").getArrayDimension(), is(2)); + assertThat(new Type("[[[I").getArrayDimension(), is(3)); + } + + @Test + public void getArrayType() { + assertThat(new Type("[I").getArrayType(), is(new Type("I"))); + assertThat(new Type("[[I").getArrayType(), is(new Type("I"))); + assertThat(new Type("[[[I").getArrayType(), is(new Type("I"))); + assertThat(new Type("[Ljava/lang/String;").getArrayType(), is(new Type("Ljava/lang/String;"))); + } + + @Test + public void hasClass() { + assertThat(new Type("LFoo;").hasClass(), is(true)); + assertThat(new Type("LCow;").hasClass(), is(true)); + assertThat(new Type("[LBar;").hasClass(), is(true)); + assertThat(new Type("[[[LCat;").hasClass(), is(true)); + + assertThat(new Type("V").hasClass(), is(false)); + assertThat(new Type("[I").hasClass(), is(false)); + assertThat(new Type("[[[I").hasClass(), is(false)); + assertThat(new Type("Z").hasClass(), is(false)); + } + + @Test + public void parseVoid() { + final String answer = "V"; + assertThat(Type.parseFirst("V"), is(answer)); + assertThat(Type.parseFirst("VVV"), is(answer)); + assertThat(Type.parseFirst("VIJ"), is(answer)); + assertThat(Type.parseFirst("V[I"), is(answer)); + assertThat(Type.parseFirst("VLFoo;"), is(answer)); + assertThat(Type.parseFirst("V[LFoo;"), is(answer)); + } + + @Test + public void parsePrimitive() { + final String answer = "I"; + assertThat(Type.parseFirst("I"), is(answer)); + assertThat(Type.parseFirst("III"), is(answer)); + assertThat(Type.parseFirst("IJZ"), is(answer)); + assertThat(Type.parseFirst("I[I"), is(answer)); + assertThat(Type.parseFirst("ILFoo;"), is(answer)); + assertThat(Type.parseFirst("I[LFoo;"), is(answer)); + } + + @Test + public void parseClass() { + { + final String answer = "LFoo;"; + assertThat(Type.parseFirst("LFoo;"), is(answer)); + assertThat(Type.parseFirst("LFoo;I"), is(answer)); + assertThat(Type.parseFirst("LFoo;JZ"), is(answer)); + assertThat(Type.parseFirst("LFoo;[I"), is(answer)); + assertThat(Type.parseFirst("LFoo;LFoo;"), is(answer)); + assertThat(Type.parseFirst("LFoo;[LFoo;"), is(answer)); + } + { + final String answer = "LFoo;"; + assertThat(Type.parseFirst("LFoo;"), is(answer)); + assertThat(Type.parseFirst("LFoo;I"), is(answer)); + assertThat(Type.parseFirst("LFoo;JZ"), is(answer)); + assertThat(Type.parseFirst("LFoo;[I"), is(answer)); + assertThat(Type.parseFirst("LFoo;LFoo;"), is(answer)); + assertThat(Type.parseFirst("LFoo;[LFoo;"), is(answer)); + } + { + final String answer = "LFoo;"; + assertThat(Type.parseFirst("LFoo;"), is(answer)); + assertThat(Type.parseFirst("LFoo;I"), is(answer)); + assertThat(Type.parseFirst("LFoo;JZ"), is(answer)); + assertThat(Type.parseFirst("LFoo;[I"), is(answer)); + assertThat(Type.parseFirst("LFoo;LFoo;"), is(answer)); + assertThat(Type.parseFirst("LFoo;[LFoo;"), is(answer)); + } + } + + @Test + public void parseArray() { + { + final String answer = "[I"; + assertThat(Type.parseFirst("[I"), is(answer)); + assertThat(Type.parseFirst("[III"), is(answer)); + assertThat(Type.parseFirst("[IJZ"), is(answer)); + assertThat(Type.parseFirst("[I[I"), is(answer)); + assertThat(Type.parseFirst("[ILFoo;"), is(answer)); + } + { + final String answer = "[[I"; + assertThat(Type.parseFirst("[[I"), is(answer)); + assertThat(Type.parseFirst("[[III"), is(answer)); + assertThat(Type.parseFirst("[[IJZ"), is(answer)); + assertThat(Type.parseFirst("[[I[I"), is(answer)); + assertThat(Type.parseFirst("[[ILFoo;"), is(answer)); + } + { + final String answer = "[LFoo;"; + assertThat(Type.parseFirst("[LFoo;"), is(answer)); + assertThat(Type.parseFirst("[LFoo;II"), is(answer)); + assertThat(Type.parseFirst("[LFoo;JZ"), is(answer)); + assertThat(Type.parseFirst("[LFoo;[I"), is(answer)); + assertThat(Type.parseFirst("[LFoo;LFoo;"), is(answer)); + } + } + + @Test + public void equals() { + assertThat(new Type("V"), is(new Type("V"))); + assertThat(new Type("Z"), is(new Type("Z"))); + assertThat(new Type("B"), is(new Type("B"))); + assertThat(new Type("C"), is(new Type("C"))); + assertThat(new Type("I"), is(new Type("I"))); + assertThat(new Type("J"), is(new Type("J"))); + assertThat(new Type("F"), is(new Type("F"))); + assertThat(new Type("D"), is(new Type("D"))); + assertThat(new Type("LFoo;"), is(new Type("LFoo;"))); + assertThat(new Type("[I"), is(new Type("[I"))); + assertThat(new Type("[[[I"), is(new Type("[[[I"))); + assertThat(new Type("[LFoo;"), is(new Type("[LFoo;"))); + assertThat(new Type("LFoo;"), is(new Type("LFoo;"))); + + assertThat(new Type("V"), is(not(new Type("I")))); + assertThat(new Type("I"), is(not(new Type("J")))); + assertThat(new Type("I"), is(not(new Type("LBar;")))); + assertThat(new Type("I"), is(not(new Type("[I")))); + assertThat(new Type("LFoo;"), is(not(new Type("LBar;")))); + assertThat(new Type("LFoo;"), is(not(new Type("LFoo;")))); + assertThat(new Type("[I"), is(not(new Type("[Z")))); + assertThat(new Type("[[[I"), is(not(new Type("[I")))); + assertThat(new Type("[LFoo;"), is(not(new Type("[LBar;")))); + } +} diff --git a/test/cuchaz/enigma/TokenChecker.java b/test/cuchaz/enigma/TokenChecker.java new file mode 100644 index 00000000..a72c2fc8 --- /dev/null +++ b/test/cuchaz/enigma/TokenChecker.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.jar.JarFile; + +import com.google.common.collect.Lists; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.mapping.Entry; + +public class TokenChecker { + + private Deobfuscator m_deobfuscator; + + protected TokenChecker(JarFile jarFile) + throws IOException { + m_deobfuscator = new Deobfuscator(jarFile); + } + + protected String getDeclarationToken(Entry entry) { + // decompile the class + CompilationUnit tree = m_deobfuscator.getSourceTree(entry.getClassName()); + // DEBUG + // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null ); + String source = m_deobfuscator.getSource(tree); + SourceIndex index = m_deobfuscator.getSourceIndex(tree, source); + + // get the token value + Token token = index.getDeclarationToken(entry); + if (token == null) { + return null; + } + return source.substring(token.start, token.end); + } + + @SuppressWarnings("unchecked") + protected Collection getReferenceTokens(EntryReference reference) { + // decompile the class + CompilationUnit tree = m_deobfuscator.getSourceTree(reference.context.getClassName()); + String source = m_deobfuscator.getSource(tree); + SourceIndex index = m_deobfuscator.getSourceIndex(tree, source); + + // get the token values + List values = Lists.newArrayList(); + for (Token token : index.getReferenceTokens((EntryReference)reference)) { + values.add(source.substring(token.start, token.end)); + } + return values; + } +} diff --git a/test/cuchaz/enigma/inputs/Keep.java b/test/cuchaz/enigma/inputs/Keep.java new file mode 100644 index 00000000..390e82fb --- /dev/null +++ b/test/cuchaz/enigma/inputs/Keep.java @@ -0,0 +1,7 @@ +package cuchaz.enigma.inputs; + +public class Keep { + public static void main(String[] args) { + System.out.println("Keep me!"); + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/BaseClass.java b/test/cuchaz/enigma/inputs/constructors/BaseClass.java new file mode 100644 index 00000000..93453086 --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/BaseClass.java @@ -0,0 +1,15 @@ +package cuchaz.enigma.inputs.constructors; + +// none/a +public class BaseClass { + + // ()V + public BaseClass() { + System.out.println("Default constructor"); + } + + // (I)V + public BaseClass(int i) { + System.out.println("Int constructor " + i); + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/Caller.java b/test/cuchaz/enigma/inputs/constructors/Caller.java new file mode 100644 index 00000000..57278751 --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/Caller.java @@ -0,0 +1,47 @@ +package cuchaz.enigma.inputs.constructors; + +// none/b +public class Caller { + + // a()V + public void callBaseDefault() { + // none/a.()V + System.out.println(new BaseClass()); + } + + // b()V + public void callBaseInt() { + // none/a.(I)V + System.out.println(new BaseClass(5)); + } + + // c()V + public void callSubDefault() { + // none/d.()V + System.out.println(new SubClass()); + } + + // d()V + public void callSubInt() { + // none/d.(I)V + System.out.println(new SubClass(6)); + } + + // e()V + public void callSubIntInt() { + // none/d.(II)V + System.out.println(new SubClass(4, 2)); + } + + // f()V + public void callSubSubInt() { + // none/e.(I)V + System.out.println(new SubSubClass(3)); + } + + // g()V + public void callDefaultConstructable() { + // none/c.()V + System.out.println(new DefaultConstructable()); + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java new file mode 100644 index 00000000..26a3ddbb --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java @@ -0,0 +1,5 @@ +package cuchaz.enigma.inputs.constructors; + +public class DefaultConstructable { + // only default constructor +} diff --git a/test/cuchaz/enigma/inputs/constructors/SubClass.java b/test/cuchaz/enigma/inputs/constructors/SubClass.java new file mode 100644 index 00000000..fecfa2b5 --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/SubClass.java @@ -0,0 +1,28 @@ +package cuchaz.enigma.inputs.constructors; + +// none/d extends none/a +public class SubClass extends BaseClass { + + // ()V + public SubClass() { + // none/a.()V + } + + // (I)V + public SubClass(int num) { + // ()V + this(); + System.out.println("SubClass " + num); + } + + // (II)V + public SubClass(int a, int b) { + // (I)V + this(a + b); + } + + // (III)V + public SubClass(int a, int b, int c) { + // none/a.()V + } +} diff --git a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java new file mode 100644 index 00000000..ab84161b --- /dev/null +++ b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java @@ -0,0 +1,11 @@ +package cuchaz.enigma.inputs.constructors; + +// none/e extends none/d +public class SubSubClass extends SubClass { + + // (I)V + public SubSubClass(int i) { + // none/c.(I)V + super(i); + } +} diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java new file mode 100644 index 00000000..5b416c41 --- /dev/null +++ b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java @@ -0,0 +1,21 @@ +package cuchaz.enigma.inputs.inheritanceTree; + +// none/a +public abstract class BaseClass { + + // a + private String m_name; + + // (Ljava/lang/String;)V + protected BaseClass(String name) { + m_name = name; + } + + // a()Ljava/lang/String; + public String getName() { + return m_name; + } + + // a()V + public abstract void doBaseThings(); +} diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java new file mode 100644 index 00000000..7a99d516 --- /dev/null +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java @@ -0,0 +1,11 @@ +package cuchaz.enigma.inputs.inheritanceTree; + +// none/b extends none/a +public abstract class SubclassA extends BaseClass { + + // (Ljava/lang/String;)V + protected SubclassA(String name) { + // call to none/a.(Ljava/lang/String)V + super(name); + } +} diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java new file mode 100644 index 00000000..c9485d31 --- /dev/null +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java @@ -0,0 +1,30 @@ +package cuchaz.enigma.inputs.inheritanceTree; + +// none/c extends none/a +public class SubclassB extends BaseClass { + + // a + private int m_numThings; + + // ()V + protected SubclassB() { + // none/a.(Ljava/lang/String;)V + super("B"); + + // access to a + m_numThings = 4; + } + + @Override + // a()V + public void doBaseThings() { + // call to none/a.a()Ljava/lang/String; + System.out.println("Base things by B! " + getName()); + } + + // b()V + public void doBThings() { + // access to a + System.out.println("" + m_numThings + " B things!"); + } +} diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java new file mode 100644 index 00000000..afd03ac4 --- /dev/null +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java @@ -0,0 +1,24 @@ +package cuchaz.enigma.inputs.inheritanceTree; + +// none/d extends none/b +public class SubsubclassAA extends SubclassA { + + protected SubsubclassAA() { + // call to none/b.(Ljava/lang/String;)V + super("AA"); + } + + @Override + // a()Ljava/lang/String; + public String getName() { + // call to none/b.a()Ljava/lang/String; + return "subsub" + super.getName(); + } + + @Override + // a()V + public void doBaseThings() { + // call to none/d.a()Ljava/lang/String; + System.out.println("Base things by " + getName()); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java b/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java new file mode 100644 index 00000000..f7118f63 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java @@ -0,0 +1,14 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class A_Anonymous { + + public void foo() { + Runnable runnable = new Runnable() { + @Override + public void run() { + // don't care + } + }; + runnable.run(); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java b/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java new file mode 100644 index 00000000..42fba9a8 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java @@ -0,0 +1,13 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class B_AnonymousWithScopeArgs { + + public static void foo(final D_Simple arg) { + System.out.println(new Object() { + @Override + public String toString() { + return arg.toString(); + } + }); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java b/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java new file mode 100644 index 00000000..8fa6c5b8 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java @@ -0,0 +1,20 @@ +package cuchaz.enigma.inputs.innerClasses; + +@SuppressWarnings("unused") +public class C_ConstructorArgs { + + class Inner { + + private int a; + + public Inner(int a) { + this.a = a; + } + } + + Inner i; + + public void foo() { + i = new Inner(5); + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java b/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java new file mode 100644 index 00000000..c4fc0ef9 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java @@ -0,0 +1,8 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class D_Simple { + + class Inner { + // nothing to do + } +} diff --git a/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java b/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java new file mode 100644 index 00000000..e1de53cb --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java @@ -0,0 +1,21 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class E_AnonymousWithOuterAccess { + + // reproduction of error case documented at: + // https://bitbucket.org/cuchaz/enigma/issue/61/stackoverflowerror-when-deobfuscating + + public Object makeInner() { + outerMethod(); + return new Object() { + @Override + public String toString() { + return outerMethod(); + } + }; + } + + private String outerMethod() { + return "foo"; + } +} diff --git a/test/cuchaz/enigma/inputs/loneClass/LoneClass.java b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java new file mode 100644 index 00000000..18c716e9 --- /dev/null +++ b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java @@ -0,0 +1,14 @@ +package cuchaz.enigma.inputs.loneClass; + +public class LoneClass { + + private String m_name; + + public LoneClass(String name) { + m_name = name; + } + + public String getName() { + return m_name; + } +} diff --git a/test/cuchaz/enigma/inputs/translation/A.java b/test/cuchaz/enigma/inputs/translation/A.java new file mode 100644 index 00000000..b8aaf11e --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/A.java @@ -0,0 +1,8 @@ +package cuchaz.enigma.inputs.translation; + +public class A { + + public int one; + public float two; + public String three; +} diff --git a/test/cuchaz/enigma/resources/translation.mappings b/test/cuchaz/enigma/resources/translation.mappings new file mode 100644 index 00000000..70755bf6 --- /dev/null +++ b/test/cuchaz/enigma/resources/translation.mappings @@ -0,0 +1,4 @@ +CLASS none/a deobf/A + FIELD a one + FIELD b two + FIELD c three \ No newline at end of file -- cgit v1.2.3 From 31a1a418b04cd3e7b06cb50cb8674a2c25079f6c Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 8 Feb 2015 23:10:26 -0500 Subject: added types to fields --- src/cuchaz/enigma/Deobfuscator.java | 4 +- src/cuchaz/enigma/analysis/EntryRenamer.java | 29 ++++++-- src/cuchaz/enigma/analysis/JarIndex.java | 26 +------- .../analysis/SourceIndexBehaviorVisitor.java | 5 +- .../enigma/analysis/SourceIndexClassVisitor.java | 5 +- src/cuchaz/enigma/bytecode/ClassTranslator.java | 5 +- src/cuchaz/enigma/convert/ClassIdentity.java | 2 +- src/cuchaz/enigma/gui/Gui.java | 1 + src/cuchaz/enigma/mapping/ClassMapping.java | 78 +++++++++++++--------- src/cuchaz/enigma/mapping/FieldEntry.java | 29 +++++--- src/cuchaz/enigma/mapping/FieldMapping.java | 10 ++- src/cuchaz/enigma/mapping/JavassistUtil.java | 6 +- src/cuchaz/enigma/mapping/Mappings.java | 4 +- src/cuchaz/enigma/mapping/MappingsReader.java | 53 ++------------- src/cuchaz/enigma/mapping/MappingsRenamer.java | 14 ++-- src/cuchaz/enigma/mapping/MappingsWriter.java | 2 +- src/cuchaz/enigma/mapping/Translator.java | 6 +- test/cuchaz/enigma/EntryFactory.java | 9 +-- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 4 +- test/cuchaz/enigma/TestJarIndexLoneClass.java | 12 ++-- test/cuchaz/enigma/TestTranslator.java | 3 + test/cuchaz/enigma/resources/translation.mappings | 6 +- 22 files changed, 156 insertions(+), 157 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 5f61686b..818bfd46 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -141,7 +141,7 @@ public class Deobfuscator { // fields for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { - FieldEntry fieldEntry = new FieldEntry(obfClassEntry, fieldMapping.getObfName()); + FieldEntry fieldEntry = new FieldEntry(obfClassEntry, fieldMapping.getObfName(), fieldMapping.getObfType()); ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(fieldEntry); if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(fieldEntry.getClassEntry())) { boolean wasMoved = renamer.moveFieldToObfClass(classMapping, fieldMapping, resolvedObfClassEntry); @@ -206,7 +206,7 @@ public class Deobfuscator { // check the fields for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { - FieldEntry fieldEntry = new FieldEntry(classEntry, fieldMapping.getObfName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, fieldMapping.getObfName(), fieldMapping.getObfType()); if (!m_jarIndex.containsObfField(fieldEntry)) { System.err.println("WARNING: unable to find field " + fieldEntry + ". dropping mapping."); classMapping.removeFieldMapping(fieldMapping); diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java index b54489cd..2f270494 100644 --- a/src/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -21,10 +21,13 @@ import com.google.common.collect.Sets; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassNameReplacer; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Signature; +import cuchaz.enigma.mapping.Type; public class EntryRenamer { @@ -127,7 +130,7 @@ public class EntryRenamer { } @SuppressWarnings("unchecked") - public static T renameClassesInThing(Map renames, T thing) { + public static T renameClassesInThing(final Map renames, T thing) { if (thing instanceof String) { String stringEntry = (String)thing; if (renames.containsKey(stringEntry)) { @@ -138,19 +141,23 @@ public class EntryRenamer { return (T)new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); } else if (thing instanceof FieldEntry) { FieldEntry fieldEntry = (FieldEntry)thing; - return (T)new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName()); + return (T)new FieldEntry( + renameClassesInThing(renames, fieldEntry.getClassEntry()), + fieldEntry.getName(), + renameClassesInThing(renames, fieldEntry.getType()) + ); } else if (thing instanceof ConstructorEntry) { ConstructorEntry constructorEntry = (ConstructorEntry)thing; return (T)new ConstructorEntry( renameClassesInThing(renames, constructorEntry.getClassEntry()), - constructorEntry.getSignature() + renameClassesInThing(renames, constructorEntry.getSignature()) ); } else if (thing instanceof MethodEntry) { MethodEntry methodEntry = (MethodEntry)thing; return (T)new MethodEntry( renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), - methodEntry.getSignature() + renameClassesInThing(renames, methodEntry.getSignature()) ); } else if (thing instanceof ArgumentEntry) { ArgumentEntry argumentEntry = (ArgumentEntry)thing; @@ -164,6 +171,20 @@ public class EntryRenamer { reference.entry = renameClassesInThing(renames, reference.entry); reference.context = renameClassesInThing(renames, reference.context); return thing; + } else if (thing instanceof Signature) { + return (T)new Signature((Signature)thing, new ClassNameReplacer() { + @Override + public String replace(String className) { + return renameClassesInThing(renames, className); + } + }); + } else if (thing instanceof Type) { + return (T)new Type((Type)thing, new ClassNameReplacer() { + @Override + public String replace(String className) { + return renameClassesInThing(renames, className); + } + }); } return thing; diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 3aac8bd0..f54bedad 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -57,7 +57,6 @@ public class JarIndex { private TranslationIndex m_translationIndex; private Multimap m_interfaces; private Map m_access; - private Map m_fieldClasses; // TODO: this will become obsolete! private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; private Multimap> m_fieldReferences; @@ -70,7 +69,6 @@ public class JarIndex { m_translationIndex = new TranslationIndex(); m_interfaces = HashMultimap.create(); m_access = Maps.newHashMap(); - m_fieldClasses = Maps.newHashMap(); m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); m_fieldReferences = HashMultimap.create(); @@ -114,9 +112,6 @@ public class JarIndex { } m_interfaces.put(className, interfaceName); } - for (CtField field : c.getDeclaredFields()) { - indexField(field); - } for (CtBehavior behavior : c.getDeclaredBehaviors()) { indexBehavior(behavior); } @@ -169,18 +164,6 @@ public class JarIndex { } } - private void indexField(CtField field) { - // get the field entry - String className = Descriptor.toJvmName(field.getDeclaringClass().getName()); - FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName()); - - // is the field a class type? - if (field.getSignature().startsWith("L")) { - ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1)); - m_fieldClasses.put(fieldEntry, fieldTypeEntry); - } - } - private void indexBehavior(CtBehavior behavior) { // get the behavior entry final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); @@ -222,7 +205,7 @@ public class JarIndex { FieldEntry calledFieldEntry = JavassistUtil.getFieldEntry(call); ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry); if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { - calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName()); + calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry); } EntryReference reference = new EntryReference( calledFieldEntry, @@ -448,8 +431,7 @@ public class JarIndex { // does the caller use this type? BehaviorEntry caller = references.iterator().next().context; for (FieldEntry fieldEntry : getReferencedFields(caller)) { - ClassEntry fieldClass = getFieldClass(fieldEntry); - if (fieldClass != null && fieldClass.equals(innerClassEntry)) { + if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) { // caller references this type, so it can't be anonymous return null; } @@ -475,10 +457,6 @@ public class JarIndex { return m_access.get(entry); } - public ClassEntry getFieldClass(FieldEntry fieldEntry) { - return m_fieldClasses.get(fieldEntry); - } - public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { // get the root node diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index 41551283..f15a724b 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -36,6 +36,7 @@ import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Signature; +import cuchaz.enigma.mapping.Type; public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { @@ -100,7 +101,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { } ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getSignature())); index.addReference(node.getMemberNameToken(), fieldEntry, m_behaviorEntry); } @@ -140,7 +141,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); if (ref != null) { ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getSignature())); index.addReference(node.getIdentifierToken(), fieldEntry, m_behaviorEntry); } diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 72220350..e2ff3004 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -31,6 +31,7 @@ import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Signature; +import cuchaz.enigma.mapping.Type; public class SourceIndexClassVisitor extends SourceIndexVisitor { @@ -94,7 +95,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getSignature())); assert (node.getVariables().size() == 1); VariableInitializer variable = node.getVariables().firstOrNullObject(); index.addDeclaration(variable.getNameToken(), fieldEntry); @@ -107,7 +108,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { // treat enum declarations as field declarations FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName()); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getSignature())); index.addDeclaration(node.getNameToken(), fieldEntry); return recurse(node, index); diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index afd3a777..4dba0d87 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -55,7 +55,8 @@ public class ClassTranslator { // translate the name FieldEntry entry = new FieldEntry( new ClassEntry(Descriptor.toJvmName(constants.getFieldrefClassName(i))), - constants.getFieldrefName(i) + constants.getFieldrefName(i), + new Type(constants.getFieldrefType(i)) ); FieldEntry translatedEntry = m_translator.translateEntry(entry); @@ -94,7 +95,7 @@ public class ClassTranslator { for (CtField field : c.getDeclaredFields()) { // translate the name - FieldEntry entry = new FieldEntry(classEntry, field.getName()); + FieldEntry entry = JavassistUtil.getFieldEntry(field); String translatedName = m_translator.translate(entry); if (translatedName != null) { field.setName(translatedName); diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index 1be61239..bb729a36 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -116,7 +116,7 @@ public class ClassIdentity { m_references = HashMultiset.create(); if (useReferences) { for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = new FieldEntry(m_classEntry, field.getName()); + FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); for (EntryReference reference : index.getFieldReferences(fieldEntry)) { addReference(reference); } diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index ca39c42a..187ef5b5 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -874,6 +874,7 @@ public class Gui { private void showFieldEntry(FieldEntry entry) { addNameValue(m_infoPanel, "Field", entry.getName()); addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); + addNameValue(m_infoPanel, "Type", entry.getType().toString()); } private void showMethodEntry(MethodEntry entry) { diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index e2c3d56f..71332650 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -152,78 +152,92 @@ public class ClassMapping implements Serializable, Comparable { return m_fieldsByObf.values(); } - public boolean containsObfField(String obfName) { - return m_fieldsByObf.containsKey(obfName); + public boolean containsObfField(String obfName, Type obfType) { + return m_fieldsByObf.containsKey(getFieldKey(obfName, obfType)); } - public boolean containsDeobfField(String deobfName) { - return m_fieldsByDeobf.containsKey(deobfName); + public boolean containsDeobfField(String deobfName, Type deobfType) { + return m_fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); } public void addFieldMapping(FieldMapping fieldMapping) { - if (m_fieldsByObf.containsKey(fieldMapping.getObfName())) { - throw new Error("Already have mapping for " + m_obfName + "." + fieldMapping.getObfName()); + String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); + if (m_fieldsByObf.containsKey(obfKey)) { + throw new Error("Already have mapping for " + m_obfName + "." + obfKey); } - if (m_fieldsByDeobf.containsKey(fieldMapping.getDeobfName())) { - throw new Error("Already have mapping for " + m_deobfName + "." + fieldMapping.getDeobfName()); + String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); + if (m_fieldsByDeobf.containsKey(deobfKey)) { + throw new Error("Already have mapping for " + m_deobfName + "." + deobfKey); } - boolean obfWasAdded = m_fieldsByObf.put(fieldMapping.getObfName(), fieldMapping) == null; + boolean obfWasAdded = m_fieldsByObf.put(obfKey, fieldMapping) == null; assert (obfWasAdded); - boolean deobfWasAdded = m_fieldsByDeobf.put(fieldMapping.getDeobfName(), fieldMapping) == null; + boolean deobfWasAdded = m_fieldsByDeobf.put(deobfKey, fieldMapping) == null; assert (deobfWasAdded); assert (m_fieldsByObf.size() == m_fieldsByDeobf.size()); } public void removeFieldMapping(FieldMapping fieldMapping) { - boolean obfWasRemoved = m_fieldsByObf.remove(fieldMapping.getObfName()) != null; + boolean obfWasRemoved = m_fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null; assert (obfWasRemoved); if (fieldMapping.getDeobfName() != null) { - boolean deobfWasRemoved = m_fieldsByDeobf.remove(fieldMapping.getDeobfName()) != null; + boolean deobfWasRemoved = m_fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; assert (deobfWasRemoved); } } - public FieldMapping getFieldByObf(String obfName) { - return m_fieldsByObf.get(obfName); + public FieldMapping getFieldByObf(String obfName, Type obfType) { + return m_fieldsByObf.get(getFieldKey(obfName, obfType)); } - public FieldMapping getFieldByDeobf(String deobfName) { - return m_fieldsByDeobf.get(deobfName); + public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { + return m_fieldsByDeobf.get(getFieldKey(deobfName, obfType)); } - public String getObfFieldName(String deobfName) { - FieldMapping fieldMapping = m_fieldsByDeobf.get(deobfName); + public String getObfFieldName(String deobfName, Type obfType) { + FieldMapping fieldMapping = m_fieldsByDeobf.get(getFieldKey(deobfName, obfType)); if (fieldMapping != null) { return fieldMapping.getObfName(); } return null; } - public String getDeobfFieldName(String obfName) { - FieldMapping fieldMapping = m_fieldsByObf.get(obfName); + public String getDeobfFieldName(String obfName, Type obfType) { + FieldMapping fieldMapping = m_fieldsByObf.get(getFieldKey(obfName, obfType)); if (fieldMapping != null) { return fieldMapping.getDeobfName(); } return null; } - public void setFieldName(String obfName, String deobfName) { - FieldMapping fieldMapping = m_fieldsByObf.get(obfName); + private String getFieldKey(String name, Type type) { + if (name == null) { + throw new IllegalArgumentException("name cannot be null!"); + } + if (type == null) { + throw new IllegalArgumentException("type cannot be null!"); + } + return name + ":" + type; + } + + + public void setFieldName(String obfName, Type obfType, String deobfName) { + FieldMapping fieldMapping = m_fieldsByObf.get(getFieldKey(obfName, obfType)); if (fieldMapping == null) { - fieldMapping = new FieldMapping(obfName, deobfName); - boolean obfWasAdded = m_fieldsByObf.put(obfName, fieldMapping) == null; + fieldMapping = new FieldMapping(obfName, obfType, deobfName); + boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; assert (obfWasAdded); } else { - boolean wasRemoved = m_fieldsByDeobf.remove(fieldMapping.getDeobfName()) != null; + boolean wasRemoved = m_fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null; assert (wasRemoved); } fieldMapping.setDeobfName(deobfName); if (deobfName != null) { - boolean wasAdded = m_fieldsByDeobf.put(deobfName, fieldMapping) == null; + boolean wasAdded = m_fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; assert (wasAdded); } } + //// METHODS //////// public Iterable methods() { @@ -235,8 +249,8 @@ public class ClassMapping implements Serializable, Comparable { return m_methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); } - public boolean containsDeobfMethod(String deobfName, Signature deobfSignature) { - return m_methodsByDeobf.containsKey(getMethodKey(deobfName, deobfSignature)); + public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { + return m_methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); } public void addMethodMapping(MethodMapping methodMapping) { @@ -266,12 +280,12 @@ public class ClassMapping implements Serializable, Comparable { } } - public MethodMapping getMethodByObf(String obfName, Signature signature) { - return m_methodsByObf.get(getMethodKey(obfName, signature)); + public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { + return m_methodsByObf.get(getMethodKey(obfName, obfSignature)); } - public MethodMapping getMethodByDeobf(String deobfName, Signature signature) { - return m_methodsByDeobf.get(getMethodKey(deobfName, signature)); + public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) { + return m_methodsByDeobf.get(getMethodKey(deobfName, obfSignature)); } private String getMethodKey(String name, Signature signature) { diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java index 6cc9eb78..7517254c 100644 --- a/src/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -20,28 +20,33 @@ public class FieldEntry implements Entry, Serializable { private ClassEntry m_classEntry; private String m_name; + private Type m_type; // NOTE: this argument order is important for the MethodReader/MethodWriter - public FieldEntry(ClassEntry classEntry, String name) { + public FieldEntry(ClassEntry classEntry, String name, Type type) { if (classEntry == null) { throw new IllegalArgumentException("Class cannot be null!"); } if (name == null) { throw new IllegalArgumentException("Field name cannot be null!"); } + if (type == null) { + throw new IllegalArgumentException("Field type cannot be null!"); + } m_classEntry = classEntry; m_name = name; + m_type = type; } public FieldEntry(FieldEntry other) { - m_classEntry = new ClassEntry(other.m_classEntry); - m_name = other.m_name; + this(other, new ClassEntry(other.m_classEntry)); } - public FieldEntry(FieldEntry other, String newClassName) { - m_classEntry = new ClassEntry(newClassName); + public FieldEntry(FieldEntry other, ClassEntry newClassEntry) { + m_classEntry = newClassEntry; m_name = other.m_name; + m_type = other.m_type; } @Override @@ -59,14 +64,18 @@ public class FieldEntry implements Entry, Serializable { return m_classEntry.getName(); } + public Type getType() { + return m_type; + } + @Override public FieldEntry cloneToNewClass(ClassEntry classEntry) { - return new FieldEntry(this, classEntry.getName()); + return new FieldEntry(this, classEntry); } @Override public int hashCode() { - return Util.combineHashesOrdered(m_classEntry, m_name); + return Util.combineHashesOrdered(m_classEntry, m_name, m_type); } @Override @@ -78,11 +87,13 @@ public class FieldEntry implements Entry, Serializable { } public boolean equals(FieldEntry other) { - return m_classEntry.equals(other.m_classEntry) && m_name.equals(other.m_name); + return m_classEntry.equals(other.m_classEntry) + && m_name.equals(other.m_name) + && m_type.equals(other.m_type); } @Override public String toString() { - return m_classEntry.getName() + "." + m_name; + return m_classEntry.getName() + "." + m_name + ":" + m_type; } } diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java index 5f5c270d..14b20dd4 100644 --- a/src/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -18,10 +18,12 @@ public class FieldMapping implements Serializable, Comparable { private String m_obfName; private String m_deobfName; + private Type m_obfType; - public FieldMapping(String obfName, String deobfName) { + public FieldMapping(String obfName, Type obfType, String deobfName) { m_obfName = obfName; m_deobfName = NameValidator.validateFieldName(deobfName); + m_obfType = obfType; } public String getObfName() { @@ -36,8 +38,12 @@ public class FieldMapping implements Serializable, Comparable { m_deobfName = NameValidator.validateFieldName(val); } + public Type getObfType() { + return m_obfType; + } + @Override public int compareTo(FieldMapping other) { - return m_obfName.compareTo(other.m_obfName); + return (m_obfName + m_obfType).compareTo(other.m_obfName + other.m_obfType); } } diff --git a/src/cuchaz/enigma/mapping/JavassistUtil.java b/src/cuchaz/enigma/mapping/JavassistUtil.java index 0c446c4a..0d6ce6a1 100644 --- a/src/cuchaz/enigma/mapping/JavassistUtil.java +++ b/src/cuchaz/enigma/mapping/JavassistUtil.java @@ -70,14 +70,16 @@ public class JavassistUtil { public static FieldEntry getFieldEntry(CtField field) { return new FieldEntry( getClassEntry(field.getDeclaringClass()), - field.getName() + field.getName(), + new Type(field.getFieldInfo().getDescriptor()) ); } public static FieldEntry getFieldEntry(FieldAccess call) { return new FieldEntry( new ClassEntry(Descriptor.toJvmName(call.getClassName())), - call.getFieldName() + call.getFieldName(), + new Type(call.getSignature()) ); } } diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 57d80013..675fdf1f 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -162,10 +162,10 @@ public class Mappings implements Serializable { return m_classesByDeobf.containsKey(deobfName); } - public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { + public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName()); if (classMapping != null) { - return classMapping.containsDeobfField(deobfName); + return classMapping.containsDeobfField(deobfName, obfType); } return false; } diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index adf460e6..1e7b6e3d 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -17,8 +17,6 @@ import java.util.Deque; import com.google.common.collect.Queues; -import cuchaz.enigma.Constants; - public class MappingsReader { public Mappings read(Reader in) throws IOException, MappingParseException { @@ -114,62 +112,21 @@ public class MappingsReader { private ClassMapping readClass(String[] parts, boolean makeSimple) { if (parts.length == 2) { - String obfName = processName(parts[1], makeSimple); - return new ClassMapping(obfName); + return new ClassMapping(parts[1]); } else { - String obfName = processName(parts[1], makeSimple); - String deobfName = processName(parts[2], makeSimple); - return new ClassMapping(obfName, deobfName); + return new ClassMapping(parts[1], parts[2]); } } - private String processName(String name, boolean makeSimple) { - if (makeSimple) { - return new ClassEntry(name).getSimpleName(); - } else { - return moveClassOutOfDefaultPackage(name, Constants.NonePackage); - } - } - - private String moveClassOutOfDefaultPackage(String className, String newPackageName) { - ClassEntry classEntry = new ClassEntry(className); - if (classEntry.isInDefaultPackage()) { - return newPackageName + "/" + classEntry.getName(); - } - return className; - } - private FieldMapping readField(String[] parts) { - return new FieldMapping(parts[1], parts[2]); + return new FieldMapping(parts[1], new Type(parts[3]), parts[2]); } private MethodMapping readMethod(String[] parts) { if (parts.length == 3) { - String obfName = parts[1]; - Signature obfSignature = moveSignatureOutOfDefaultPackage(new Signature(parts[2]), Constants.NonePackage); - return new MethodMapping(obfName, obfSignature); + return new MethodMapping(parts[1], new Signature(parts[2])); } else { - String obfName = parts[1]; - String deobfName = parts[2]; - Signature obfSignature = moveSignatureOutOfDefaultPackage(new Signature(parts[3]), Constants.NonePackage); - if (obfName.equals(deobfName)) { - return new MethodMapping(obfName, obfSignature); - } else { - return new MethodMapping(obfName, obfSignature, deobfName); - } + return new MethodMapping(parts[1], new Signature(parts[3]), parts[2]); } } - - private Signature moveSignatureOutOfDefaultPackage(Signature signature, final String newPackageName) { - return new Signature(signature, new ClassNameReplacer() { - @Override - public String replace(String className) { - ClassEntry classEntry = new ClassEntry(className); - if (classEntry.isInDefaultPackage()) { - return newPackageName + "/" + className; - } - return null; - } - }); - } } diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index 0a41c2b8..095e5e9a 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -76,23 +76,23 @@ public class MappingsRenamer { public void setFieldName(FieldEntry obf, String deobfName) { deobfName = NameValidator.validateFieldName(deobfName); - FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName); - if (m_mappings.containsDeobfField(obf.getClassEntry(), deobfName) || m_index.containsObfField(targetEntry)) { + FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); + if (m_mappings.containsDeobfField(obf.getClassEntry(), deobfName, obf.getType()) || m_index.containsObfField(targetEntry)) { throw new IllegalNameException(deobfName, "There is already a field with that name"); } ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); - classMapping.setFieldName(obf.getName(), deobfName); + classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); } public void removeFieldMapping(FieldEntry obf) { ClassMapping classMapping = getClassMappingOrInnerClassMapping(obf.getClassEntry()); - classMapping.setFieldName(obf.getName(), null); + classMapping.setFieldName(obf.getName(), obf.getType(), null); } public void markFieldAsDeobfuscated(FieldEntry obf) { ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); - classMapping.setFieldName(obf.getName(), obf.getName()); + classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); } public void setMethodTreeName(MethodEntry obf, String deobfName) { @@ -171,8 +171,8 @@ public class MappingsRenamer { public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { classMapping.removeFieldMapping(fieldMapping); ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); - if (!targetClassMapping.containsObfField(fieldMapping.getObfName())) { - if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName())) { + if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { + if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { targetClassMapping.addFieldMapping(fieldMapping); return true; } else { diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java index 5ac409fc..c7c2cc00 100644 --- a/src/cuchaz/enigma/mapping/MappingsWriter.java +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -50,7 +50,7 @@ public class MappingsWriter { } private void write(PrintWriter out, FieldMapping fieldMapping, int depth) throws IOException { - out.format("%sFIELD %s %s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName()); + out.format("%sFIELD %s %s %s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString()); } private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 5eba18ce..759dddf9 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -112,8 +112,8 @@ public class Translator { // look for the field String translatedName = m_direction.choose( - classMapping.getDeobfFieldName(in.getName()), - classMapping.getObfFieldName(in.getName()) + classMapping.getDeobfFieldName(in.getName(), in.getType()), + classMapping.getObfFieldName(in.getName(), translateType(in.getType())) ); if (translatedName != null) { return translatedName; @@ -128,7 +128,7 @@ public class Translator { if (name == null) { name = in.getName(); } - return new FieldEntry(translateEntry(in.getClassEntry()), name); + return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType())); } public String translate(MethodEntry in) { diff --git a/test/cuchaz/enigma/EntryFactory.java b/test/cuchaz/enigma/EntryFactory.java index fa90779d..8c94bdf3 100644 --- a/test/cuchaz/enigma/EntryFactory.java +++ b/test/cuchaz/enigma/EntryFactory.java @@ -18,6 +18,7 @@ import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Signature; +import cuchaz.enigma.mapping.Type; public class EntryFactory { @@ -25,12 +26,12 @@ public class EntryFactory { return new ClassEntry(name); } - public static FieldEntry newField(String className, String fieldName) { - return newField(newClass(className), fieldName); + public static FieldEntry newField(String className, String fieldName, String fieldType) { + return newField(newClass(className), fieldName, fieldType); } - public static FieldEntry newField(ClassEntry classEntry, String fieldName) { - return new FieldEntry(classEntry, fieldName); + public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) { + return new FieldEntry(classEntry, fieldName, new Type(fieldType)); } public static MethodEntry newMethod(String className, String methodName, String methodSignature) { diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 1d6e5a55..349d33b1 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -37,8 +37,8 @@ public class TestJarIndexInheritanceTree { private ClassEntry m_subClassA = newClass("none/b"); private ClassEntry m_subClassAA = newClass("none/d"); private ClassEntry m_subClassB = newClass("none/c"); - private FieldEntry m_nameField = new FieldEntry(m_baseClass, "a"); - private FieldEntry m_numThingsField = new FieldEntry(m_subClassB, "a"); + private FieldEntry m_nameField = newField(m_baseClass, "a", "Ljava/lang/String;"); + private FieldEntry m_numThingsField = newField(m_subClassB, "a", "I"); public TestJarIndexInheritanceTree() throws Exception { diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index c6a9e550..c0ac8d7d 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -64,9 +64,10 @@ public class TestJarIndexLoneClass { @Test public void access() { - assertThat(m_index.getAccess(newField("none/a", "a")), is(Access.Private)); + assertThat(m_index.getAccess(newField("none/a", "a", "Ljava/lang/String;")), is(Access.Private)); assertThat(m_index.getAccess(newMethod("none/a", "a", "()Ljava/lang/String;")), is(Access.Public)); - assertThat(m_index.getAccess(newField("none/a", "b")), is(nullValue())); + assertThat(m_index.getAccess(newField("none/a", "b", "Ljava/lang/String;")), is(nullValue())); + assertThat(m_index.getAccess(newField("none/a", "a", "LFoo;")), is(nullValue())); } @Test @@ -110,7 +111,7 @@ public class TestJarIndexLoneClass { @Test @SuppressWarnings("unchecked") public void fieldReferences() { - FieldEntry source = newField("none/a", "a"); + FieldEntry source = newField("none/a", "a", "Ljava/lang/String;"); Collection> references = m_index.getFieldReferences(source); assertThat(references, containsInAnyOrder( newFieldReferenceByConstructor(source, "none/a", "(Ljava/lang/String;)V"), @@ -157,8 +158,9 @@ public class TestJarIndexLoneClass { public void contains() { assertThat(m_index.containsObfClass(newClass("none/a")), is(true)); assertThat(m_index.containsObfClass(newClass("none/b")), is(false)); - assertThat(m_index.containsObfField(newField("none/a", "a")), is(true)); - assertThat(m_index.containsObfField(newField("none/a", "b")), is(false)); + assertThat(m_index.containsObfField(newField("none/a", "a", "Ljava/lang/String;")), is(true)); + assertThat(m_index.containsObfField(newField("none/a", "b", "Ljava/lang/String;")), is(false)); + assertThat(m_index.containsObfField(newField("none/a", "a", "LFoo;")), is(false)); assertThat(m_index.containsObfBehavior(newMethod("none/a", "a", "()Ljava/lang/String;")), is(true)); assertThat(m_index.containsObfBehavior(newMethod("none/a", "b", "()Ljava/lang/String;")), is(false)); } diff --git a/test/cuchaz/enigma/TestTranslator.java b/test/cuchaz/enigma/TestTranslator.java index 290f6f04..f91c5852 100644 --- a/test/cuchaz/enigma/TestTranslator.java +++ b/test/cuchaz/enigma/TestTranslator.java @@ -35,5 +35,8 @@ public class TestTranslator { throws Exception { Translator translator = m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating); assertThat(translator.translateEntry(newClass("none/a")), is(newClass("deobf/A"))); + assertThat(translator.translateEntry(newField("none/a", "a", "I")), is(newField("deobf/A", "one", "I"))); + assertThat(translator.translateEntry(newField("none/a", "a", "F")), is(newField("deobf/A", "two", "F"))); + assertThat(translator.translateEntry(newField("none/a", "a", "Ljava/lang/String;")), is(newField("deobf/A", "three", "Ljava/lang/String;"))); } } diff --git a/test/cuchaz/enigma/resources/translation.mappings b/test/cuchaz/enigma/resources/translation.mappings index 70755bf6..c71493e9 100644 --- a/test/cuchaz/enigma/resources/translation.mappings +++ b/test/cuchaz/enigma/resources/translation.mappings @@ -1,4 +1,4 @@ CLASS none/a deobf/A - FIELD a one - FIELD b two - FIELD c three \ No newline at end of file + FIELD a one I + FIELD a two F + FIELD a three Ljava/lang/String; \ No newline at end of file -- cgit v1.2.3 From 71ec7b53a1b4ecce0623dded1e445818a757b695 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Feb 2015 12:17:26 -0500 Subject: add converter to update old mappings format fix a few decompiler issues too --- src/cuchaz/enigma/MainFormatConverter.java | 120 +++++++++++++++++++++ .../analysis/SourceIndexBehaviorVisitor.java | 12 +-- .../enigma/bytecode/MethodParameterWriter.java | 8 +- src/cuchaz/enigma/mapping/MappingsReader.java | 36 ++++--- 4 files changed, 152 insertions(+), 24 deletions(-) create mode 100644 src/cuchaz/enigma/MainFormatConverter.java diff --git a/src/cuchaz/enigma/MainFormatConverter.java b/src/cuchaz/enigma/MainFormatConverter.java new file mode 100644 index 00000000..1848144d --- /dev/null +++ b/src/cuchaz/enigma/MainFormatConverter.java @@ -0,0 +1,120 @@ +package cuchaz.enigma; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.lang.reflect.Field; +import java.util.Map; +import java.util.jar.JarFile; + +import javassist.CtClass; +import javassist.CtField; + +import com.google.common.collect.Maps; + +import cuchaz.enigma.analysis.JarClassIterator; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.ClassNameReplacer; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.FieldMapping; +import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.MappingsWriter; +import cuchaz.enigma.mapping.Type; + +public class MainFormatConverter { + + public static void main(String[] args) + throws Exception { + + System.out.println("Getting field types from jar..."); + + JarFile jar = new JarFile(System.getProperty("user.home") + "/.minecraft/versions/1.8/1.8.jar"); + Map fieldTypes = Maps.newHashMap(); + for (CtClass c : JarClassIterator.classes(jar)) { + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + fieldTypes.put(getFieldKey(fieldEntry), moveClasssesOutOfDefaultPackage(fieldEntry.getType())); + } + } + + System.out.println("Reading mappings..."); + + File fileMappings = new File("../Enigma Mappings/1.8.mappings"); + MappingsReader mappingsReader = new MappingsReader() { + + @Override + protected FieldMapping readField(String[] parts) { + // assume the void type for now + return new FieldMapping(parts[1], new Type("V"), parts[2]); + } + }; + Mappings mappings = mappingsReader.read(new FileReader(fileMappings)); + + System.out.println("Updating field types..."); + + for (ClassMapping classMapping : mappings.classes()) { + updateFieldsInClass(fieldTypes, classMapping); + } + + System.out.println("Saving mappings..."); + + try (FileWriter writer = new FileWriter(fileMappings)) { + new MappingsWriter().write(writer, mappings); + } + + System.out.println("Done!"); + } + + private static Type moveClasssesOutOfDefaultPackage(Type type) { + return new Type(type, new ClassNameReplacer() { + @Override + public String replace(String className) { + ClassEntry entry = new ClassEntry(className); + if (entry.isInDefaultPackage()) { + return Constants.NonePackage + "/" + className; + } + return null; + } + }); + } + + private static void updateFieldsInClass(Map fieldTypes, ClassMapping classMapping) + throws Exception { + + // update the fields + for (FieldMapping fieldMapping : classMapping.fields()) { + setFieldType(fieldTypes, classMapping, fieldMapping); + } + + // recurse + for (ClassMapping innerClassMapping : classMapping.innerClasses()) { + updateFieldsInClass(fieldTypes, innerClassMapping); + } + } + + private static void setFieldType(Map fieldTypes, ClassMapping classMapping, FieldMapping fieldMapping) + throws Exception { + + // get the new type + Type newType = fieldTypes.get(getFieldKey(classMapping, fieldMapping)); + if (newType == null) { + throw new Error("Can't find type for field: " + getFieldKey(classMapping, fieldMapping)); + } + + // hack in the new field type + Field field = fieldMapping.getClass().getDeclaredField("m_obfType"); + field.setAccessible(true); + field.set(fieldMapping, newType); + } + + private static Object getFieldKey(ClassMapping classMapping, FieldMapping fieldMapping) { + return new ClassEntry(classMapping.getObfName()).getSimpleName() + "." + fieldMapping.getObfName(); + } + + private static String getFieldKey(FieldEntry obfFieldEntry) { + return obfFieldEntry.getClassEntry().getSimpleName() + "." + obfFieldEntry.getName(); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index f15a724b..b4094d9e 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -66,11 +66,11 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { if (ref instanceof MethodReference) { MethodReference methodRef = (MethodReference)ref; if (methodRef.isConstructor()) { - behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getSignature())); + behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); } else if (methodRef.isTypeInitializer()) { behaviorEntry = new ConstructorEntry(classEntry); } else { - behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getSignature())); + behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getErasedSignature())); } } if (behaviorEntry != null) { @@ -96,12 +96,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); if (ref != null) { // make sure this is actually a field - if (ref.getSignature().indexOf('(') >= 0) { + if (ref.getErasedSignature().indexOf('(') >= 0) { throw new Error("Expected a field here! got " + ref); } ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getSignature())); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); index.addReference(node.getMemberNameToken(), fieldEntry, m_behaviorEntry); } @@ -141,7 +141,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); if (ref != null) { ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getSignature())); + FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); index.addReference(node.getIdentifierToken(), fieldEntry, m_behaviorEntry); } @@ -153,7 +153,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); if (ref != null) { ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); - ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getSignature())); + ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); if (node.getType() instanceof SimpleType) { SimpleType simpleTypeNode = (SimpleType)node.getType(); index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, m_behaviorEntry); diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java index 5d4ca1ad..853928c7 100644 --- a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -18,6 +18,7 @@ import javassist.CtClass; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.Signature; import cuchaz.enigma.mapping.Translator; public class MethodParameterWriter { @@ -35,7 +36,12 @@ public class MethodParameterWriter { BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); // get the number of arguments - int numParams = behaviorEntry.getSignature().getArgumentTypes().size(); + Signature signature = behaviorEntry.getSignature(); + if (signature == null) { + // static initializers have no signatures, or arguments + continue; + } + int numParams = signature.getArgumentTypes().size(); if (numParams <= 0) { continue; } diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index 1e7b6e3d..bfb27319 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -19,11 +19,13 @@ import com.google.common.collect.Queues; public class MappingsReader { - public Mappings read(Reader in) throws IOException, MappingParseException { + public Mappings read(Reader in) + throws IOException, MappingParseException { return read(new BufferedReader(in)); } - public Mappings read(BufferedReader in) throws IOException, MappingParseException { + public Mappings read(BufferedReader in) + throws IOException, MappingParseException { Mappings mappings = new Mappings(); Deque mappingStack = Queues.newArrayDeque(); @@ -64,42 +66,41 @@ public class MappingsReader { if (token.equalsIgnoreCase("CLASS")) { ClassMapping classMapping; - if (indent == 0) { + if (indent <= 0) { // outer class classMapping = readClass(parts, false); mappings.addClassMapping(classMapping); - } else if (indent == 1) { + } else { + // inner class - if (! (mappingStack.getFirst() instanceof ClassMapping)) { + if (!(mappingStack.peek() instanceof ClassMapping)) { throw new MappingParseException(lineNumber, "Unexpected CLASS entry here!"); } classMapping = readClass(parts, true); - ((ClassMapping)mappingStack.getFirst()).addInnerClassMapping(classMapping); - } else { - throw new MappingParseException(lineNumber, "Unexpected CLASS entry nesting!"); + ((ClassMapping)mappingStack.peek()).addInnerClassMapping(classMapping); } mappingStack.push(classMapping); } else if (token.equalsIgnoreCase("FIELD")) { - if (mappingStack.isEmpty() || ! (mappingStack.getFirst() instanceof ClassMapping)) { + if (mappingStack.isEmpty() || ! (mappingStack.peek() instanceof ClassMapping)) { throw new MappingParseException(lineNumber, "Unexpected FIELD entry here!"); } - ((ClassMapping)mappingStack.getFirst()).addFieldMapping(readField(parts)); + ((ClassMapping)mappingStack.peek()).addFieldMapping(readField(parts)); } else if (token.equalsIgnoreCase("METHOD")) { - if (mappingStack.isEmpty() || ! (mappingStack.getFirst() instanceof ClassMapping)) { + if (mappingStack.isEmpty() || ! (mappingStack.peek() instanceof ClassMapping)) { throw new MappingParseException(lineNumber, "Unexpected METHOD entry here!"); } MethodMapping methodMapping = readMethod(parts); - ((ClassMapping)mappingStack.getFirst()).addMethodMapping(methodMapping); + ((ClassMapping)mappingStack.peek()).addMethodMapping(methodMapping); mappingStack.push(methodMapping); } else if (token.equalsIgnoreCase("ARG")) { - if (mappingStack.isEmpty() || ! (mappingStack.getFirst() instanceof MethodMapping)) { + if (mappingStack.isEmpty() || ! (mappingStack.peek() instanceof MethodMapping)) { throw new MappingParseException(lineNumber, "Unexpected ARG entry here!"); } - ((MethodMapping)mappingStack.getFirst()).addArgumentMapping(readArgument(parts)); + ((MethodMapping)mappingStack.peek()).addArgumentMapping(readArgument(parts)); } - } catch (ArrayIndexOutOfBoundsException | NumberFormatException ex) { - throw new MappingParseException(lineNumber, "Malformed line!"); + } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) { + throw new MappingParseException(lineNumber, "Malformed line:\n" + line); } } @@ -118,7 +119,8 @@ public class MappingsReader { } } - private FieldMapping readField(String[] parts) { + /* TEMP */ + protected FieldMapping readField(String[] parts) { return new FieldMapping(parts[1], new Type(parts[3]), parts[2]); } -- cgit v1.2.3 From 250fe96550f9ab9179446a70cb15e8a8e23ae790 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Feb 2015 16:33:43 -0500 Subject: Don't automatically move mappings around. We're more interested in stability than backwards compatibility now. Just drop them instead. --- src/cuchaz/enigma/Deobfuscator.java | 87 ++++++------------------------------- 1 file changed, 13 insertions(+), 74 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 818bfd46..b4ac501f 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -114,86 +114,20 @@ public class Deobfuscator { val = new Mappings(); } - // pass 1: look for any classes that got moved to inner classes - Map renames = Maps.newHashMap(); - for (ClassMapping classMapping : val.classes()) { - // make sure we strip the packages off of obfuscated inner classes - String innerClassName = new ClassEntry(classMapping.getObfName()).getSimpleName(); - String outerClassName = m_jarIndex.getOuterClass(innerClassName); - if (outerClassName != null) { - // build the composite class name - String newName = outerClassName + "$" + innerClassName; - - // add a rename - renames.put(classMapping.getObfName(), newName); - - System.out.println(String.format("Converted class mapping %s to %s", classMapping.getObfName(), newName)); - } - } - for (Map.Entry entry : renames.entrySet()) { - val.renameObfClass(entry.getKey(), entry.getValue()); - } - - // pass 2: look for fields/methods that are actually declared in superclasses - MappingsRenamer renamer = new MappingsRenamer(m_jarIndex, val); + // drop mappings that don't match the jar for (ClassMapping classMapping : Lists.newArrayList(val.classes())) { - ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfName()); - - // fields - for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { - FieldEntry fieldEntry = new FieldEntry(obfClassEntry, fieldMapping.getObfName(), fieldMapping.getObfType()); - ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(fieldEntry); - if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(fieldEntry.getClassEntry())) { - boolean wasMoved = renamer.moveFieldToObfClass(classMapping, fieldMapping, resolvedObfClassEntry); - if (wasMoved) { - System.out.println(String.format("Moved field %s to class %s", fieldEntry, resolvedObfClassEntry)); - } else { - System.err.println(String.format("WARNING: Would move field %s to class %s but the field was already there. Dropping instead.", fieldEntry, resolvedObfClassEntry)); - } - } + if (!checkClassMapping(classMapping)) { + val.removeClassMapping(classMapping); } - - // methods - for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { - // skip constructors - if (methodMapping.isConstructor()) { - continue; - } - - MethodEntry methodEntry = new MethodEntry( - obfClassEntry, - methodMapping.getObfName(), - methodMapping.getObfSignature() - ); - ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(methodEntry); - if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(methodEntry.getClassEntry())) { - boolean wasMoved = renamer.moveMethodToObfClass(classMapping, methodMapping, resolvedObfClassEntry); - if (wasMoved) { - System.out.println(String.format("Moved method %s to class %s", methodEntry, resolvedObfClassEntry)); - } else { - System.err.println(String.format("WARNING: Would move method %s to class %s but the method was already there. Dropping instead.", methodEntry, resolvedObfClassEntry)); - } - } - } - - // TODO: recurse to inner classes? - } - - // drop mappings that don't match the jar - List unknownClasses = Lists.newArrayList(); - for (ClassMapping classMapping : val.classes()) { - checkClassMapping(unknownClasses, classMapping); - } - if (!unknownClasses.isEmpty()) { - throw new Error("Unable to find classes in jar: " + unknownClasses); } m_mappings = val; - m_renamer = renamer; + m_renamer = new MappingsRenamer(m_jarIndex, val); m_translatorCache.clear(); } - private void checkClassMapping(List unknownClasses, ClassMapping classMapping) { + private boolean checkClassMapping(ClassMapping classMapping) { + // check the class ClassEntry classEntry = new ClassEntry(classMapping.getObfName()); String outerClassName = m_jarIndex.getOuterClass(classEntry.getSimpleName()); @@ -201,7 +135,7 @@ public class Deobfuscator { classEntry = new ClassEntry(outerClassName + "$" + classMapping.getObfName()); } if (!m_jarIndex.getObfClassEntries().contains(classEntry)) { - unknownClasses.add(classEntry); + return false; } // check the fields @@ -224,8 +158,13 @@ public class Deobfuscator { // check inner classes for (ClassMapping innerClassMapping : classMapping.innerClasses()) { - checkClassMapping(unknownClasses, innerClassMapping); + if (!checkClassMapping(innerClassMapping)) { + System.err.println("WARNING: unable to find inner class " + innerClassMapping + ". dropping mapping."); + classMapping.removeInnerClassMapping(innerClassMapping); + } } + + return true; } public Translator getTranslator(TranslationDirection direction) { -- cgit v1.2.3 From af1041731c8c0ce1846ff7e7b6052ed7327a5dbc Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Feb 2015 22:23:45 -0500 Subject: fix translation issues, particularly with fields --- src/cuchaz/enigma/Deobfuscator.java | 25 +-- src/cuchaz/enigma/MainFormatConverter.java | 4 +- src/cuchaz/enigma/analysis/JarIndex.java | 57 +++---- .../analysis/MethodImplementationsTreeNode.java | 1 + .../enigma/analysis/RelatedMethodChecker.java | 96 +++++++++++ .../enigma/analysis/SourceIndexClassVisitor.java | 11 +- src/cuchaz/enigma/analysis/TranslationIndex.java | 10 +- src/cuchaz/enigma/bytecode/ClassTranslator.java | 9 +- .../enigma/bytecode/MethodParameterWriter.java | 4 +- src/cuchaz/enigma/convert/ClassIdentity.java | 6 +- src/cuchaz/enigma/convert/ClassMatcher.java | 6 +- src/cuchaz/enigma/gui/GuiController.java | 9 +- .../enigma/mapping/BehaviorEntryFactory.java | 57 ------- src/cuchaz/enigma/mapping/EntryFactory.java | 184 +++++++++++++++++++++ src/cuchaz/enigma/mapping/JavassistUtil.java | 85 ---------- test/cuchaz/enigma/EntryFactory.java | 68 -------- test/cuchaz/enigma/TestEntryFactory.java | 68 ++++++++ .../enigma/TestJarIndexConstructorReferences.java | 2 +- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 2 +- test/cuchaz/enigma/TestJarIndexLoneClass.java | 5 +- test/cuchaz/enigma/TestTokensConstructors.java | 2 +- test/cuchaz/enigma/TestTranslator.java | 86 ++++++++-- test/cuchaz/enigma/TestType.java | 2 +- test/cuchaz/enigma/inputs/translation/A.java | 8 - test/cuchaz/enigma/inputs/translation/A_Basic.java | 22 +++ .../enigma/inputs/translation/B_BaseClass.java | 15 ++ .../enigma/inputs/translation/C_SubClass.java | 17 ++ test/cuchaz/enigma/resources/translation.mappings | 23 ++- 28 files changed, 574 insertions(+), 310 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/RelatedMethodChecker.java delete mode 100644 src/cuchaz/enigma/mapping/BehaviorEntryFactory.java create mode 100644 src/cuchaz/enigma/mapping/EntryFactory.java delete mode 100644 src/cuchaz/enigma/mapping/JavassistUtil.java delete mode 100644 test/cuchaz/enigma/EntryFactory.java create mode 100644 test/cuchaz/enigma/TestEntryFactory.java delete mode 100644 test/cuchaz/enigma/inputs/translation/A.java create mode 100644 test/cuchaz/enigma/inputs/translation/A_Basic.java create mode 100644 test/cuchaz/enigma/inputs/translation/B_BaseClass.java create mode 100644 test/cuchaz/enigma/inputs/translation/C_SubClass.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index b4ac501f..b54a94ac 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -42,16 +42,17 @@ import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.JarClassIterator; import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.analysis.RelatedMethodChecker; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.FieldMapping; import cuchaz.enigma.mapping.Mappings; @@ -115,25 +116,27 @@ public class Deobfuscator { } // drop mappings that don't match the jar + RelatedMethodChecker relatedMethodChecker = new RelatedMethodChecker(m_jarIndex); for (ClassMapping classMapping : Lists.newArrayList(val.classes())) { - if (!checkClassMapping(classMapping)) { + if (!checkClassMapping(relatedMethodChecker, classMapping)) { val.removeClassMapping(classMapping); } } + // check for related method inconsistencies + if (relatedMethodChecker.hasProblems()) { + throw new Error("Related methods are inconsistent! Need to fix the mappings manually.\n" + relatedMethodChecker.getReport()); + } + m_mappings = val; m_renamer = new MappingsRenamer(m_jarIndex, val); m_translatorCache.clear(); } - private boolean checkClassMapping(ClassMapping classMapping) { + private boolean checkClassMapping(RelatedMethodChecker relatedMethodChecker, ClassMapping classMapping) { // check the class - ClassEntry classEntry = new ClassEntry(classMapping.getObfName()); - String outerClassName = m_jarIndex.getOuterClass(classEntry.getSimpleName()); - if (outerClassName != null) { - classEntry = new ClassEntry(outerClassName + "$" + classMapping.getObfName()); - } + ClassEntry classEntry = EntryFactory.getObfClassEntry(m_jarIndex, classMapping); if (!m_jarIndex.getObfClassEntries().contains(classEntry)) { return false; } @@ -149,16 +152,18 @@ public class Deobfuscator { // check methods for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { - BehaviorEntry obfBehaviorEntry = BehaviorEntryFactory.createObf(classEntry, methodMapping); + BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); if (!m_jarIndex.containsObfBehavior(obfBehaviorEntry)) { System.err.println("WARNING: unable to find behavior " + obfBehaviorEntry + ". dropping mapping."); classMapping.removeMethodMapping(methodMapping); } + + relatedMethodChecker.checkMethod(classEntry, methodMapping); } // check inner classes for (ClassMapping innerClassMapping : classMapping.innerClasses()) { - if (!checkClassMapping(innerClassMapping)) { + if (!checkClassMapping(relatedMethodChecker, innerClassMapping)) { System.err.println("WARNING: unable to find inner class " + innerClassMapping + ". dropping mapping."); classMapping.removeInnerClassMapping(innerClassMapping); } diff --git a/src/cuchaz/enigma/MainFormatConverter.java b/src/cuchaz/enigma/MainFormatConverter.java index 1848144d..d4bb2db3 100644 --- a/src/cuchaz/enigma/MainFormatConverter.java +++ b/src/cuchaz/enigma/MainFormatConverter.java @@ -18,7 +18,7 @@ import cuchaz.enigma.mapping.ClassMapping; import cuchaz.enigma.mapping.ClassNameReplacer; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.FieldMapping; -import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; @@ -35,7 +35,7 @@ public class MainFormatConverter { Map fieldTypes = Maps.newHashMap(); for (CtClass c : JarClassIterator.classes(jar)) { for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); fieldTypes.put(getFieldKey(fieldEntry), moveClasssesOutOfDefaultPackage(fieldEntry.getType())); } } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index f54bedad..24d110ee 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -42,12 +42,11 @@ import cuchaz.enigma.Constants; import cuchaz.enigma.bytecode.ClassRenamer; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.JavassistUtil; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; @@ -92,10 +91,10 @@ public class JarIndex { for (CtClass c : JarClassIterator.classes(jar)) { ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); for (CtField field : c.getDeclaredFields()) { - m_access.put(JavassistUtil.getFieldEntry(field), Access.get(field)); + m_access.put(EntryFactory.getFieldEntry(field), Access.get(field)); } for (CtBehavior behavior : c.getDeclaredBehaviors()) { - m_access.put(JavassistUtil.getBehaviorEntry(behavior), Access.get(behavior)); + m_access.put(EntryFactory.getBehaviorEntry(behavior), Access.get(behavior)); } } @@ -166,7 +165,7 @@ public class JarIndex { private void indexBehavior(CtBehavior behavior) { // get the behavior entry - final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); if (behaviorEntry instanceof MethodEntry) { MethodEntry methodEntry = (MethodEntry)behaviorEntry; @@ -178,12 +177,12 @@ public class JarIndex { private void indexBehaviorReferences(CtBehavior behavior) { // index method calls - final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); try { behavior.instrument(new ExprEditor() { @Override public void edit(MethodCall call) { - MethodEntry calledMethodEntry = JavassistUtil.getMethodEntry(call); + MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call); ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledMethodEntry); if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { calledMethodEntry = new MethodEntry( @@ -202,7 +201,7 @@ public class JarIndex { @Override public void edit(FieldAccess call) { - FieldEntry calledFieldEntry = JavassistUtil.getFieldEntry(call); + FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call); ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry); if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry); @@ -217,7 +216,7 @@ public class JarIndex { @Override public void edit(ConstructorCall call) { - ConstructorEntry calledConstructorEntry = JavassistUtil.getConstructorEntry(call); + ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); EntryReference reference = new EntryReference( calledConstructorEntry, call.getMethodName(), @@ -228,7 +227,7 @@ public class JarIndex { @Override public void edit(NewExpr call) { - ConstructorEntry calledConstructorEntry = JavassistUtil.getConstructorEntry(call); + ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); EntryReference reference = new EntryReference( calledConstructorEntry, call.getClassName(), @@ -256,7 +255,7 @@ public class JarIndex { } ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); - ConstructorEntry constructorEntry = JavassistUtil.getConstructorEntry(constructor); + ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); // gather the classes from the illegally-set synthetic fields Set illegallySetClasses = Sets.newHashSet(); @@ -422,7 +421,7 @@ public class JarIndex { CtConstructor constructor = c.getDeclaredConstructors()[0]; // is this constructor called exactly once? - ConstructorEntry constructorEntry = JavassistUtil.getConstructorEntry(constructor); + ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); Collection> references = getBehaviorReferences(constructorEntry); if (references.size() != 1) { return null; @@ -520,17 +519,17 @@ public class JarIndex { return rootNode; } - public MethodImplementationsTreeNode getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { + public List getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { - MethodEntry interfaceMethodEntry; + List interfaceMethodEntries = Lists.newArrayList(); // is this method on an interface? if (isInterface(obfMethodEntry.getClassName())) { - interfaceMethodEntry = obfMethodEntry; + interfaceMethodEntries.add(obfMethodEntry); } else { // get the interface class - List methodInterfaces = Lists.newArrayList(); for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) { + // is this method defined in this interface? MethodEntry methodInterface = new MethodEntry( new ClassEntry(interfaceName), @@ -538,21 +537,20 @@ public class JarIndex { obfMethodEntry.getSignature() ); if (containsObfBehavior(methodInterface)) { - methodInterfaces.add(methodInterface); + interfaceMethodEntries.add(methodInterface); } } - if (methodInterfaces.isEmpty()) { - return null; - } - if (methodInterfaces.size() > 1) { - throw new Error("Too many interfaces define this method! This is not yet supported by Enigma!"); - } - interfaceMethodEntry = methodInterfaces.get(0); } - MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); - rootNode.load(this); - return rootNode; + List nodes = Lists.newArrayList(); + if (!interfaceMethodEntries.isEmpty()) { + for (MethodEntry interfaceMethodEntry : interfaceMethodEntries) { + MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); + node.load(this); + nodes.add(node); + } + } + return nodes; } public Set getRelatedMethodImplementations(MethodEntry obfMethodEntry) { @@ -569,9 +567,8 @@ public class JarIndex { } // look at interface methods too - MethodImplementationsTreeNode implementations = getMethodImplementations(null, methodEntry); - if (implementations != null) { - getRelatedMethodImplementations(methodEntries, implementations); + for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(null, methodEntry)) { + getRelatedMethodImplementations(methodEntries, implementationsNode); } // recurse diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index 10092268..6cafc55b 100644 --- a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -63,6 +63,7 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { } public void load(JarIndex index) { + // get all method implementations List nodes = Lists.newArrayList(); for (String implementingClassName : index.getImplementingClasses(m_entry.getClassName())) { diff --git a/src/cuchaz/enigma/analysis/RelatedMethodChecker.java b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java new file mode 100644 index 00000000..5bd67a0a --- /dev/null +++ b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java @@ -0,0 +1,96 @@ +package cuchaz.enigma.analysis; + +import java.util.Map; +import java.util.Set; + +import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.MethodMapping; + +public class RelatedMethodChecker { + + private JarIndex m_jarIndex; + private Map,String> m_deobfNamesByGroup; + private Map m_deobfNamesByObfMethod; + private Map> m_groupsByObfMethod; + private Set> m_inconsistentGroups; + + public RelatedMethodChecker(JarIndex jarIndex) { + m_jarIndex = jarIndex; + m_deobfNamesByGroup = Maps.newHashMap(); + m_deobfNamesByObfMethod = Maps.newHashMap(); + m_groupsByObfMethod = Maps.newHashMap(); + m_inconsistentGroups = Sets.newHashSet(); + } + + public void checkMethod(ClassEntry classEntry, MethodMapping methodMapping) { + + // TEMP: disable the expensive check for now, maybe we can optimize it later, or just use it for debugging + if (true) return; + + BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); + if (!(obfBehaviorEntry instanceof MethodEntry)) { + // only methods have related implementations + return; + } + MethodEntry obfMethodEntry = (MethodEntry)obfBehaviorEntry; + String deobfName = methodMapping.getDeobfName(); + m_deobfNamesByObfMethod.put(obfMethodEntry, deobfName); + + // have we seen this method's group before? + Set group = m_groupsByObfMethod.get(obfMethodEntry); + if (group == null) { + + // no, compute the group and save the name + group = m_jarIndex.getRelatedMethodImplementations(obfMethodEntry); + m_deobfNamesByGroup.put(group, deobfName); + + assert(group.contains(obfMethodEntry)); + for (MethodEntry relatedMethodEntry : group) { + m_groupsByObfMethod.put(relatedMethodEntry, group); + } + } + + // check the name + if (!sameName(m_deobfNamesByGroup.get(group), deobfName)) { + m_inconsistentGroups.add(group); + } + } + + private boolean sameName(String a, String b) { + if (a == null && b == null) { + return true; + } else if (a != null && b != null) { + return a.equals(b); + } + return false; + } + + public boolean hasProblems() { + return m_inconsistentGroups.size() > 0; + } + + public String getReport() { + StringBuilder buf = new StringBuilder(); + buf.append(m_inconsistentGroups.size()); + buf.append(" groups of methods related by inheritance and/or interfaces have different deobf names!\n"); + for (Set group : m_inconsistentGroups) { + buf.append("\tGroup with "); + buf.append(group.size()); + buf.append(" methods:\n"); + for (MethodEntry methodEntry : group) { + buf.append("\t\t"); + buf.append(methodEntry.toString()); + buf.append(" => "); + buf.append(m_deobfNamesByObfMethod.get(methodEntry)); + buf.append("\n"); + } + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index e2ff3004..d6692f60 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -26,11 +26,10 @@ import com.strobel.decompiler.languages.java.ast.TypeDeclaration; import com.strobel.decompiler.languages.java.ast.VariableInitializer; import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.Signature; import cuchaz.enigma.mapping.Type; public class SourceIndexClassVisitor extends SourceIndexVisitor { @@ -69,12 +68,13 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(classEntry, def.getName(), def.getSignature()); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(def); AstNode tokenNode = node.getNameToken(); + if (behaviorEntry instanceof ConstructorEntry) { ConstructorEntry constructorEntry = (ConstructorEntry)behaviorEntry; if (constructorEntry.isStatic()) { + // for static initializers, check elsewhere for the token node tokenNode = node.getModifiers().firstOrNullObject(); } } @@ -85,8 +85,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(def.getSignature())); + ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(def); index.addDeclaration(node.getNameToken(), constructorEntry); return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); } diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index 7597c3ae..8651ebd6 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -37,7 +37,7 @@ import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.Translator; public class TranslationIndex implements Serializable { @@ -85,23 +85,23 @@ public class TranslationIndex implements Serializable { public void indexClass(CtClass c) { - ClassEntry classEntry = JavassistUtil.getClassEntry(c); + ClassEntry classEntry = EntryFactory.getClassEntry(c); // add the superclass - ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c); + ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); if (!isJre(classEntry) && superclassEntry != null && !isJre(superclassEntry)) { m_superclasses.put(classEntry, superclassEntry); } // add fields for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); } // add behaviors for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); } } diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 4dba0d87..4167731a 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -23,10 +23,9 @@ import javassist.bytecode.SourceFileAttribute; import com.google.common.collect.Maps; import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.JavassistUtil; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Signature; import cuchaz.enigma.mapping.Translator; @@ -74,7 +73,7 @@ public class ClassTranslator { case ConstPool.CONST_InterfaceMethodref: { // translate the name and type - BehaviorEntry entry = BehaviorEntryFactory.create( + BehaviorEntry entry = EntryFactory.getBehaviorEntry( Descriptor.toJvmName(editor.getMemberrefClassname(i)), editor.getMemberrefName(i), editor.getMemberrefType(i) @@ -95,7 +94,7 @@ public class ClassTranslator { for (CtField field : c.getDeclaredFields()) { // translate the name - FieldEntry entry = JavassistUtil.getFieldEntry(field); + FieldEntry entry = EntryFactory.getFieldEntry(field); String translatedName = m_translator.translate(entry); if (translatedName != null) { field.setName(translatedName); @@ -112,7 +111,7 @@ public class ClassTranslator { CtMethod method = (CtMethod)behavior; // translate the name - MethodEntry entry = JavassistUtil.getMethodEntry(method); + MethodEntry entry = EntryFactory.getMethodEntry(method); String translatedName = m_translator.translate(entry); if (translatedName != null) { method.setName(translatedName); diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java index 853928c7..f64ca02a 100644 --- a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -17,7 +17,7 @@ import javassist.CtBehavior; import javassist.CtClass; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.Signature; import cuchaz.enigma.mapping.Translator; @@ -33,7 +33,7 @@ public class MethodParameterWriter { // Procyon will read method arguments from the "MethodParameters" attribute, so write those for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); // get the number of arguments Signature signature = behaviorEntry.getSignature(); diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index bb729a36..b5140124 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -53,7 +53,7 @@ import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassNameReplacer; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.Signature; public class ClassIdentity { @@ -116,13 +116,13 @@ public class ClassIdentity { m_references = HashMultiset.create(); if (useReferences) { for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); for (EntryReference reference : index.getFieldReferences(fieldEntry)) { addReference(reference); } } for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); for (EntryReference reference : index.getBehaviorReferences(behaviorEntry)) { addReference(reference); } diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index ccf6b787..d70b8ebb 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -42,7 +42,7 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; -import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsReader; @@ -242,13 +242,13 @@ public class ClassMatcher { System.err.println("\tAvailable dest methods:"); CtClass c = destLoader.loadClass(classMapping.getObfName()); for (CtBehavior behavior : c.getDeclaredBehaviors()) { - System.err.println("\t\t" + JavassistUtil.getBehaviorEntry(behavior)); + System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); } System.err.println("\tAvailable source methods:"); c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfName())); for (CtBehavior behavior : c.getDeclaredBehaviors()) { - System.err.println("\t\t" + JavassistUtil.getBehaviorEntry(behavior)); + System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); } } } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 61fea9c0..9fa633eb 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -186,14 +186,17 @@ public class GuiController { public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry(deobfMethodEntry); - MethodImplementationsTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodImplementations( + List rootNodes = m_deobfuscator.getJarIndex().getMethodImplementations( m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry ); - if (rootNode == null) { + if (rootNodes.isEmpty()) { return null; } - return MethodImplementationsTreeNode.findNode(rootNode, obfMethodEntry); + if (rootNodes.size() > 1) { + System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one."); + } + return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry); } public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { diff --git a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java deleted file mode 100644 index 61e501b7..00000000 --- a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.mapping; - -import javassist.CtBehavior; -import javassist.CtConstructor; -import javassist.CtMethod; -import javassist.bytecode.Descriptor; - -public class BehaviorEntryFactory { - - public static BehaviorEntry create(String className, String name, String signature) { - return create(new ClassEntry(className), name, signature); - } - - public static BehaviorEntry create(ClassEntry classEntry, String name, String signature) { - if (name.equals("")) { - return new ConstructorEntry(classEntry, new Signature(signature)); - } else if (name.equals("")) { - return new ConstructorEntry(classEntry); - } else { - return new MethodEntry(classEntry, name, new Signature(signature)); - } - } - - public static BehaviorEntry create(CtBehavior behavior) { - String className = Descriptor.toJvmName(behavior.getDeclaringClass().getName()); - if (behavior instanceof CtMethod) { - return create(className, behavior.getName(), behavior.getSignature()); - } else if (behavior instanceof CtConstructor) { - CtConstructor constructor = (CtConstructor)behavior; - if (constructor.isClassInitializer()) { - return create(className, "", null); - } else { - return create(className, "", constructor.getSignature()); - } - } else { - throw new IllegalArgumentException("Unable to create BehaviorEntry from " + behavior); - } - } - - public static BehaviorEntry createObf(ClassEntry classEntry, MethodMapping methodMapping) { - return create(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature().toString()); - } - - public static BehaviorEntry createDeobf(ClassEntry classEntry, MethodMapping methodMapping) { - return create(classEntry, methodMapping.getDeobfName(), methodMapping.getObfSignature().toString()); - } -} diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java new file mode 100644 index 00000000..dceea29d --- /dev/null +++ b/src/cuchaz/enigma/mapping/EntryFactory.java @@ -0,0 +1,184 @@ +package cuchaz.enigma.mapping; + +import java.util.List; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.CtMethod; +import javassist.bytecode.Descriptor; +import javassist.expr.ConstructorCall; +import javassist.expr.FieldAccess; +import javassist.expr.MethodCall; +import javassist.expr.NewExpr; + +import com.beust.jcommander.internal.Lists; +import com.strobel.assembler.metadata.MethodDefinition; + +import cuchaz.enigma.analysis.JarIndex; + +public class EntryFactory { + + public static ClassEntry getClassEntry(CtClass c) { + return new ClassEntry(Descriptor.toJvmName(c.getName())); + } + + public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { + return new ClassEntry(getChainedOuterClassName(jarIndex, classMapping.getObfName())); + } + + private static String getChainedOuterClassName(JarIndex jarIndex, String obfClassName) { + + // lookup the chain of outer classes + List obfOuterClassNames = Lists.newArrayList(); + String checkName = obfClassName; + while (true) { + + // if this class name has a package, then it can't be an inner class + if (!new ClassEntry(checkName).isInDefaultPackage()) { + break; + } + + String obfOuterClassName = jarIndex.getOuterClass(checkName); + if (obfOuterClassName != null) { + obfOuterClassNames.add(obfOuterClassName); + checkName = obfOuterClassName; + } else { + break; + } + } + + // build the chained class name + StringBuilder buf = new StringBuilder(); + for (int i=obfOuterClassNames.size()-1; i>=0; i--) { + buf.append(obfOuterClassNames.get(i)); + buf.append("$"); + } + buf.append(obfClassName); + return buf.toString(); + } + + public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { + return new ClassEntry(classMapping.getDeobfName()); + } + + public static ClassEntry getSuperclassEntry(CtClass c) { + return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); + } + + public static FieldEntry getFieldEntry(CtField field) { + return new FieldEntry( + getClassEntry(field.getDeclaringClass()), + field.getName(), + new Type(field.getFieldInfo().getDescriptor()) + ); + } + + public static FieldEntry getFieldEntry(FieldAccess call) { + return new FieldEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + call.getFieldName(), + new Type(call.getSignature()) + ); + } + + public static MethodEntry getMethodEntry(CtMethod method) { + return new MethodEntry( + getClassEntry(method.getDeclaringClass()), + method.getName(), + new Signature(method.getMethodInfo().getDescriptor()) + ); + } + + public static MethodEntry getMethodEntry(MethodCall call) { + return new MethodEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + call.getMethodName(), + new Signature(call.getSignature()) + ); + } + + public static MethodEntry getMethodEntry(MethodDefinition def) { + return new MethodEntry( + new ClassEntry(def.getDeclaringType().getInternalName()), + def.getName(), + new Signature(def.getSignature()) + ); + } + + public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { + if (constructor.isClassInitializer()) { + return new ConstructorEntry( + getClassEntry(constructor.getDeclaringClass()) + ); + } else { + return new ConstructorEntry( + getClassEntry(constructor.getDeclaringClass()), + new Signature(constructor.getMethodInfo().getDescriptor()) + ); + } + } + + public static ConstructorEntry getConstructorEntry(ConstructorCall call) { + return new ConstructorEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + new Signature(call.getSignature()) + ); + } + + public static ConstructorEntry getConstructorEntry(NewExpr call) { + return new ConstructorEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + new Signature(call.getSignature()) + ); + } + + public static ConstructorEntry getConstructorEntry(MethodDefinition def) { + if (def.isTypeInitializer()) { + return new ConstructorEntry( + new ClassEntry(def.getDeclaringType().getInternalName()) + ); + } else { + return new ConstructorEntry( + new ClassEntry(def.getDeclaringType().getInternalName()), + new Signature(def.getSignature()) + ); + } + } + + public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { + if (behavior instanceof CtMethod) { + return getMethodEntry((CtMethod)behavior); + } else if (behavior instanceof CtConstructor) { + return getConstructorEntry((CtConstructor)behavior); + } + throw new Error("behavior is neither Method nor Constructor!"); + } + + public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) { + return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); + } + + public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) { + if (behaviorName.equals("")) { + return new ConstructorEntry(classEntry, behaviorSignature); + } else if(behaviorName.equals("")) { + return new ConstructorEntry(classEntry); + } else { + return new MethodEntry(classEntry, behaviorName, behaviorSignature); + } + } + + public static BehaviorEntry getBehaviorEntry(MethodDefinition def) { + if (def.isConstructor() || def.isTypeInitializer()) { + return getConstructorEntry(def); + } else { + return getMethodEntry(def); + } + } + + public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { + return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); + } +} diff --git a/src/cuchaz/enigma/mapping/JavassistUtil.java b/src/cuchaz/enigma/mapping/JavassistUtil.java deleted file mode 100644 index 0d6ce6a1..00000000 --- a/src/cuchaz/enigma/mapping/JavassistUtil.java +++ /dev/null @@ -1,85 +0,0 @@ -package cuchaz.enigma.mapping; - -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.CtConstructor; -import javassist.CtField; -import javassist.CtMethod; -import javassist.bytecode.Descriptor; -import javassist.expr.ConstructorCall; -import javassist.expr.FieldAccess; -import javassist.expr.MethodCall; -import javassist.expr.NewExpr; - -public class JavassistUtil { - - public static ClassEntry getClassEntry(CtClass c) { - return new ClassEntry(Descriptor.toJvmName(c.getName())); - } - - public static ClassEntry getSuperclassEntry(CtClass c) { - return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); - } - - public static MethodEntry getMethodEntry(CtMethod method) { - return new MethodEntry( - getClassEntry(method.getDeclaringClass()), - method.getName(), - new Signature(method.getMethodInfo().getDescriptor()) - ); - } - - public static MethodEntry getMethodEntry(MethodCall call) { - return new MethodEntry( - new ClassEntry(Descriptor.toJvmName(call.getClassName())), - call.getMethodName(), - new Signature(call.getSignature()) - ); - } - - public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { - return new ConstructorEntry( - getClassEntry(constructor.getDeclaringClass()), - new Signature(constructor.getMethodInfo().getDescriptor()) - ); - } - - public static ConstructorEntry getConstructorEntry(ConstructorCall call) { - return new ConstructorEntry( - new ClassEntry(Descriptor.toJvmName(call.getClassName())), - new Signature(call.getSignature()) - ); - } - - public static ConstructorEntry getConstructorEntry(NewExpr call) { - return new ConstructorEntry( - new ClassEntry(Descriptor.toJvmName(call.getClassName())), - new Signature(call.getSignature()) - ); - } - - public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { - if (behavior instanceof CtMethod) { - return getMethodEntry((CtMethod)behavior); - } else if (behavior instanceof CtConstructor) { - return getConstructorEntry((CtConstructor)behavior); - } - throw new Error("behavior is neither Method nor Constructor!"); - } - - public static FieldEntry getFieldEntry(CtField field) { - return new FieldEntry( - getClassEntry(field.getDeclaringClass()), - field.getName(), - new Type(field.getFieldInfo().getDescriptor()) - ); - } - - public static FieldEntry getFieldEntry(FieldAccess call) { - return new FieldEntry( - new ClassEntry(Descriptor.toJvmName(call.getClassName())), - call.getFieldName(), - new Type(call.getSignature()) - ); - } -} diff --git a/test/cuchaz/enigma/EntryFactory.java b/test/cuchaz/enigma/EntryFactory.java deleted file mode 100644 index 8c94bdf3..00000000 --- a/test/cuchaz/enigma/EntryFactory.java +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin.\ - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma; - -import cuchaz.enigma.analysis.EntryReference; -import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.ConstructorEntry; -import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.MethodEntry; -import cuchaz.enigma.mapping.Signature; -import cuchaz.enigma.mapping.Type; - -public class EntryFactory { - - public static ClassEntry newClass(String name) { - return new ClassEntry(name); - } - - public static FieldEntry newField(String className, String fieldName, String fieldType) { - return newField(newClass(className), fieldName, fieldType); - } - - public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) { - return new FieldEntry(classEntry, fieldName, new Type(fieldType)); - } - - public static MethodEntry newMethod(String className, String methodName, String methodSignature) { - return newMethod(newClass(className), methodName, methodSignature); - } - - public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) { - return new MethodEntry(classEntry, methodName, new Signature(methodSignature)); - } - - public static ConstructorEntry newConstructor(String className, String signature) { - return newConstructor(newClass(className), signature); - } - - public static ConstructorEntry newConstructor(ClassEntry classEntry, String signature) { - return new ConstructorEntry(classEntry, new Signature(signature)); - } - - public static EntryReference newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) { - return new EntryReference(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature)); - } - - public static EntryReference newFieldReferenceByConstructor(FieldEntry fieldEntry, String callerClassName, String callerSignature) { - return new EntryReference(fieldEntry, "", newConstructor(callerClassName, callerSignature)); - } - - public static EntryReference newBehaviorReferenceByMethod(BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature) { - return new EntryReference(behaviorEntry, "", newMethod(callerClassName, callerName, callerSignature)); - } - - public static EntryReference newBehaviorReferenceByConstructor(BehaviorEntry behaviorEntry, String callerClassName, String callerSignature) { - return new EntryReference(behaviorEntry, "", newConstructor(callerClassName, callerSignature)); - } -} diff --git a/test/cuchaz/enigma/TestEntryFactory.java b/test/cuchaz/enigma/TestEntryFactory.java new file mode 100644 index 00000000..754f3081 --- /dev/null +++ b/test/cuchaz/enigma/TestEntryFactory.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Signature; +import cuchaz.enigma.mapping.Type; + +public class TestEntryFactory { + + public static ClassEntry newClass(String name) { + return new ClassEntry(name); + } + + public static FieldEntry newField(String className, String fieldName, String fieldType) { + return newField(newClass(className), fieldName, fieldType); + } + + public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) { + return new FieldEntry(classEntry, fieldName, new Type(fieldType)); + } + + public static MethodEntry newMethod(String className, String methodName, String methodSignature) { + return newMethod(newClass(className), methodName, methodSignature); + } + + public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) { + return new MethodEntry(classEntry, methodName, new Signature(methodSignature)); + } + + public static ConstructorEntry newConstructor(String className, String signature) { + return newConstructor(newClass(className), signature); + } + + public static ConstructorEntry newConstructor(ClassEntry classEntry, String signature) { + return new ConstructorEntry(classEntry, new Signature(signature)); + } + + public static EntryReference newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) { + return new EntryReference(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature)); + } + + public static EntryReference newFieldReferenceByConstructor(FieldEntry fieldEntry, String callerClassName, String callerSignature) { + return new EntryReference(fieldEntry, "", newConstructor(callerClassName, callerSignature)); + } + + public static EntryReference newBehaviorReferenceByMethod(BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature) { + return new EntryReference(behaviorEntry, "", newMethod(callerClassName, callerName, callerSignature)); + } + + public static EntryReference newBehaviorReferenceByConstructor(BehaviorEntry behaviorEntry, String callerClassName, String callerSignature) { + return new EntryReference(behaviorEntry, "", newConstructor(callerClassName, callerSignature)); + } +} diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java index 22812fea..e1a30226 100644 --- a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -10,7 +10,7 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.*; +import static cuchaz.enigma.TestEntryFactory.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 349d33b1..6e2c1ad3 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -10,7 +10,7 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.*; +import static cuchaz.enigma.TestEntryFactory.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index c0ac8d7d..768284f9 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -11,7 +11,7 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.*; +import static cuchaz.enigma.TestEntryFactory.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; @@ -96,8 +96,7 @@ public class TestJarIndexLoneClass { @Test public void methodImplementations() { MethodEntry source = newMethod("none/a", "a", "()Ljava/lang/String;"); - MethodImplementationsTreeNode node = m_index.getMethodImplementations(new Translator(), source); - assertThat(node, is(nullValue())); + assertThat(m_index.getMethodImplementations(new Translator(), source), is(empty())); } @Test diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java index 6758d2a7..a563f832 100644 --- a/test/cuchaz/enigma/TestTokensConstructors.java +++ b/test/cuchaz/enigma/TestTokensConstructors.java @@ -10,7 +10,7 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.*; +import static cuchaz.enigma.TestEntryFactory.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; diff --git a/test/cuchaz/enigma/TestTranslator.java b/test/cuchaz/enigma/TestTranslator.java index f91c5852..3fe1680f 100644 --- a/test/cuchaz/enigma/TestTranslator.java +++ b/test/cuchaz/enigma/TestTranslator.java @@ -1,6 +1,6 @@ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.*; +import static cuchaz.enigma.TestEntryFactory.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; @@ -8,8 +8,10 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.jar.JarFile; +import org.junit.BeforeClass; import org.junit.Test; +import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.TranslationDirection; @@ -18,25 +20,85 @@ import cuchaz.enigma.mapping.Translator; public class TestTranslator { - private Deobfuscator m_deobfuscator; - private Mappings m_mappings; + private static Deobfuscator m_deobfuscator; + private static Mappings m_mappings; + private static Translator m_deobfTranslator; + private static Translator m_obfTranslator; - public TestTranslator() + @BeforeClass + public static void beforeClass() throws Exception { m_deobfuscator = new Deobfuscator(new JarFile("build/testTranslation.obf.jar")); - try (InputStream in = getClass().getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) { + try (InputStream in = TestTranslator.class.getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) { m_mappings = new MappingsReader().read(new InputStreamReader(in)); m_deobfuscator.setMappings(m_mappings); + m_deobfTranslator = m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating); + m_obfTranslator = m_deobfuscator.getTranslator(TranslationDirection.Obfuscating); } } @Test - public void deobfuscatingTranslations() - throws Exception { - Translator translator = m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating); - assertThat(translator.translateEntry(newClass("none/a")), is(newClass("deobf/A"))); - assertThat(translator.translateEntry(newField("none/a", "a", "I")), is(newField("deobf/A", "one", "I"))); - assertThat(translator.translateEntry(newField("none/a", "a", "F")), is(newField("deobf/A", "two", "F"))); - assertThat(translator.translateEntry(newField("none/a", "a", "Ljava/lang/String;")), is(newField("deobf/A", "three", "Ljava/lang/String;"))); + public void basicClasses() { + assertMapping(newClass("none/a"), newClass("deobf/A_Basic")); + assertMapping(newClass("none/b"), newClass("deobf/B_BaseClass")); + assertMapping(newClass("none/c"), newClass("deobf/C_SubClass")); + } + + @Test + public void basicFields() { + assertMapping(newField("none/a", "a", "I"), newField("deobf/A_Basic", "f1", "I")); + assertMapping(newField("none/a", "a", "F"), newField("deobf/A_Basic", "f2", "F")); + assertMapping(newField("none/a", "a", "Ljava/lang/String;"), newField("deobf/A_Basic", "f3", "Ljava/lang/String;")); + } + + @Test + public void basicMethods() { + assertMapping(newMethod("none/a", "a", "()V"), newMethod("deobf/A_Basic", "m1", "()V")); + assertMapping(newMethod("none/a", "a", "()I"), newMethod("deobf/A_Basic", "m2", "()I")); + assertMapping(newMethod("none/a", "a", "(I)V"), newMethod("deobf/A_Basic", "m3", "(I)V")); + assertMapping(newMethod("none/a", "a", "(I)I"), newMethod("deobf/A_Basic", "m4", "(I)I")); + } + + // TODO: basic constructors + + @Test + public void inheritanceFields() { + assertMapping(newField("none/b", "a", "I"), newField("deobf/B_BaseClass", "f1", "I")); + assertMapping(newField("none/b", "a", "C"), newField("deobf/B_BaseClass", "f2", "C")); + assertMapping(newField("none/c", "b", "I"), newField("deobf/C_SubClass", "f3", "I")); + assertMapping(newField("none/c", "c", "I"), newField("deobf/C_SubClass", "f4", "I")); + } + + @Test + public void inheritanceFieldsShadowing() { + assertMapping(newField("none/c", "b", "C"), newField("deobf/C_SubClass", "f2", "C")); + } + + @Test + public void inheritanceFieldsBySubClass() { + assertMapping(newField("none/c", "a", "I"), newField("deobf/C_SubClass", "f1", "I")); + // NOTE: can't reference b.C by subclass since it's shadowed + } + + @Test + public void inheritanceMethods() { + assertMapping(newMethod("none/b", "a", "()I"), newMethod("deobf/B_BaseClass", "m1", "()I")); + assertMapping(newMethod("none/b", "b", "()I"), newMethod("deobf/B_BaseClass", "m2", "()I")); + assertMapping(newMethod("none/c", "c", "()I"), newMethod("deobf/C_SubClass", "m3", "()I")); + } + + @Test + public void inheritanceMethodsOverrides() { + assertMapping(newMethod("none/c", "a", "()I"), newMethod("deobf/C_SubClass", "m1", "()I")); + } + + @Test + public void inheritanceMethodsBySubClass() { + assertMapping(newMethod("none/c", "b", "()I"), newMethod("deobf/C_SubClass", "m2", "()I")); + } + + private void assertMapping(Entry obf, Entry deobf) { + assertThat(m_deobfTranslator.translateEntry(obf), is(deobf)); + assertThat(m_obfTranslator.translateEntry(deobf), is(obf)); } } diff --git a/test/cuchaz/enigma/TestType.java b/test/cuchaz/enigma/TestType.java index 7c3cebe2..93f864b0 100644 --- a/test/cuchaz/enigma/TestType.java +++ b/test/cuchaz/enigma/TestType.java @@ -7,7 +7,7 @@ import static org.hamcrest.Matchers.*; import cuchaz.enigma.mapping.Type; -import static cuchaz.enigma.EntryFactory.*; +import static cuchaz.enigma.TestEntryFactory.*; public class TestType { diff --git a/test/cuchaz/enigma/inputs/translation/A.java b/test/cuchaz/enigma/inputs/translation/A.java deleted file mode 100644 index b8aaf11e..00000000 --- a/test/cuchaz/enigma/inputs/translation/A.java +++ /dev/null @@ -1,8 +0,0 @@ -package cuchaz.enigma.inputs.translation; - -public class A { - - public int one; - public float two; - public String three; -} diff --git a/test/cuchaz/enigma/inputs/translation/A_Basic.java b/test/cuchaz/enigma/inputs/translation/A_Basic.java new file mode 100644 index 00000000..89307630 --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/A_Basic.java @@ -0,0 +1,22 @@ +package cuchaz.enigma.inputs.translation; + +public class A_Basic { + + public int one; + public float two; + public String three; + + public void m1() { + } + + public int m2() { + return 42; + } + + public void m3(int a1) { + } + + public int m4(int a1) { + return 5; // chosen by fair die roll, guaranteed to be random + } +} diff --git a/test/cuchaz/enigma/inputs/translation/B_BaseClass.java b/test/cuchaz/enigma/inputs/translation/B_BaseClass.java new file mode 100644 index 00000000..44fbc8db --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/B_BaseClass.java @@ -0,0 +1,15 @@ +package cuchaz.enigma.inputs.translation; + +public class B_BaseClass { + + public int f1; + public char f2; + + public int m1() { + return 5; + } + + public int m2() { + return 42; + } +} diff --git a/test/cuchaz/enigma/inputs/translation/C_SubClass.java b/test/cuchaz/enigma/inputs/translation/C_SubClass.java new file mode 100644 index 00000000..8fe0b799 --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/C_SubClass.java @@ -0,0 +1,17 @@ +package cuchaz.enigma.inputs.translation; + +public class C_SubClass extends B_BaseClass { + + public char f2; // shadows B_BaseClass.f2 + public int f3; + public int f4; + + @Override + public int m1() { + return 32; + } + + public int m3() { + return 7; + } +} diff --git a/test/cuchaz/enigma/resources/translation.mappings b/test/cuchaz/enigma/resources/translation.mappings index c71493e9..d87d437c 100644 --- a/test/cuchaz/enigma/resources/translation.mappings +++ b/test/cuchaz/enigma/resources/translation.mappings @@ -1,4 +1,19 @@ -CLASS none/a deobf/A - FIELD a one I - FIELD a two F - FIELD a three Ljava/lang/String; \ No newline at end of file +CLASS none/a deobf/A_Basic + FIELD a f1 I + FIELD a f2 F + FIELD a f3 Ljava/lang/String; + METHOD a m1 ()V + METHOD a m2 ()I + METHOD a m3 (I)V + METHOD a m4 (I)I +CLASS none/b deobf/B_BaseClass + FIELD a f1 I + FIELD a f2 C + METHOD a m1 ()I + METHOD b m2 ()I +CLASS none/c deobf/C_SubClass + FIELD b f2 C + FIELD b f3 I + FIELD c f4 I + METHOD a m1 ()I + METHOD c m3 ()I -- cgit v1.2.3 From ab6de199201f3cb292b986b2803d7d30b1485a47 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Feb 2015 23:31:24 -0500 Subject: work around bad tokens generated by procyon for now --- src/cuchaz/enigma/TranslatingTypeLoader.java | 5 ++++- src/cuchaz/enigma/analysis/SourceIndex.java | 9 +++++---- .../enigma/analysis/SourceIndexBehaviorVisitor.java | 21 ++------------------- .../enigma/analysis/SourceIndexClassVisitor.java | 4 ++-- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 6 +++--- .../inputs/translation/D_AnonymousTesting.java | 18 ++++++++++++++++++ 6 files changed, 34 insertions(+), 29 deletions(-) create mode 100644 test/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index cfa03a1a..8bec17fb 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -104,6 +104,7 @@ public class TranslatingTypeLoader implements ITypeLoader { } private byte[] loadType(String deobfClassName) { + ClassEntry deobfClassEntry = new ClassEntry(deobfClassName); ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(deobfClassEntry); @@ -176,7 +177,9 @@ public class TranslatingTypeLoader implements ITypeLoader { } } - public CtClass transformClass(CtClass c) throws IOException, NotFoundException, CannotCompileException { + public CtClass transformClass(CtClass c) + throws IOException, NotFoundException, CannotCompileException { + // we moved a lot of classes out of the default package into the none package // make sure all the class references are consistent ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index b43ab614..e31b8032 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -82,10 +82,11 @@ public class SourceIndex { // DEBUG // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); - // for tokens representing inner classes, make sure we only get the simple name - int pos = name.lastIndexOf('$'); - if (pos >= 0) { - token.end -= pos + 1; + // if the token has a $ in it, something's wrong. Ignore this token + if (name.lastIndexOf('$') >= 0) { + // DEBUG + System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); + return null; } return token; diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index b4094d9e..a9a055be 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -17,12 +17,10 @@ import com.strobel.assembler.metadata.ParameterDefinition; import com.strobel.assembler.metadata.TypeReference; import com.strobel.decompiler.languages.TextLocation; import com.strobel.decompiler.languages.java.ast.AstNode; -import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; import com.strobel.decompiler.languages.java.ast.IdentifierExpression; import com.strobel.decompiler.languages.java.ast.InvocationExpression; import com.strobel.decompiler.languages.java.ast.Keys; import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; -import com.strobel.decompiler.languages.java.ast.MethodDeclaration; import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; import com.strobel.decompiler.languages.java.ast.SimpleType; @@ -33,6 +31,7 @@ import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Signature; @@ -46,16 +45,6 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { m_behaviorEntry = behaviorEntry; } - @Override - public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { - return recurse(node, index); - } - - @Override - public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { - return recurse(node, index); - } - @Override public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); @@ -122,14 +111,8 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { @Override public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); MethodDefinition methodDef = (MethodDefinition)def.getMethod(); - BehaviorEntry behaviorEntry; - if (methodDef.isConstructor()) { - behaviorEntry = new ConstructorEntry(classEntry, new Signature(methodDef.getSignature())); - } else { - behaviorEntry = new MethodEntry(classEntry, methodDef.getName(), new Signature(methodDef.getSignature())); - } + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(methodDef); ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); index.addDeclaration(node.getNameToken(), argumentEntry); diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index d6692f60..f4f49568 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -94,7 +94,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getSignature())); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getErasedSignature())); assert (node.getVariables().size() == 1); VariableInitializer variable = node.getVariables().firstOrNullObject(); index.addDeclaration(variable.getNameToken(), fieldEntry); @@ -107,7 +107,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { // treat enum declarations as field declarations FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getSignature())); + FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getErasedSignature())); index.addDeclaration(node.getNameToken(), fieldEntry); return recurse(node, index); diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 817500b7..5350b86c 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -13,7 +13,6 @@ package cuchaz.enigma.bytecode; import java.util.Collection; import javassist.CtClass; -import javassist.bytecode.AccessFlag; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.EnclosingMethodAttribute; @@ -77,18 +76,19 @@ public class InnerClassWriter { int innerClassIndex = constPool.addClassInfo(obfClassEntry.getName()); int outerClassIndex = 0; int innerClassSimpleNameIndex = 0; + int accessFlags = 0; if (!m_jarIndex.isAnonymousClass(obfInnerClassName)) { outerClassIndex = constPool.addClassInfo(obfClassEntry.getOuterClassName()); innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getInnerClassName()); } - attr.append(innerClassIndex, outerClassIndex, innerClassSimpleNameIndex, c.getClassFile().getAccessFlags() & ~AccessFlag.SUPER); + attr.append(innerClassIndex, outerClassIndex, innerClassSimpleNameIndex, accessFlags); /* DEBUG System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", obfClassEntry, - attr.outerClass(attr.tableLength() - 1), attr.innerClass(attr.tableLength() - 1), + attr.outerClass(attr.tableLength() - 1), attr.innerName(attr.tableLength() - 1), Constants.NonePackage + "/" + obfInnerClassName, obfClassEntry.getName() diff --git a/test/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java b/test/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java new file mode 100644 index 00000000..a1166e20 --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java @@ -0,0 +1,18 @@ +package cuchaz.enigma.inputs.translation; + +import java.util.ArrayList; +import java.util.List; + +public class D_AnonymousTesting { + + public List getObjs() { + List objs = new ArrayList(); + objs.add(new Object() { + @Override + public String toString() { + return "Object!"; + } + }); + return objs; + } +} -- cgit v1.2.3 From 2b3c5c52865b40adfa93910d41738242f17338d4 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 10 Feb 2015 22:10:16 -0500 Subject: add BRIDGE flag to bridge methods --- build.py | 4 ++ src/cuchaz/enigma/TranslatingTypeLoader.java | 2 + src/cuchaz/enigma/analysis/BridgeMarker.java | 36 ++++++++++++++ src/cuchaz/enigma/analysis/JarIndex.java | 55 ++++++++++++++++++++++ .../enigma/inputs/translation/E_Bridges.java | 22 +++++++++ 5 files changed, 119 insertions(+) create mode 100644 src/cuchaz/enigma/analysis/BridgeMarker.java create mode 100644 test/cuchaz/enigma/inputs/translation/E_Bridges.java diff --git a/build.py b/build.py index 47294987..bc9561b9 100644 --- a/build.py +++ b/build.py @@ -106,6 +106,9 @@ def taskBuildTestJars(): buildTestJar("testInheritanceTree", "cuchaz/enigma/inputs/inheritanceTree/*.class") buildTestJar("testInnerClasses", "cuchaz/enigma/inputs/innerClasses/*.class") buildTestJar("testTranslation", "cuchaz/enigma/inputs/translation/*.class") + +def taskBuildTranslationTestJar(): + buildTestJar("testTranslation", "cuchaz/enigma/inputs/translation/*.class") def taskBuild(): ssjb.file.delete(DirBuild) @@ -115,6 +118,7 @@ def taskBuild(): ssjb.registerTask("getDeps", taskGetDeps) ssjb.registerTask("buildTestJars", taskBuildTestJars) +ssjb.registerTask("buildTranslationTestJar", taskBuildTranslationTestJar) ssjb.registerTask("build", taskBuild) ssjb.run() diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 8bec17fb..19e3d2ad 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -29,6 +29,7 @@ import com.strobel.assembler.metadata.Buffer; import com.strobel.assembler.metadata.ClasspathTypeLoader; import com.strobel.assembler.metadata.ITypeLoader; +import cuchaz.enigma.analysis.BridgeMarker; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.bytecode.ClassRenamer; import cuchaz.enigma.bytecode.ClassTranslator; @@ -198,6 +199,7 @@ public class TranslatingTypeLoader implements ITypeLoader { assertClassName(c, obfClassEntry); // do all kinds of deobfuscating transformations on the class + new BridgeMarker(m_jarIndex.getBridgedMethods()).markBridges(c); new MethodParameterWriter(m_deobfuscatingTranslator).writeMethodArguments(c); new ClassTranslator(m_deobfuscatingTranslator).translate(c); diff --git a/src/cuchaz/enigma/analysis/BridgeMarker.java b/src/cuchaz/enigma/analysis/BridgeMarker.java new file mode 100644 index 00000000..e80f87dc --- /dev/null +++ b/src/cuchaz/enigma/analysis/BridgeMarker.java @@ -0,0 +1,36 @@ +package cuchaz.enigma.analysis; + +import javassist.CtClass; +import javassist.CtMethod; +import javassist.bytecode.AccessFlag; + +import com.google.common.collect.BiMap; + +import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.MethodEntry; + +public class BridgeMarker { + + private BiMap m_bridgedMethods; + + public BridgeMarker(BiMap bridgedMethods) { + m_bridgedMethods = bridgedMethods; + } + + public void markBridges(CtClass c) { + + for (CtMethod method : c.getDeclaredMethods()) { + MethodEntry methodEntry = EntryFactory.getMethodEntry(method); + + // is this a bridge method? + MethodEntry bridgedMethodEntry = m_bridgedMethods.get(methodEntry); + if (bridgedMethodEntry != null) { + + // it's a bridge method! add the bridge flag + int flags = method.getMethodInfo().getAccessFlags(); + flags |= AccessFlag.BRIDGE; + method.getMethodInfo().setAccessFlags(flags); + } + } + } +} diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 24d110ee..797deb82 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -23,6 +23,8 @@ import javassist.CtBehavior; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; +import javassist.CtMethod; +import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import javassist.bytecode.Descriptor; import javassist.bytecode.FieldInfo; @@ -32,6 +34,8 @@ import javassist.expr.FieldAccess; import javassist.expr.MethodCall; import javassist.expr.NewExpr; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -62,6 +66,7 @@ public class JarIndex { private Multimap m_innerClasses; private Map m_outerClasses; private Map m_anonymousClasses; + private BiMap m_bridgedMethods; public JarIndex() { m_obfClassEntries = Sets.newHashSet(); @@ -74,6 +79,7 @@ public class JarIndex { m_innerClasses = HashMultimap.create(); m_outerClasses = Maps.newHashMap(); m_anonymousClasses = Maps.newHashMap(); + m_bridgedMethods = HashBiMap.create(); } public void indexJar(JarFile jar, boolean buildInnerClasses) { @@ -171,6 +177,12 @@ public class JarIndex { // index implementation m_methodImplementations.put(behaviorEntry.getClassName(), methodEntry); + + // look for bridge and bridged methods + CtMethod bridgedMethod = getBridgedMethod((CtMethod)behavior); + if (bridgedMethod != null) { + m_bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod)); + } } // looks like we don't care about constructors here } @@ -241,6 +253,45 @@ public class JarIndex { } } + private CtMethod getBridgedMethod(CtMethod method) { + + // bridge methods just call another method, cast it to the return type, and return the result + // let's see if we can detect this scenario + + // skip non-synthetic methods + if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) { + return null; + } + + // get all the called methods + final List methodCalls = Lists.newArrayList(); + try { + method.instrument(new ExprEditor() { + @Override + public void edit(MethodCall call) { + methodCalls.add(call); + } + }); + } catch (CannotCompileException ex) { + // this is stupid... we're not even compiling anything + throw new Error(ex); + } + + // is there just one? + if (methodCalls.size() != 1) { + return null; + } + MethodCall call = methodCalls.get(0); + + try { + // we have a bridge method! + return call.getMethod(); + } catch (NotFoundException ex) { + // can't find the type? not a bridge method + return null; + } + } + private String findOuterClass(CtClass c) { // inner classes: @@ -706,4 +757,8 @@ public class JarIndex { throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); } } + + public BiMap getBridgedMethods() { + return m_bridgedMethods; + } } diff --git a/test/cuchaz/enigma/inputs/translation/E_Bridges.java b/test/cuchaz/enigma/inputs/translation/E_Bridges.java new file mode 100644 index 00000000..dac50d39 --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/E_Bridges.java @@ -0,0 +1,22 @@ +package cuchaz.enigma.inputs.translation; + +import java.util.Iterator; + + +public class E_Bridges implements Iterator { + + @Override + public boolean hasNext() { + return false; + } + + @Override + public String next() { + // the compiler will generate a bridge for this method + return "foo"; + } + + @Override + public void remove() { + } +} -- cgit v1.2.3 From c6a194dcf933dd7a4e2bf6b92bcb417957aba765 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 10 Feb 2015 22:23:12 -0500 Subject: ignore harmless exceptions I can't fix fyi, it's really hard to test this fix because the exception is not generally reproducable --- src/cuchaz/enigma/ExceptionIgnorer.java | 24 ++++++++++++++++++++++++ src/cuchaz/enigma/gui/Gui.java | 9 ++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 src/cuchaz/enigma/ExceptionIgnorer.java diff --git a/src/cuchaz/enigma/ExceptionIgnorer.java b/src/cuchaz/enigma/ExceptionIgnorer.java new file mode 100644 index 00000000..37c67da7 --- /dev/null +++ b/src/cuchaz/enigma/ExceptionIgnorer.java @@ -0,0 +1,24 @@ +package cuchaz.enigma; + +public class ExceptionIgnorer { + + public static boolean shouldIgnore(Throwable t) { + + // is this that pesky concurrent access bug in the highlight painter system? + // (ancient ui code is ancient) + if (t instanceof ArrayIndexOutOfBoundsException) { + StackTraceElement[] stackTrace = t.getStackTrace(); + if (stackTrace.length > 1) { + + // does this stack frame match javax.swing.text.DefaultHighlighter.paint() ? + StackTraceElement frame = stackTrace[1]; + if (frame.getClassName().equals("javax.swing.text.DefaultHighlighter") && frame.getMethodName().equals("paint")) { + return true; + } + } + } + + return false; + } + +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 187ef5b5..00cff595 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -70,6 +70,7 @@ import jsyntaxpane.DefaultSyntaxKit; import com.google.common.collect.Lists; import cuchaz.enigma.Constants; +import cuchaz.enigma.ExceptionIgnorer; import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; import cuchaz.enigma.analysis.ClassImplementationsTreeNode; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; @@ -147,9 +148,11 @@ public class Gui { CrashDialog.init(m_frame); Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override - public void uncaughtException(Thread thread, Throwable ex) { - ex.printStackTrace(System.err); - CrashDialog.show(ex); + public void uncaughtException(Thread thread, Throwable t) { + t.printStackTrace(System.err); + if (!ExceptionIgnorer.shouldIgnore(t)) { + CrashDialog.show(t); + } } }); } -- cgit v1.2.3 From ef3c296d17d8213dfadd66212d66d9e92c089402 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 10 Feb 2015 22:32:00 -0500 Subject: fix issue with removing field mappings --- src/cuchaz/enigma/mapping/ClassMapping.java | 2 ++ src/cuchaz/enigma/mapping/MappingsRenamer.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 71332650..885400b4 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -221,6 +221,7 @@ public class ClassMapping implements Serializable, Comparable { public void setFieldName(String obfName, Type obfType, String deobfName) { + assert(deobfName != null); FieldMapping fieldMapping = m_fieldsByObf.get(getFieldKey(obfName, obfType)); if (fieldMapping == null) { fieldMapping = new FieldMapping(obfName, obfType, deobfName); @@ -316,6 +317,7 @@ public class ClassMapping implements Serializable, Comparable { //// ARGUMENTS //////// public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { + assert(argumentName != null); MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); if (methodMapping == null) { methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index 095e5e9a..ea343c4e 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -87,7 +87,7 @@ public class MappingsRenamer { public void removeFieldMapping(FieldEntry obf) { ClassMapping classMapping = getClassMappingOrInnerClassMapping(obf.getClassEntry()); - classMapping.setFieldName(obf.getName(), obf.getType(), null); + classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); } public void markFieldAsDeobfuscated(FieldEntry obf) { -- cgit v1.2.3 From 6044c91079ae416ecaff2412f8ca8653f39e6f83 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 10 Feb 2015 22:51:55 -0500 Subject: black list Object methods from deobfuscation --- src/cuchaz/enigma/Deobfuscator.java | 32 ++++++++++++++++++++++ .../enigma/inputs/translation/F_ObjectMethods.java | 19 +++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index b54a94ac..9cd6e41f 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -397,6 +397,38 @@ public class Deobfuscator { } public boolean isObfuscatedIdentifier(Entry obfEntry) { + + if (obfEntry instanceof MethodEntry) { + + // HACKHACK: Object methods are not obfuscated identifiers + MethodEntry obfMethodEntry = (MethodEntry)obfEntry; + String name = obfMethodEntry.getName(); + String sig = obfMethodEntry.getSignature().toString(); + if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) { + return false; + } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) { + return false; + } else if (name.equals("finalize") && sig.equals("()V")) { + return false; + } else if (name.equals("getClass") && sig.equals("()Ljava/lang/Class;")) { + return false; + } else if (name.equals("hashCode") && sig.equals("()I")) { + return false; + } else if (name.equals("notify") && sig.equals("()V")) { + return false; + } else if (name.equals("notifyAll") && sig.equals("()V")) { + return false; + } else if (name.equals("toString") && sig.equals("()Ljava/lang/String;")) { + return false; + } else if (name.equals("wait") && sig.equals("()V")) { + return false; + } else if (name.equals("wait") && sig.equals("(J)V")) { + return false; + } else if (name.equals("wait") && sig.equals("(JI)V")) { + return false; + } + } + return m_jarIndex.containsObfEntry(obfEntry); } diff --git a/test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java b/test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java new file mode 100644 index 00000000..4e091797 --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java @@ -0,0 +1,19 @@ +package cuchaz.enigma.inputs.translation; + +public class F_ObjectMethods { + + public void callEmAll() + throws Throwable { + clone(); + equals(this); + finalize(); + getClass(); + hashCode(); + notify(); + notifyAll(); + toString(); + wait(); + wait(0); + wait(0, 0); + } +} -- cgit v1.2.3 From 1bddb51a8370f96af2dfd61e75d72b155b71923e Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 10 Feb 2015 23:26:33 -0500 Subject: repackage as v0.7b --- build.py | 4 ++-- readme.txt | 4 ++-- src/cuchaz/enigma/Constants.java | 2 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 2 +- src/cuchaz/enigma/analysis/BridgeMarker.java | 11 ++++------- src/cuchaz/enigma/analysis/JarIndex.java | 10 ++++------ test/cuchaz/enigma/TestSourceIndex.java | 2 +- 7 files changed, 15 insertions(+), 20 deletions(-) diff --git a/build.py b/build.py index bc9561b9..f5723e05 100644 --- a/build.py +++ b/build.py @@ -18,8 +18,8 @@ import ssjb import ssjb.ivy -ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.6b") -ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.6b") +ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.7b") +ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.7b") # dependencies ExtraRepos = [ diff --git a/readme.txt b/readme.txt index 3844f54e..db294cd9 100644 --- a/readme.txt +++ b/readme.txt @@ -1,8 +1,8 @@ -Enigma v0.6 beta +Enigma v0.7 beta A tool for deobfuscation of Java bytecode -Copyright Jeff Martin, 2014 +Copyright Jeff Martin, 2015 LICENSE diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index a1ba2e98..3bf39995 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -12,7 +12,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.6 beta"; + public static final String Version = "0.7 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024 * 1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 19e3d2ad..12cde4b9 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -199,7 +199,7 @@ public class TranslatingTypeLoader implements ITypeLoader { assertClassName(c, obfClassEntry); // do all kinds of deobfuscating transformations on the class - new BridgeMarker(m_jarIndex.getBridgedMethods()).markBridges(c); + new BridgeMarker(m_jarIndex).markBridges(c); new MethodParameterWriter(m_deobfuscatingTranslator).writeMethodArguments(c); new ClassTranslator(m_deobfuscatingTranslator).translate(c); diff --git a/src/cuchaz/enigma/analysis/BridgeMarker.java b/src/cuchaz/enigma/analysis/BridgeMarker.java index e80f87dc..28e35171 100644 --- a/src/cuchaz/enigma/analysis/BridgeMarker.java +++ b/src/cuchaz/enigma/analysis/BridgeMarker.java @@ -3,18 +3,15 @@ package cuchaz.enigma.analysis; import javassist.CtClass; import javassist.CtMethod; import javassist.bytecode.AccessFlag; - -import com.google.common.collect.BiMap; - import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.MethodEntry; public class BridgeMarker { - private BiMap m_bridgedMethods; + private JarIndex m_jarIndex; - public BridgeMarker(BiMap bridgedMethods) { - m_bridgedMethods = bridgedMethods; + public BridgeMarker(JarIndex jarIndex) { + m_jarIndex = jarIndex; } public void markBridges(CtClass c) { @@ -23,7 +20,7 @@ public class BridgeMarker { MethodEntry methodEntry = EntryFactory.getMethodEntry(method); // is this a bridge method? - MethodEntry bridgedMethodEntry = m_bridgedMethods.get(methodEntry); + MethodEntry bridgedMethodEntry = m_jarIndex.getBridgedMethod(methodEntry); if (bridgedMethodEntry != null) { // it's a bridge method! add the bridge flag diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 797deb82..1c74f158 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -34,8 +34,6 @@ import javassist.expr.FieldAccess; import javassist.expr.MethodCall; import javassist.expr.NewExpr; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -66,7 +64,7 @@ public class JarIndex { private Multimap m_innerClasses; private Map m_outerClasses; private Map m_anonymousClasses; - private BiMap m_bridgedMethods; + private Map m_bridgedMethods; public JarIndex() { m_obfClassEntries = Sets.newHashSet(); @@ -79,7 +77,7 @@ public class JarIndex { m_innerClasses = HashMultimap.create(); m_outerClasses = Maps.newHashMap(); m_anonymousClasses = Maps.newHashMap(); - m_bridgedMethods = HashBiMap.create(); + m_bridgedMethods = Maps.newHashMap(); } public void indexJar(JarFile jar, boolean buildInnerClasses) { @@ -758,7 +756,7 @@ public class JarIndex { } } - public BiMap getBridgedMethods() { - return m_bridgedMethods; + public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) { + return m_bridgedMethods.get(bridgeMethodEntry); } } diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java index 357acb62..96a8923b 100644 --- a/test/cuchaz/enigma/TestSourceIndex.java +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -24,7 +24,7 @@ import cuchaz.enigma.mapping.ClassEntry; public class TestSourceIndex { // TEMP - //@Test + @Test public void indexEverything() throws Exception { Deobfuscator deobfuscator = new Deobfuscator(new JarFile("input/1.8.jar")); -- cgit v1.2.3 -- cgit v1.2.3 From ddaa0f6bdeeec392764ff36d822d39baf154d8c6 Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 20 Feb 2015 18:19:57 -0500 Subject: better error messages for procyon type resolution --- src/cuchaz/enigma/Deobfuscator.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 9cd6e41f..c1954fc7 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -31,6 +31,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.strobel.assembler.metadata.MetadataSystem; import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; import com.strobel.decompiler.DecompilerContext; import com.strobel.decompiler.DecompilerSettings; import com.strobel.decompiler.PlainTextOutput; @@ -225,9 +226,15 @@ public class Deobfuscator { getTranslator(TranslationDirection.Obfuscating), getTranslator(TranslationDirection.Deobfuscating) )); + + // see if procyon can find the type + TypeReference type = new MetadataSystem(m_settings.getTypeLoader()).lookupType(lookupClassName); + if (type == null) { + throw new Error("Unable to find type: " + lookupClassName + " (obf name: " + obfClassName + ")"); + } + TypeDefinition resolvedType = type.resolve(); // decompile it! - TypeDefinition resolvedType = new MetadataSystem(m_settings.getTypeLoader()).lookupType(lookupClassName).resolve(); DecompilerContext context = new DecompilerContext(); context.setCurrentType(resolvedType); context.setSettings(m_settings); -- cgit v1.2.3 From 2107e493239333b3c62802c97209775a1e3f543f Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 21 Feb 2015 18:14:24 -0500 Subject: make types serializable --- src/cuchaz/enigma/mapping/Signature.java | 5 ++++- src/cuchaz/enigma/mapping/Type.java | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/cuchaz/enigma/mapping/Signature.java b/src/cuchaz/enigma/mapping/Signature.java index ff7f8070..273a77b9 100644 --- a/src/cuchaz/enigma/mapping/Signature.java +++ b/src/cuchaz/enigma/mapping/Signature.java @@ -1,12 +1,15 @@ package cuchaz.enigma.mapping; +import java.io.Serializable; import java.util.List; import com.beust.jcommander.internal.Lists; import cuchaz.enigma.Util; -public class Signature { +public class Signature implements Serializable { + + private static final long serialVersionUID = -5843719505729497539L; private List m_argumentTypes; private Type m_returnType; diff --git a/src/cuchaz/enigma/mapping/Type.java b/src/cuchaz/enigma/mapping/Type.java index 9f5d52f0..d8c073e8 100644 --- a/src/cuchaz/enigma/mapping/Type.java +++ b/src/cuchaz/enigma/mapping/Type.java @@ -1,11 +1,14 @@ package cuchaz.enigma.mapping; +import java.io.Serializable; import java.util.Map; import com.google.common.collect.Maps; -public class Type { +public class Type implements Serializable { + private static final long serialVersionUID = 7862257669347104063L; + public enum Primitive { Byte('B'), Character('C'), -- cgit v1.2.3 From 2dc7428e37bdd7a119f53d02ce157675509b0d63 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 23 Feb 2015 23:29:22 -0500 Subject: lots of work in better handling of inner classes also working on recognizing unobfuscated and deobfuscated jars (needed for M3L) --- build.py | 18 ++- src/cuchaz/enigma/CommandMain.java | 43 ++++-- src/cuchaz/enigma/Deobfuscator.java | 4 +- src/cuchaz/enigma/MainFormatConverter.java | 2 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 6 +- src/cuchaz/enigma/analysis/JarIndex.java | 67 ++++----- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 43 +++--- src/cuchaz/enigma/convert/ClassMatcher.java | 6 +- src/cuchaz/enigma/mapping/ClassMapping.java | 41 +++--- src/cuchaz/enigma/mapping/EntryFactory.java | 35 +++-- src/cuchaz/enigma/mapping/Mappings.java | 14 +- src/cuchaz/enigma/mapping/MappingsRenamer.java | 2 +- src/cuchaz/enigma/mapping/MappingsWriter.java | 4 +- src/cuchaz/enigma/mapping/Translator.java | 154 +++++++++++++++------ test/cuchaz/enigma/TestInnerClasses.java | 67 +++++++-- test/cuchaz/enigma/TestJarIndexLoneClass.java | 7 +- test/cuchaz/enigma/TestTranslator.java | 36 +++++ .../enigma/inputs/innerClasses/F_ClassTree.java | 20 +++ .../enigma/inputs/translation/F_ObjectMethods.java | 19 --- .../enigma/inputs/translation/G_ObjectMethods.java | 19 +++ .../enigma/inputs/translation/H_OuterClass.java | 26 ++++ .../enigma/inputs/translation/M_NamelessClass.java | 26 ++++ test/cuchaz/enigma/resources/translation.mappings | 12 ++ 23 files changed, 470 insertions(+), 201 deletions(-) create mode 100644 test/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java delete mode 100644 test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java create mode 100644 test/cuchaz/enigma/inputs/translation/G_ObjectMethods.java create mode 100644 test/cuchaz/enigma/inputs/translation/H_OuterClass.java create mode 100644 test/cuchaz/enigma/inputs/translation/M_NamelessClass.java diff --git a/build.py b/build.py index f5723e05..1f80d504 100644 --- a/build.py +++ b/build.py @@ -18,8 +18,8 @@ import ssjb import ssjb.ivy -ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.7b") -ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.7b") +ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.8b") +ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.8b") # dependencies ExtraRepos = [ @@ -46,7 +46,7 @@ def buildTestJar(name, glob): pathJar = os.path.join(DirBuild, "%s.jar" % name) pathObfJar = os.path.join(DirBuild, "%s.obf.jar" % name) - # build the deobf jar + # build the unobf jar with ssjb.file.TempDir("tmp") as dirTemp: ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin, "cuchaz/enigma/inputs/Keep.class")) ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin, glob)) @@ -58,13 +58,18 @@ def buildTestJar(name, glob): ["@proguard.conf", "-injars", pathJar, "-outjars", pathObfJar] ) +def buildDeobfTestJar(outPath, inPath): + ssjb.callJava( + [DirBin, os.path.join(DirLib, "deps.jar")], + "cuchaz.enigma.CommandMain", + ["deobfuscate", inPath, outPath] + ) def applyReadme(dirTemp): ssjb.file.copy(dirTemp, "license.APL2.txt") ssjb.file.copy(dirTemp, "license.GPL3.txt") ssjb.file.copy(dirTemp, "readme.txt") - def buildStandaloneJar(dirOut): with ssjb.file.TempDir(os.path.join(dirOut, "tmp")) as dirTemp: ssjb.file.copyTree(dirTemp, DirBin, ssjb.file.find(DirBin)) @@ -105,10 +110,11 @@ def taskBuildTestJars(): buildTestJar("testConstructors", "cuchaz/enigma/inputs/constructors/*.class") buildTestJar("testInheritanceTree", "cuchaz/enigma/inputs/inheritanceTree/*.class") buildTestJar("testInnerClasses", "cuchaz/enigma/inputs/innerClasses/*.class") - buildTestJar("testTranslation", "cuchaz/enigma/inputs/translation/*.class") - + taskBuildTranslationTestJar() + def taskBuildTranslationTestJar(): buildTestJar("testTranslation", "cuchaz/enigma/inputs/translation/*.class") + buildDeobfTestJar(os.path.join(DirBuild, "testTranslation.deobf.jar"), os.path.join(DirBuild, "testTranslation.obf.jar")) def taskBuild(): ssjb.file.delete(DirBuild) diff --git a/src/cuchaz/enigma/CommandMain.java b/src/cuchaz/enigma/CommandMain.java index 1ec2ad23..0253a92f 100644 --- a/src/cuchaz/enigma/CommandMain.java +++ b/src/cuchaz/enigma/CommandMain.java @@ -51,7 +51,7 @@ public class CommandMain { try { // process the command - String command = getArg(args, 0, "command"); + String command = getArg(args, 0, "command", true); if (command.equalsIgnoreCase("deobfuscate")) { deobfuscate(args); } else if(command.equalsIgnoreCase("decompile")) { @@ -70,46 +70,55 @@ public class CommandMain { System.out.println("Usage:"); System.out.println("\tjava -cp enigma.jar cuchaz.enigma.CommandMain "); System.out.println("\twhere is one of:"); - System.out.println("\t\tdeobfuscate "); - System.out.println("\t\tdecompile "); + System.out.println("\t\tdeobfuscate []"); + System.out.println("\t\tdecompile []"); } private static void decompile(String[] args) throws Exception { - File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); - File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); - File fileJarOut = getWritableFolder(getArg(args, 3, "out folder")); + File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); + File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)); + File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); } private static void deobfuscate(String[] args) throws Exception { - File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); - File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); - File fileJarOut = getWritableFile(getArg(args, 3, "out jar")); + File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); + File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); + File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); } private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) throws Exception { - System.out.println("Reading mappings..."); - Mappings mappings = new MappingsReader().read(new FileReader(fileMappings)); System.out.println("Reading jar..."); Deobfuscator deobfuscator = new Deobfuscator(jar); - deobfuscator.setMappings(mappings); + if (fileMappings != null) { + System.out.println("Reading mappings..."); + Mappings mappings = new MappingsReader().read(new FileReader(fileMappings)); + deobfuscator.setMappings(mappings); + } return deobfuscator; } - private static String getArg(String[] args, int i, String name) { + private static String getArg(String[] args, int i, String name, boolean required) { if (i >= args.length) { - throw new IllegalArgumentException(name + " is required"); + if (required) { + throw new IllegalArgumentException(name + " is required"); + } else { + return null; + } } return args[i]; } private static File getWritableFile(String path) { + if (path == null) { + return null; + } File file = new File(path).getAbsoluteFile(); File dir = file.getParentFile(); if (dir == null || !dir.exists()) { @@ -119,6 +128,9 @@ public class CommandMain { } private static File getWritableFolder(String path) { + if (path == null) { + return null; + } File dir = new File(path).getAbsoluteFile(); if (!dir.exists()) { throw new IllegalArgumentException("Cannot write to folder: " + dir); @@ -127,6 +139,9 @@ public class CommandMain { } private static File getReadableFile(String path) { + if (path == null) { + return null; + } File file = new File(path).getAbsoluteFile(); if (!file.exists()) { throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index c1954fc7..b7440a72 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -163,9 +163,9 @@ public class Deobfuscator { } // check inner classes - for (ClassMapping innerClassMapping : classMapping.innerClasses()) { + for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { if (!checkClassMapping(relatedMethodChecker, innerClassMapping)) { - System.err.println("WARNING: unable to find inner class " + innerClassMapping + ". dropping mapping."); + System.err.println("WARNING: unable to find inner class " + EntryFactory.getObfClassEntry(m_jarIndex, classMapping) + ". dropping mapping."); classMapping.removeInnerClassMapping(innerClassMapping); } } diff --git a/src/cuchaz/enigma/MainFormatConverter.java b/src/cuchaz/enigma/MainFormatConverter.java index d4bb2db3..5db0e539 100644 --- a/src/cuchaz/enigma/MainFormatConverter.java +++ b/src/cuchaz/enigma/MainFormatConverter.java @@ -111,7 +111,7 @@ public class MainFormatConverter { } private static Object getFieldKey(ClassMapping classMapping, FieldMapping fieldMapping) { - return new ClassEntry(classMapping.getObfName()).getSimpleName() + "." + fieldMapping.getObfName(); + return classMapping.getObfSimpleName() + "." + fieldMapping.getObfName(); } private static String getFieldKey(FieldEntry obfFieldEntry) { diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 12cde4b9..26d5e7a7 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -110,10 +110,10 @@ public class TranslatingTypeLoader implements ITypeLoader { ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(deobfClassEntry); // is this an inner class referenced directly? - String obfOuterClassName = m_jarIndex.getOuterClass(obfClassEntry.getSimpleName()); - if (obfOuterClassName != null) { + ClassEntry obfOuterClassEntry = m_jarIndex.getOuterClass(obfClassEntry); + if (obfOuterClassEntry != null) { // this class doesn't really exist. Reference it by outer$inner instead - System.err.println(String.format("WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassName)); + System.err.println(String.format("WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassEntry)); return null; } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 1c74f158..6e7c69da 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -61,9 +61,9 @@ public class JarIndex { private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; private Multimap> m_fieldReferences; - private Multimap m_innerClasses; - private Map m_outerClasses; - private Map m_anonymousClasses; + private Multimap m_innerClassesByOuter; + private Map m_outerClassesByInner; + private Map m_anonymousClasses; private Map m_bridgedMethods; public JarIndex() { @@ -74,8 +74,8 @@ public class JarIndex { m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); m_fieldReferences = HashMultimap.create(); - m_innerClasses = HashMultimap.create(); - m_outerClasses = Maps.newHashMap(); + m_innerClassesByOuter = HashMultimap.create(); + m_outerClassesByInner = Maps.newHashMap(); m_anonymousClasses = Maps.newHashMap(); m_bridgedMethods = Maps.newHashMap(); } @@ -129,33 +129,40 @@ public class JarIndex { } if (buildInnerClasses) { + // step 5: index inner classes and anonymous classes for (CtClass c : JarClassIterator.classes(jar)) { ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); - String outerClassName = findOuterClass(c); - if (outerClassName != null) { - String innerClassName = c.getSimpleName(); - m_innerClasses.put(outerClassName, innerClassName); - boolean innerWasAdded = m_outerClasses.put(innerClassName, outerClassName) == null; + ClassEntry innerClassEntry = EntryFactory.getClassEntry(c); + ClassEntry outerClassEntry = findOuterClass(c); + if (outerClassEntry != null) { + m_innerClassesByOuter.put(outerClassEntry, innerClassEntry); + boolean innerWasAdded = m_outerClassesByInner.put(innerClassEntry, outerClassEntry) == null; assert (innerWasAdded); - BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassName); + BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry); if (enclosingBehavior != null) { - m_anonymousClasses.put(innerClassName, enclosingBehavior); + m_anonymousClasses.put(innerClassEntry, enclosingBehavior); // DEBUG - // System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); + //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); } else { // DEBUG - // System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); + //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); } } } // step 6: update other indices with inner class info Map renames = Maps.newHashMap(); - for (Map.Entry entry : m_outerClasses.entrySet()) { - renames.put(Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey()); + for (Map.Entry mapEntry : m_innerClassesByOuter.entries()) { + ClassEntry outerClassEntry = mapEntry.getKey(); + ClassEntry innerClassEntry = mapEntry.getValue(); + outerClassEntry = EntryFactory.getChainedOuterClassName(this, outerClassEntry); + String newName = outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName(); + // DEBUG + //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); + renames.put(innerClassEntry.getName(), newName); } EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); m_translationIndex.renameClasses(renames); @@ -290,7 +297,7 @@ public class JarIndex { } } - private String findOuterClass(CtClass c) { + private ClassEntry findOuterClass(CtClass c) { // inner classes: // have constructors that can (illegally) set synthetic fields @@ -341,19 +348,19 @@ public class JarIndex { // do we have an answer yet? if (callerClasses.isEmpty()) { if (illegallySetClasses.size() == 1) { - return illegallySetClasses.iterator().next().getName(); + return illegallySetClasses.iterator().next(); } else { System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); } } else { if (callerClasses.size() == 1) { - return callerClasses.iterator().next().getName(); + return callerClasses.iterator().next(); } else { // multiple callers, do the illegally set classes narrow it down? Set intersection = Sets.newHashSet(callerClasses); intersection.retainAll(illegallySetClasses); if (intersection.size() == 1) { - return intersection.iterator().next().getName(); + return intersection.iterator().next(); } else { System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); } @@ -448,7 +455,7 @@ public class JarIndex { return true; } - private BehaviorEntry isAnonymousClass(CtClass c, String outerClassName) { + private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); @@ -669,23 +676,19 @@ public class JarIndex { return behaviorEntries; } - public Collection getInnerClasses(String obfOuterClassName) { - return m_innerClasses.get(obfOuterClassName); + public Collection getInnerClasses(ClassEntry obfOuterClassEntry) { + return m_innerClassesByOuter.get(obfOuterClassEntry); } - public String getOuterClass(String obfInnerClassName) { - // make sure we use the right name - if (new ClassEntry(obfInnerClassName).getPackageName() != null) { - throw new IllegalArgumentException("Don't reference obfuscated inner classes using packages: " + obfInnerClassName); - } - return m_outerClasses.get(obfInnerClassName); + public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) { + return m_outerClassesByInner.get(obfInnerClassEntry); } - public boolean isAnonymousClass(String obfInnerClassName) { - return m_anonymousClasses.containsKey(obfInnerClassName); + public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) { + return m_anonymousClasses.containsKey(obfInnerClassEntry); } - public BehaviorEntry getAnonymousClassCaller(String obfInnerClassName) { + public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) { return m_anonymousClasses.get(obfInnerClassName); } diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 5350b86c..e82f56ca 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -14,13 +14,12 @@ import java.util.Collection; import javassist.CtClass; import javassist.bytecode.ConstPool; -import javassist.bytecode.Descriptor; import javassist.bytecode.EnclosingMethodAttribute; import javassist.bytecode.InnerClassesAttribute; -import cuchaz.enigma.Constants; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.EntryFactory; public class InnerClassWriter { @@ -32,18 +31,23 @@ public class InnerClassWriter { public void write(CtClass c) { - // is this an inner or outer class? - String obfInnerClassName = new ClassEntry(Descriptor.toJvmName(c.getName())).getSimpleName(); - String obfOuterClassName = m_jarIndex.getOuterClass(obfInnerClassName); - if (obfOuterClassName == null) { - // this is an outer class - obfOuterClassName = Descriptor.toJvmName(c.getName()); + // first, assume this is an inner class + ClassEntry obfInnerClassEntry = EntryFactory.getClassEntry(c); + ClassEntry obfOuterClassEntry = m_jarIndex.getOuterClass(obfInnerClassEntry); + + // see if we're right + if (obfOuterClassEntry == null) { + + // nope, it's an outer class + obfInnerClassEntry = null; + obfOuterClassEntry = EntryFactory.getClassEntry(c); } else { - // this is an inner class, rename it to outer$inner - ClassEntry obfClassEntry = new ClassEntry(obfOuterClassName + "$" + obfInnerClassName); + + // yeah, it's an inner class, rename it to outer$inner + ClassEntry obfClassEntry = new ClassEntry(obfOuterClassEntry.getName() + "$" + obfInnerClassEntry.getSimpleName()); c.setName(obfClassEntry.getName()); - BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller(obfInnerClassName); + BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller(obfInnerClassEntry); if (caller != null) { // write the enclosing method attribute if (caller.getName().equals("")) { @@ -55,18 +59,19 @@ public class InnerClassWriter { } // write the inner classes if needed - Collection obfInnerClassNames = m_jarIndex.getInnerClasses(obfOuterClassName); - if (obfInnerClassNames != null && !obfInnerClassNames.isEmpty()) { - writeInnerClasses(c, obfOuterClassName, obfInnerClassNames); + Collection obfInnerClassEntries = m_jarIndex.getInnerClasses(obfOuterClassEntry); + if (obfInnerClassEntries != null && !obfInnerClassEntries.isEmpty()) { + writeInnerClasses(c, obfOuterClassEntry, obfInnerClassEntries); } } - private void writeInnerClasses(CtClass c, String obfOuterClassName, Collection obfInnerClassNames) { + private void writeInnerClasses(CtClass c, ClassEntry obfOuterClassEntry, Collection obfInnerClassEntries) { InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); c.getClassFile().addAttribute(attr); - for (String obfInnerClassName : obfInnerClassNames) { + for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { + // get the new inner class name - ClassEntry obfClassEntry = new ClassEntry(obfOuterClassName + "$" + obfInnerClassName); + ClassEntry obfClassEntry = new ClassEntry(obfOuterClassEntry.getName() + "$" + obfInnerClassEntry.getSimpleName()); // here's what the JVM spec says about the InnerClasses attribute // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); @@ -77,7 +82,7 @@ public class InnerClassWriter { int outerClassIndex = 0; int innerClassSimpleNameIndex = 0; int accessFlags = 0; - if (!m_jarIndex.isAnonymousClass(obfInnerClassName)) { + if (!m_jarIndex.isAnonymousClass(obfInnerClassEntry)) { outerClassIndex = constPool.addClassInfo(obfClassEntry.getOuterClassName()); innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getInnerClassName()); } @@ -96,7 +101,7 @@ public class InnerClassWriter { */ // make sure the outer class references only the new inner class names - c.replaceClassName(Constants.NonePackage + "/" + obfInnerClassName, obfClassEntry.getName()); + c.replaceClassName(obfInnerClassEntry.getName(), obfClassEntry.getName()); } } } diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index d70b8ebb..224d0048 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -222,7 +222,7 @@ public class ClassMatcher { // check the method matches System.out.println("Checking methods..."); for (ClassMapping classMapping : mappings.classes()) { - ClassEntry classEntry = new ClassEntry(classMapping.getObfName()); + ClassEntry classEntry = new ClassEntry(classMapping.getObfFullName()); for (MethodMapping methodMapping : classMapping.methods()) { // skip constructors @@ -240,13 +240,13 @@ public class ClassMatcher { // show the available methods System.err.println("\tAvailable dest methods:"); - CtClass c = destLoader.loadClass(classMapping.getObfName()); + CtClass c = destLoader.loadClass(classMapping.getObfFullName()); for (CtBehavior behavior : c.getDeclaredBehaviors()) { System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); } System.err.println("\tAvailable source methods:"); - c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfName())); + c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfFullName())); for (CtBehavior behavior : c.getDeclaredBehaviors()) { System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); } diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 885400b4..3610e33b 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -20,7 +20,8 @@ public class ClassMapping implements Serializable, Comparable { private static final long serialVersionUID = -5148491146902340107L; - private String m_obfName; + private String m_obfFullName; + private String m_obfSimpleName; private String m_deobfName; private Map m_innerClassesByObf; private Map m_innerClassesByDeobf; @@ -34,7 +35,8 @@ public class ClassMapping implements Serializable, Comparable { } public ClassMapping(String obfName, String deobfName) { - m_obfName = obfName; + m_obfFullName = obfName; + m_obfSimpleName = new ClassEntry(obfName).getSimpleName(); m_deobfName = NameValidator.validateClassName(deobfName, false); m_innerClassesByObf = Maps.newHashMap(); m_innerClassesByDeobf = Maps.newHashMap(); @@ -44,8 +46,12 @@ public class ClassMapping implements Serializable, Comparable { m_methodsByDeobf = Maps.newHashMap(); } - public String getObfName() { - return m_obfName; + public String getObfFullName() { + return m_obfFullName; + } + + public String getObfSimpleName() { + return m_obfSimpleName; } public String getDeobfName() { @@ -64,8 +70,7 @@ public class ClassMapping implements Serializable, Comparable { } public void addInnerClassMapping(ClassMapping classMapping) { - assert (isSimpleClassName(classMapping.getObfName())); - boolean obfWasAdded = m_innerClassesByObf.put(classMapping.getObfName(), classMapping) == null; + boolean obfWasAdded = m_innerClassesByObf.put(classMapping.getObfSimpleName(), classMapping) == null; assert (obfWasAdded); if (classMapping.getDeobfName() != null) { assert (isSimpleClassName(classMapping.getDeobfName())); @@ -75,7 +80,7 @@ public class ClassMapping implements Serializable, Comparable { } public void removeInnerClassMapping(ClassMapping classMapping) { - boolean obfWasRemoved = m_innerClassesByObf.remove(classMapping.getObfName()) != null; + boolean obfWasRemoved = m_innerClassesByObf.remove(classMapping.getObfSimpleName()) != null; assert (obfWasRemoved); if (classMapping.getDeobfName() != null) { boolean deobfWasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; @@ -112,11 +117,11 @@ public class ClassMapping implements Serializable, Comparable { return classMapping; } - public String getObfInnerClassName(String deobfName) { + public String getObfInnerClassSimpleName(String deobfName) { assert (isSimpleClassName(deobfName)); ClassMapping classMapping = m_innerClassesByDeobf.get(deobfName); if (classMapping != null) { - return classMapping.getObfName(); + return classMapping.getObfSimpleName(); } return null; } @@ -163,7 +168,7 @@ public class ClassMapping implements Serializable, Comparable { public void addFieldMapping(FieldMapping fieldMapping) { String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); if (m_fieldsByObf.containsKey(obfKey)) { - throw new Error("Already have mapping for " + m_obfName + "." + obfKey); + throw new Error("Already have mapping for " + m_obfFullName + "." + obfKey); } String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); if (m_fieldsByDeobf.containsKey(deobfKey)) { @@ -257,7 +262,7 @@ public class ClassMapping implements Serializable, Comparable { public void addMethodMapping(MethodMapping methodMapping) { String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); if (m_methodsByObf.containsKey(obfKey)) { - throw new Error("Already have mapping for " + m_obfName + "." + obfKey); + throw new Error("Already have mapping for " + m_obfFullName + "." + obfKey); } boolean wasAdded = m_methodsByObf.put(obfKey, methodMapping) == null; assert (wasAdded); @@ -339,7 +344,7 @@ public class ClassMapping implements Serializable, Comparable { @Override public String toString() { StringBuilder buf = new StringBuilder(); - buf.append(m_obfName); + buf.append(m_obfFullName); buf.append(" <-> "); buf.append(m_deobfName); buf.append("\n"); @@ -359,7 +364,7 @@ public class ClassMapping implements Serializable, Comparable { buf.append("Inner Classes:\n"); for (ClassMapping classMapping : m_innerClassesByObf.values()) { buf.append("\t"); - buf.append(classMapping.getObfName()); + buf.append(classMapping.getObfSimpleName()); buf.append(" <-> "); buf.append(classMapping.getDeobfName()); buf.append("\n"); @@ -370,10 +375,10 @@ public class ClassMapping implements Serializable, Comparable { @Override public int compareTo(ClassMapping other) { // sort by a, b, c, ... aa, ab, etc - if (m_obfName.length() != other.m_obfName.length()) { - return m_obfName.length() - other.m_obfName.length(); + if (m_obfFullName.length() != other.m_obfFullName.length()) { + return m_obfFullName.length() - other.m_obfFullName.length(); } - return m_obfName.compareTo(other.m_obfName); + return m_obfFullName.compareTo(other.m_obfFullName); } public boolean renameObfClass(String oldObfClassName, String newObfClassName) { @@ -399,9 +404,9 @@ public class ClassMapping implements Serializable, Comparable { } } - if (m_obfName.equals(oldObfClassName)) { + if (m_obfFullName.equals(oldObfClassName)) { // rename this class - m_obfName = newObfClassName; + m_obfFullName = newObfClassName; return true; } return false; diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index dceea29d..bbdfa739 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java @@ -25,25 +25,19 @@ public class EntryFactory { } public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { - return new ClassEntry(getChainedOuterClassName(jarIndex, classMapping.getObfName())); + return getChainedOuterClassName(jarIndex, new ClassEntry(classMapping.getObfFullName())); } - private static String getChainedOuterClassName(JarIndex jarIndex, String obfClassName) { + public static ClassEntry getChainedOuterClassName(JarIndex jarIndex, ClassEntry obfClassEntry) { // lookup the chain of outer classes - List obfOuterClassNames = Lists.newArrayList(); - String checkName = obfClassName; + List obfClassChain = Lists.newArrayList(obfClassEntry); + ClassEntry checkClassEntry = obfClassEntry; while (true) { - - // if this class name has a package, then it can't be an inner class - if (!new ClassEntry(checkName).isInDefaultPackage()) { - break; - } - - String obfOuterClassName = jarIndex.getOuterClass(checkName); - if (obfOuterClassName != null) { - obfOuterClassNames.add(obfOuterClassName); - checkName = obfOuterClassName; + ClassEntry obfOuterClassEntry = jarIndex.getOuterClass(checkClassEntry); + if (obfOuterClassEntry != null) { + obfClassChain.add(obfOuterClassEntry); + checkClassEntry = obfOuterClassEntry; } else { break; } @@ -51,12 +45,15 @@ public class EntryFactory { // build the chained class name StringBuilder buf = new StringBuilder(); - for (int i=obfOuterClassNames.size()-1; i>=0; i--) { - buf.append(obfOuterClassNames.get(i)); - buf.append("$"); + for (int i=obfClassChain.size()-1; i>=0; i--) { + if (buf.length() == 0) { + buf.append(obfClassChain.get(i).getName()); + } else { + buf.append("$"); + buf.append(obfClassChain.get(i).getSimpleName()); + } } - buf.append(obfClassName); - return buf.toString(); + return new ClassEntry(buf.toString()); } public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 675fdf1f..a85bcbf6 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -37,7 +37,7 @@ public class Mappings implements Serializable { this(); for (ClassMapping classMapping : classes) { - m_classesByObf.put(classMapping.getObfName(), classMapping); + m_classesByObf.put(classMapping.getObfFullName(), classMapping); if (classMapping.getDeobfName() != null) { m_classesByDeobf.put(classMapping.getDeobfName(), classMapping); } @@ -50,10 +50,10 @@ public class Mappings implements Serializable { } public void addClassMapping(ClassMapping classMapping) { - if (m_classesByObf.containsKey(classMapping.getObfName())) { - throw new Error("Already have mapping for " + classMapping.getObfName()); + if (m_classesByObf.containsKey(classMapping.getObfFullName())) { + throw new Error("Already have mapping for " + classMapping.getObfFullName()); } - boolean obfWasAdded = m_classesByObf.put(classMapping.getObfName(), classMapping) == null; + boolean obfWasAdded = m_classesByObf.put(classMapping.getObfFullName(), classMapping) == null; assert (obfWasAdded); if (classMapping.getDeobfName() != null) { if (m_classesByDeobf.containsKey(classMapping.getDeobfName())) { @@ -65,7 +65,7 @@ public class Mappings implements Serializable { } public void removeClassMapping(ClassMapping classMapping) { - boolean obfWasRemoved = m_classesByObf.remove(classMapping.getObfName()) != null; + boolean obfWasRemoved = m_classesByObf.remove(classMapping.getObfFullName()) != null; assert (obfWasRemoved); if (classMapping.getDeobfName() != null) { boolean deobfWasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null; @@ -103,7 +103,7 @@ public class Mappings implements Serializable { if (classMapping.getDeobfName() != null) { classes.put(classMapping.getDeobfName(), classMapping); } else { - classes.put(classMapping.getObfName(), classMapping); + classes.put(classMapping.getObfFullName(), classMapping); } } @@ -144,7 +144,7 @@ public class Mappings implements Serializable { for (ClassMapping classMapping : classes()) { // add the class name - classNames.add(classMapping.getObfName()); + classNames.add(classMapping.getObfFullName()); // add classes from method signatures for (MethodMapping methodMapping : classMapping.methods()) { diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index ea343c4e..16f700d2 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -213,7 +213,7 @@ public class MappingsRenamer { ClassMapping classMapping = m_mappings.m_classesByObf.get(obfClassName); if (classMapping == null) { classMapping = new ClassMapping(obfClassName); - boolean obfWasAdded = m_mappings.m_classesByObf.put(classMapping.getObfName(), classMapping) == null; + boolean obfWasAdded = m_mappings.m_classesByObf.put(classMapping.getObfFullName(), classMapping) == null; assert (obfWasAdded); } return classMapping; diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java index c7c2cc00..8b62db8c 100644 --- a/src/cuchaz/enigma/mapping/MappingsWriter.java +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -31,9 +31,9 @@ public class MappingsWriter { private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { if (classMapping.getDeobfName() == null) { - out.format("%sCLASS %s\n", getIndent(depth), classMapping.getObfName()); + out.format("%sCLASS %s\n", getIndent(depth), classMapping.getObfFullName()); } else { - out.format("%sCLASS %s %s\n", getIndent(depth), classMapping.getObfName(), classMapping.getDeobfName()); + out.format("%sCLASS %s %s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName()); } for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 759dddf9..d9850324 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -10,8 +10,10 @@ ******************************************************************************/ package cuchaz.enigma.mapping; +import java.util.List; import java.util.Map; +import com.beust.jcommander.internal.Lists; import com.google.common.collect.Maps; import cuchaz.enigma.analysis.TranslationIndex; @@ -50,54 +52,106 @@ public class Translator { } } + public String translate(T entry) { + if (entry instanceof ClassEntry) { + return translate((ClassEntry)entry); + } else if (entry instanceof FieldEntry) { + return translate((FieldEntry)entry); + } else if (entry instanceof MethodEntry) { + return translate((MethodEntry)entry); + } else if (entry instanceof ConstructorEntry) { + return translate((ConstructorEntry)entry); + } else if (entry instanceof ArgumentEntry) { + return translate((ArgumentEntry)entry); + } else { + throw new Error("Unknown entry type: " + entry.getClass().getName()); + } + } + public String translateClass(String className) { return translate(new ClassEntry(className)); } public String translate(ClassEntry in) { - ClassMapping classMapping = m_classes.get(in.getOuterClassName()); - if (classMapping != null) { - if (in.isInnerClass()) { - // translate the inner class - String translatedInnerClassName = m_direction.choose( - classMapping.getDeobfInnerClassName(in.getInnerClassName()), - classMapping.getObfInnerClassName(in.getInnerClassName()) + + if (in.isInnerClass()) { + + // translate everything in the class chain, or return null + List mappingsChain = getClassMappingChain(in); + StringBuilder buf = new StringBuilder(); + for (ClassMapping classMapping : mappingsChain) { + if (classMapping == null) { + return null; + } + boolean isFirstClass = buf.length() == 0; + String name = m_direction.choose( + classMapping.getDeobfName(), + isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() ); - if (translatedInnerClassName != null) { - // try to translate the outer name - String translatedOuterClassName = m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName()); - if (translatedOuterClassName != null) { - return translatedOuterClassName + "$" + translatedInnerClassName; - } else { - return in.getOuterClassName() + "$" + translatedInnerClassName; - } + if (name == null) { + return null; + } + if (!isFirstClass) { + buf.append("$"); } - } else { - // just return outer - return m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName()); + buf.append(name); } + return buf.toString(); + + } else { + + // normal classes are easier + ClassMapping classMapping = m_classes.get(in.getName()); + if (classMapping == null) { + return null; + } + return m_direction.choose( + classMapping.getDeobfName(), + classMapping.getObfFullName() + ); } - return null; } public ClassEntry translateEntry(ClassEntry in) { - // can we translate the inner class? - String name = translate(in); - if (name != null) { - return new ClassEntry(name); - } - if (in.isInnerClass()) { - // guess not. just translate the outer class name then - String outerClassName = translate(in.getOuterClassEntry()); - if (outerClassName != null) { - return new ClassEntry(outerClassName + "$" + in.getInnerClassName()); + // translate as much of the class chain as we can + List mappingsChain = getClassMappingChain(in); + String[] obfClassNames = in.getName().split("\\$"); + StringBuilder buf = new StringBuilder(); + for (int i=0; i mappingChain = getClassMappingChain(in); + return mappingChain.get(mappingChain.size() - 1); + } + + private List getClassMappingChain(ClassEntry in) { + + // get a list of all the classes in the hierarchy + String[] parts = in.getName().split("\\$"); + List mappingsChain = Lists.newArrayList(); + + // get mappings for the outer class + ClassMapping outerClassMapping = m_classes.get(parts[0]); + mappingsChain.add(outerClassMapping); + + for (int i=1; i obfClassChain = Lists.newArrayList(); + ClassEntry checkClassEntry = obfClassEntry; + while (checkClassEntry != null) { + obfClassChain.add(checkClassEntry); + checkClassEntry = m_index.getOuterClass(checkClassEntry); + } - // see if we're right - if (obfOuterClassEntry == null) { - - // nope, it's an outer class - obfInnerClassEntry = null; - obfOuterClassEntry = EntryFactory.getClassEntry(c); - } else { + // change order: outer to inner + Collections.reverse(obfClassChain); + + // does this class have any inner classes? + Collection obfInnerClassEntries = m_index.getInnerClasses(obfClassEntry); + + boolean isInnerClass = obfClassChain.size() > 1; + if (isInnerClass) { - // yeah, it's an inner class, rename it to outer$inner - ClassEntry obfClassEntry = new ClassEntry(obfOuterClassEntry.getName() + "$" + obfInnerClassEntry.getSimpleName()); - c.setName(obfClassEntry.getName()); + // it's an inner class, rename it to the fully qualified name + StringBuilder buf = new StringBuilder(); + for (ClassEntry obfChainClassEntry : obfClassChain) { + if (buf.length() == 0) { + buf.append(obfChainClassEntry.getName()); + } else { + buf.append("$"); + buf.append(obfChainClassEntry.getSimpleName()); + } + } + c.setName(buf.toString()); - BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller(obfInnerClassEntry); + BehaviorEntry caller = m_index.getAnonymousClassCaller(obfClassEntry); if (caller != null) { + // write the enclosing method attribute if (caller.getName().equals("")) { c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); @@ -58,50 +78,89 @@ public class InnerClassWriter { } } - // write the inner classes if needed - Collection obfInnerClassEntries = m_jarIndex.getInnerClasses(obfOuterClassEntry); - if (obfInnerClassEntries != null && !obfInnerClassEntries.isEmpty()) { - writeInnerClasses(c, obfOuterClassEntry, obfInnerClassEntries); - } - } - - private void writeInnerClasses(CtClass c, ClassEntry obfOuterClassEntry, Collection obfInnerClassEntries) { - InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); - c.getClassFile().addAttribute(attr); - for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { + if (isInnerClass || !obfInnerClassEntries.isEmpty()) { - // get the new inner class name - ClassEntry obfClassEntry = new ClassEntry(obfOuterClassEntry.getName() + "$" + obfInnerClassEntry.getSimpleName()); + // create an inner class attribute + InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); + c.getClassFile().addAttribute(attr); - // here's what the JVM spec says about the InnerClasses attribute - // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); - - // update the attribute with this inner class - ConstPool constPool = c.getClassFile().getConstPool(); - int innerClassIndex = constPool.addClassInfo(obfClassEntry.getName()); - int outerClassIndex = 0; - int innerClassSimpleNameIndex = 0; - int accessFlags = 0; - if (!m_jarIndex.isAnonymousClass(obfInnerClassEntry)) { - outerClassIndex = constPool.addClassInfo(obfClassEntry.getOuterClassName()); - innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getInnerClassName()); + // write the ancestry, but not the outermost class + for (int i=1; i ATTR: %s,%s,%s (replace %s with %s)", - obfClassEntry, - attr.innerClass(attr.tableLength() - 1), - attr.outerClass(attr.tableLength() - 1), - attr.innerName(attr.tableLength() - 1), - Constants.NonePackage + "/" + obfInnerClassName, - obfClassEntry.getName() - )); - */ - - // make sure the outer class references only the new inner class names - c.replaceClassName(obfInnerClassEntry.getName(), obfClassEntry.getName()); + // write the inner classes + for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { + + // extend the class chain + List extendedObfClassChain = Lists.newArrayList(obfClassChain); + extendedObfClassChain.add(obfInnerClassEntry); + + String fullyQualifiedInnerClassName = writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); + + // make sure we only reference the fully qualified inner class name + c.replaceClassName(obfInnerClassEntry.getName(), fullyQualifiedInnerClassName); + } + } + } + + private String writeInnerClass(InnerClassesAttribute attr, List obfClassChain, ClassEntry obfClassEntry) { + + // get the new inner class name + String obfInnerClassName = getFullyQualifiedName(obfClassChain, obfClassEntry); + String obfParentClassName = getFullyQualifiedParentName(obfClassChain, obfClassEntry); + + // here's what the JVM spec says about the InnerClasses attribute + // append(inner, parent, 0 if anonymous else simple name, flags); + + // update the attribute with this inner class + ConstPool constPool = attr.getConstPool(); + int innerClassIndex = constPool.addClassInfo(obfInnerClassName); + int parentClassIndex = constPool.addClassInfo(obfParentClassName); + int innerClassSimpleNameIndex = 0; + int accessFlags = 0; + if (!m_index.isAnonymousClass(obfClassEntry)) { + innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getSimpleName()); + } + + attr.append(innerClassIndex, parentClassIndex, innerClassSimpleNameIndex, accessFlags); + + /* DEBUG + System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", + obfClassEntry, + attr.innerClass(attr.tableLength() - 1), + attr.outerClass(attr.tableLength() - 1), + attr.innerName(attr.tableLength() - 1), + Constants.NonePackage + "/" + obfInnerClassName, + obfClassEntry.getName() + )); + */ + + return obfInnerClassName; + } + + private String getFullyQualifiedParentName(List classChain, ClassEntry classEntry) { + assert(classChain.size() > 1); + assert(classChain.contains(classEntry)); + StringBuilder buf = new StringBuilder(); + for (int i=0; classChain.get(i) != classEntry; i++) { + ClassEntry chainEntry = classChain.get(i); + if (buf.length() == 0) { + buf.append(chainEntry.getName()); + } else { + buf.append("$"); + buf.append(chainEntry.getSimpleName()); + } + } + return buf.toString(); + } + + private String getFullyQualifiedName(List classChain, ClassEntry classEntry) { + boolean isInner = classChain.size() > 1; + if (isInner) { + return getFullyQualifiedParentName(classChain, classEntry) + "$" + classEntry.getSimpleName(); + } else { + return classEntry.getName(); } } } diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index cf410012..69f171a5 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -114,6 +114,9 @@ public class ClassEntry implements Entry, Serializable { } public String getSimpleName() { + if (isInnerClass()) { + return getInnerClassName(); + } int pos = m_name.lastIndexOf('/'); if (pos > 0) { return m_name.substring(pos + 1); diff --git a/test/cuchaz/enigma/TestJarIndexDeobfed.java b/test/cuchaz/enigma/TestJarIndexDeobfed.java new file mode 100644 index 00000000..f776e4f6 --- /dev/null +++ b/test/cuchaz/enigma/TestJarIndexDeobfed.java @@ -0,0 +1,52 @@ +package cuchaz.enigma; + + +import static cuchaz.enigma.TestEntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.util.jar.JarFile; + +import org.junit.BeforeClass; +import org.junit.Test; + +import cuchaz.enigma.analysis.JarIndex; + + +public class TestJarIndexDeobfed { + + private static JarIndex m_index; + + @BeforeClass + public static void beforeClass() + throws Exception { + m_index = new JarIndex(); + m_index.indexJar(new JarFile("build/testTranslation.deobf.jar"), true); + } + + @Test + public void obfEntries() { + assertThat(m_index.getObfClassEntries(), containsInAnyOrder( + newClass("cuchaz/enigma/inputs/Keep"), + newClass("none/a"), + newClass("none/b"), + newClass("none/c"), + newClass("none/d"), + newClass("none/d$e"), + newClass("none/f"), + newClass("none/g"), + newClass("none/h"), + newClass("none/h$i"), + newClass("none/h$i$j"), + newClass("none/h$k"), + newClass("none/h$k$l"), + newClass("none/m"), + newClass("none/m$n"), + newClass("none/m$n$o"), + newClass("none/m$p"), + newClass("none/m$p$q"), + newClass("none/m$p$q$r"), + newClass("none/m$p$q$s") + )); + } +} diff --git a/test/cuchaz/enigma/inputs/translation/M_NamelessClass.java b/test/cuchaz/enigma/inputs/translation/M_NamelessClass.java index afc9a9a5..5d8acbc7 100644 --- a/test/cuchaz/enigma/inputs/translation/M_NamelessClass.java +++ b/test/cuchaz/enigma/inputs/translation/M_NamelessClass.java @@ -3,14 +3,14 @@ package cuchaz.enigma.inputs.translation; public class M_NamelessClass { - public class I_InnerClass { + public class N_InnerClass { public int f1; public String f2; public void m1() {} - public class J_InnerInnerClass { + public class O_InnerInnerClass { public int f3; @@ -18,9 +18,11 @@ public class M_NamelessClass { } } - public class K_NamelessClass { - public class L_NamedInnerClass { + public class P_NamelessClass { + public class Q_NamedInnerClass { public int f4; + public class R_AnotherInnerClass {} + public class S_YetAnotherInnerClass {} } } } -- cgit v1.2.3 From 9809078524bd3bd40fbf7aa411f6e0dca02fd009 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 25 Feb 2015 22:42:34 -0500 Subject: fixed lots of issues with inner class reconstruction, particularly for inner class trees also fixed lots of issues with reading jars that aren't Minecraft. =P --- readme.txt | 7 +- src/cuchaz/enigma/Deobfuscator.java | 33 ++++--- src/cuchaz/enigma/TranslatingTypeLoader.java | 113 +++++++++++++++-------- src/cuchaz/enigma/analysis/JarIndex.java | 29 +++++- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 84 +++++------------ src/cuchaz/enigma/mapping/ClassEntry.java | 22 ++++- src/cuchaz/enigma/mapping/EntryFactory.java | 34 +------ test/cuchaz/enigma/TestDeobfed.java | 79 ++++++++++++++++ test/cuchaz/enigma/TestJarIndexDeobfed.java | 52 ----------- 9 files changed, 241 insertions(+), 212 deletions(-) create mode 100644 test/cuchaz/enigma/TestDeobfed.java delete mode 100644 test/cuchaz/enigma/TestJarIndexDeobfed.java diff --git a/readme.txt b/readme.txt index db294cd9..68d13303 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ -Enigma v0.7 beta +Enigma v0.8 beta A tool for deobfuscation of Java bytecode Copyright Jeff Martin, 2015 @@ -26,3 +26,8 @@ Launch the GUI: Use Enigma on the command line: java -cp enigma.jar cuchaz.enigma.CommandMain + + +OPEN SOURCE + +https://bitbucket.org/cuchaz/enigma diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index b7440a72..0b7808da 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -204,33 +204,36 @@ public class Deobfuscator { } } - public CompilationUnit getSourceTree(String obfClassName) { - // is this class deobfuscated? + public CompilationUnit getSourceTree(String className) { + + // we don't know if this class name is obfuscated or deobfuscated // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out - // the decompiler only sees the deobfuscated class, so we need to load it by the deobfuscated name - String lookupClassName = obfClassName; - ClassMapping classMapping = m_mappings.getClassByObf(obfClassName); - if (classMapping != null && classMapping.getDeobfName() != null) { - lookupClassName = classMapping.getDeobfName(); - } + // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one - // is this class even in the jar? - if (!m_jarIndex.containsObfClass(new ClassEntry(obfClassName))) { - return null; + // first, assume class name is deobf + String deobfClassName = className; + + // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name + ClassMapping classMapping = m_mappings.getClassByObf(className); + if (classMapping != null && classMapping.getDeobfName() != null) { + deobfClassName = classMapping.getDeobfName(); } // set the type loader - m_settings.setTypeLoader(new TranslatingTypeLoader( + TranslatingTypeLoader loader = new TranslatingTypeLoader( m_jar, m_jarIndex, getTranslator(TranslationDirection.Obfuscating), getTranslator(TranslationDirection.Deobfuscating) - )); + ); + m_settings.setTypeLoader(loader); // see if procyon can find the type - TypeReference type = new MetadataSystem(m_settings.getTypeLoader()).lookupType(lookupClassName); + TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName); if (type == null) { - throw new Error("Unable to find type: " + lookupClassName + " (obf name: " + obfClassName + ")"); + throw new Error(String.format("Unable to find type: %s (deobf: %s)\nTried class names: %s", + className, deobfClassName, loader.getClassNamesToTry(deobfClassName) + )); } TypeDefinition resolvedType = type.resolve(); diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 26d5e7a7..94ad6ebe 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -13,6 +13,7 @@ package cuchaz.enigma; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.List; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -24,6 +25,7 @@ import javassist.CtClass; import javassist.NotFoundException; import javassist.bytecode.Descriptor; +import com.beust.jcommander.internal.Lists; import com.google.common.collect.Maps; import com.strobel.assembler.metadata.Buffer; import com.strobel.assembler.metadata.ClasspathTypeLoader; @@ -65,19 +67,20 @@ public class TranslatingTypeLoader implements ITypeLoader { } @Override - public boolean tryLoadType(String deobfClassName, Buffer out) { + public boolean tryLoadType(String className, Buffer out) { + // check the cache byte[] data; - if (m_cache.containsKey(deobfClassName)) { - data = m_cache.get(deobfClassName); + if (m_cache.containsKey(className)) { + data = m_cache.get(className); } else { - data = loadType(deobfClassName); - m_cache.put(deobfClassName, data); + data = loadType(className); + m_cache.put(className, data); } if (data == null) { // chain to default type loader - return m_defaultTypeLoader.tryLoadType(deobfClassName, out); + return m_defaultTypeLoader.tryLoadType(className, out); } // send the class to the decompiler @@ -88,6 +91,7 @@ public class TranslatingTypeLoader implements ITypeLoader { } public CtClass loadClass(String deobfClassName) { + byte[] data = loadType(deobfClassName); if (data == null) { return null; @@ -104,40 +108,35 @@ public class TranslatingTypeLoader implements ITypeLoader { } } - private byte[] loadType(String deobfClassName) { - - ClassEntry deobfClassEntry = new ClassEntry(deobfClassName); - ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(deobfClassEntry); - - // is this an inner class referenced directly? - ClassEntry obfOuterClassEntry = m_jarIndex.getOuterClass(obfClassEntry); - if (obfOuterClassEntry != null) { - // this class doesn't really exist. Reference it by outer$inner instead - System.err.println(String.format("WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassEntry)); - return null; - } - - /* DEBUG - if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) { - System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); + private byte[] loadType(String className) { + + // NOTE: don't know if class name is obf or deobf + ClassEntry classEntry = new ClassEntry(className); + ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(classEntry); + + // is this an inner class referenced directly? (ie trying to load b instead of a$b) + if (!obfClassEntry.isInnerClass()) { + List classChain = m_jarIndex.getObfClassChain(obfClassEntry); + if (classChain.size() > 1) { + System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s", + className, obfClassEntry.buildClassEntry(classChain) + )); + return null; + } } - */ - // get the jar entry - String classFileName; - if (obfClassEntry.isInnerClass()) { - // use just the inner class name for inner classes - classFileName = obfClassEntry.getInnerClassName(); - } else if (obfClassEntry.getPackageName().equals(Constants.NonePackage)) { - // use the outer class simple name for classes in the none package - classFileName = obfClassEntry.getSimpleName(); - } else { - // otherwise, just use the class name (ie for classes in packages) - classFileName = obfClassEntry.getName(); + // is this a class we should even know about? + if (!m_jarIndex.containsObfClass(obfClassEntry)) { + return null; } - JarEntry entry = m_jar.getJarEntry(classFileName + ".class"); - if (entry == null) { + // DEBUG + //System.out.println(String.format("Looking for %s (obf: %s)", classEntry.getName(), obfClassEntry.getName())); + + // find the class in the jar + String classInJarName = findClassInJar(obfClassEntry); + if (classInJarName == null) { + // couldn't find it return null; } @@ -145,7 +144,7 @@ public class TranslatingTypeLoader implements ITypeLoader { // read the class file into a buffer ByteArrayOutputStream data = new ByteArrayOutputStream(); byte[] buf = new byte[1024 * 1024]; // 1 KiB - InputStream in = m_jar.getInputStream(entry); + InputStream in = m_jar.getInputStream(m_jar.getJarEntry(classInJarName + ".class")); while (true) { int bytesRead = in.read(buf); if (bytesRead <= 0) { @@ -158,15 +157,15 @@ public class TranslatingTypeLoader implements ITypeLoader { buf = data.toByteArray(); // load the javassist handle to the raw class - String javaClassFileName = Descriptor.toJavaName(classFileName); ClassPool classPool = new ClassPool(); - classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, buf)); - CtClass c = classPool.get(javaClassFileName); + String classInJarJavaName = Descriptor.toJavaName(classInJarName); + classPool.insertClassPath(new ByteArrayClassPath(classInJarJavaName, buf)); + CtClass c = classPool.get(classInJarJavaName); c = transformClass(c); // sanity checking - assertClassName(c, deobfClassEntry); + assertClassName(c, classEntry); // DEBUG //Util.writeClass( c ); @@ -178,6 +177,38 @@ public class TranslatingTypeLoader implements ITypeLoader { } } + private String findClassInJar(ClassEntry obfClassEntry) { + + // try to find the class in the jar + for (String className : getClassNamesToTry(obfClassEntry)) { + JarEntry jarEntry = m_jar.getJarEntry(className + ".class"); + if (jarEntry != null) { + return className; + } + } + + // didn't find it ;_; + return null; + } + + public List getClassNamesToTry(String className) { + return getClassNamesToTry(m_obfuscatingTranslator.translateEntry(new ClassEntry(className))); + } + + public List getClassNamesToTry(ClassEntry obfClassEntry) { + List classNamesToTry = Lists.newArrayList(); + classNamesToTry.add(obfClassEntry.getName()); + if (obfClassEntry.getPackageName().equals(Constants.NonePackage)) { + // taking off the none package, if any + classNamesToTry.add(obfClassEntry.getSimpleName()); + } + if (obfClassEntry.isInnerClass()) { + // try just the inner class name + classNamesToTry.add(obfClassEntry.getInnerClassName()); + } + return classNamesToTry; + } + public CtClass transformClass(CtClass c) throws IOException, NotFoundException, CannotCompileException { diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 1afcb76c..e0a8bf52 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -12,6 +12,7 @@ package cuchaz.enigma.analysis; import java.lang.reflect.Modifier; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -156,11 +157,8 @@ public class JarIndex { // step 6: update other indices with inner class info Map renames = Maps.newHashMap(); - for (Map.Entry mapEntry : m_innerClassesByOuter.entries()) { - ClassEntry outerClassEntry = mapEntry.getKey(); - ClassEntry innerClassEntry = mapEntry.getValue(); - outerClassEntry = EntryFactory.getChainedOuterClassName(this, outerClassEntry); - String newName = outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName(); + for (ClassEntry innerClassEntry : m_innerClassesByOuter.values()) { + String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName(); if (!innerClassEntry.getName().equals(newName)) { // DEBUG //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); @@ -780,4 +778,25 @@ public class JarIndex { public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) { return m_bridgedMethods.get(bridgeMethodEntry); } + + public List getObfClassChain(ClassEntry obfClassEntry) { + + // build class chain in inner-to-outer order + List obfClassChain = Lists.newArrayList(obfClassEntry); + ClassEntry checkClassEntry = obfClassEntry; + while (true) { + ClassEntry obfOuterClassEntry = getOuterClass(checkClassEntry); + if (obfOuterClassEntry != null) { + obfClassChain.add(obfOuterClassEntry); + checkClassEntry = obfOuterClassEntry; + } else { + break; + } + } + + // switch to outer-to-inner order + Collections.reverse(obfClassChain); + + return obfClassChain; + } } diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 3bebd176..dd21a780 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -11,7 +11,6 @@ package cuchaz.enigma.bytecode; import java.util.Collection; -import java.util.Collections; import java.util.List; import javassist.CtClass; @@ -36,35 +35,14 @@ public class InnerClassWriter { public void write(CtClass c) { - // build the class chain (inner to outer) ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); - List obfClassChain = Lists.newArrayList(); - ClassEntry checkClassEntry = obfClassEntry; - while (checkClassEntry != null) { - obfClassChain.add(checkClassEntry); - checkClassEntry = m_index.getOuterClass(checkClassEntry); - } - - // change order: outer to inner - Collections.reverse(obfClassChain); - - // does this class have any inner classes? - Collection obfInnerClassEntries = m_index.getInnerClasses(obfClassEntry); + List obfClassChain = m_index.getObfClassChain(obfClassEntry); boolean isInnerClass = obfClassChain.size() > 1; if (isInnerClass) { // it's an inner class, rename it to the fully qualified name - StringBuilder buf = new StringBuilder(); - for (ClassEntry obfChainClassEntry : obfClassChain) { - if (buf.length() == 0) { - buf.append(obfChainClassEntry.getName()); - } else { - buf.append("$"); - buf.append(obfChainClassEntry.getSimpleName()); - } - } - c.setName(buf.toString()); + c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName()); BehaviorEntry caller = m_index.getAnonymousClassCaller(obfClassEntry); if (caller != null) { @@ -78,6 +56,9 @@ public class InnerClassWriter { } } + // does this class have any inner classes? + Collection obfInnerClassEntries = m_index.getInnerClasses(obfClassEntry); + if (isInnerClass || !obfInnerClassEntries.isEmpty()) { // create an inner class attribute @@ -86,7 +67,11 @@ public class InnerClassWriter { // write the ancestry, but not the outermost class for (int i=1; i extendedObfClassChain = Lists.newArrayList(obfClassChain); extendedObfClassChain.add(obfInnerClassEntry); - String fullyQualifiedInnerClassName = writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); + writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); - // make sure we only reference the fully qualified inner class name - c.replaceClassName(obfInnerClassEntry.getName(), fullyQualifiedInnerClassName); + // update references to use the fully qualified inner class name + c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName()); } } } - private String writeInnerClass(InnerClassesAttribute attr, List obfClassChain, ClassEntry obfClassEntry) { + private void writeInnerClass(InnerClassesAttribute attr, List obfClassChain, ClassEntry obfClassEntry) { // get the new inner class name - String obfInnerClassName = getFullyQualifiedName(obfClassChain, obfClassEntry); - String obfParentClassName = getFullyQualifiedParentName(obfClassChain, obfClassEntry); + ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); + ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); // here's what the JVM spec says about the InnerClasses attribute // append(inner, parent, 0 if anonymous else simple name, flags); // update the attribute with this inner class ConstPool constPool = attr.getConstPool(); - int innerClassIndex = constPool.addClassInfo(obfInnerClassName); - int parentClassIndex = constPool.addClassInfo(obfParentClassName); - int innerClassSimpleNameIndex = 0; + int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); + int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); + int innerClassNameIndex = 0; int accessFlags = 0; if (!m_index.isAnonymousClass(obfClassEntry)) { - innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getSimpleName()); + innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnerClassName()); } - attr.append(innerClassIndex, parentClassIndex, innerClassSimpleNameIndex, accessFlags); + attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags); /* DEBUG System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", @@ -135,32 +120,5 @@ public class InnerClassWriter { obfClassEntry.getName() )); */ - - return obfInnerClassName; - } - - private String getFullyQualifiedParentName(List classChain, ClassEntry classEntry) { - assert(classChain.size() > 1); - assert(classChain.contains(classEntry)); - StringBuilder buf = new StringBuilder(); - for (int i=0; classChain.get(i) != classEntry; i++) { - ClassEntry chainEntry = classChain.get(i); - if (buf.length() == 0) { - buf.append(chainEntry.getName()); - } else { - buf.append("$"); - buf.append(chainEntry.getSimpleName()); - } - } - return buf.toString(); - } - - private String getFullyQualifiedName(List classChain, ClassEntry classEntry) { - boolean isInner = classChain.size() > 1; - if (isInner) { - return getFullyQualifiedParentName(classChain, classEntry) + "$" + classEntry.getSimpleName(); - } else { - return classEntry.getName(); - } } } diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 69f171a5..69e66bc0 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -11,6 +11,7 @@ package cuchaz.enigma.mapping; import java.io.Serializable; +import java.util.List; public class ClassEntry implements Entry, Serializable { @@ -114,13 +115,28 @@ public class ClassEntry implements Entry, Serializable { } public String getSimpleName() { - if (isInnerClass()) { - return getInnerClassName(); - } int pos = m_name.lastIndexOf('/'); if (pos > 0) { return m_name.substring(pos + 1); } return m_name; } + + public ClassEntry buildClassEntry(List classChain) { + assert(classChain.contains(this)); + StringBuilder buf = new StringBuilder(); + for (ClassEntry chainEntry : classChain) { + if (buf.length() == 0) { + buf.append(chainEntry.getName()); + } else { + buf.append("$"); + buf.append(chainEntry.isInnerClass() ? chainEntry.getInnerClassName() : chainEntry.getSimpleName()); + } + + if (chainEntry == this) { + break; + } + } + return new ClassEntry(buf.toString()); + } } diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index bbdfa739..f4d62c8e 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java @@ -1,7 +1,5 @@ package cuchaz.enigma.mapping; -import java.util.List; - import javassist.CtBehavior; import javassist.CtClass; import javassist.CtConstructor; @@ -13,7 +11,6 @@ import javassist.expr.FieldAccess; import javassist.expr.MethodCall; import javassist.expr.NewExpr; -import com.beust.jcommander.internal.Lists; import com.strobel.assembler.metadata.MethodDefinition; import cuchaz.enigma.analysis.JarIndex; @@ -25,35 +22,8 @@ public class EntryFactory { } public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { - return getChainedOuterClassName(jarIndex, new ClassEntry(classMapping.getObfFullName())); - } - - public static ClassEntry getChainedOuterClassName(JarIndex jarIndex, ClassEntry obfClassEntry) { - - // lookup the chain of outer classes - List obfClassChain = Lists.newArrayList(obfClassEntry); - ClassEntry checkClassEntry = obfClassEntry; - while (true) { - ClassEntry obfOuterClassEntry = jarIndex.getOuterClass(checkClassEntry); - if (obfOuterClassEntry != null) { - obfClassChain.add(obfOuterClassEntry); - checkClassEntry = obfOuterClassEntry; - } else { - break; - } - } - - // build the chained class name - StringBuilder buf = new StringBuilder(); - for (int i=obfClassChain.size()-1; i>=0; i--) { - if (buf.length() == 0) { - buf.append(obfClassChain.get(i).getName()); - } else { - buf.append("$"); - buf.append(obfClassChain.get(i).getSimpleName()); - } - } - return new ClassEntry(buf.toString()); + ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName()); + return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); } public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { diff --git a/test/cuchaz/enigma/TestDeobfed.java b/test/cuchaz/enigma/TestDeobfed.java new file mode 100644 index 00000000..3c2ae51d --- /dev/null +++ b/test/cuchaz/enigma/TestDeobfed.java @@ -0,0 +1,79 @@ +package cuchaz.enigma; + + +import static cuchaz.enigma.TestEntryFactory.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +import java.util.jar.JarFile; + +import org.junit.BeforeClass; +import org.junit.Test; + +import cuchaz.enigma.analysis.JarIndex; + + +public class TestDeobfed { + + private static JarFile m_jar; + private static JarIndex m_index; + + @BeforeClass + public static void beforeClass() + throws Exception { + m_jar = new JarFile("build/testTranslation.deobf.jar"); + m_index = new JarIndex(); + m_index.indexJar(m_jar, true); + } + + @Test + public void obfEntries() { + assertThat(m_index.getObfClassEntries(), containsInAnyOrder( + newClass("cuchaz/enigma/inputs/Keep"), + newClass("none/a"), + newClass("none/b"), + newClass("none/c"), + newClass("none/d"), + newClass("none/d$e"), + newClass("none/f"), + newClass("none/g"), + newClass("none/h"), + newClass("none/h$i"), + newClass("none/h$i$j"), + newClass("none/h$k"), + newClass("none/h$k$l"), + newClass("none/m"), + newClass("none/m$n"), + newClass("none/m$n$o"), + newClass("none/m$p"), + newClass("none/m$p$q"), + newClass("none/m$p$q$r"), + newClass("none/m$p$q$s") + )); + } + + @Test + public void decompile() + throws Exception { + Deobfuscator deobfuscator = new Deobfuscator(m_jar); + deobfuscator.getSourceTree("none/a"); + deobfuscator.getSourceTree("none/b"); + deobfuscator.getSourceTree("none/c"); + deobfuscator.getSourceTree("none/d"); + deobfuscator.getSourceTree("none/d$e"); + deobfuscator.getSourceTree("none/f"); + deobfuscator.getSourceTree("none/g"); + deobfuscator.getSourceTree("none/h"); + deobfuscator.getSourceTree("none/h$i"); + deobfuscator.getSourceTree("none/h$i$j"); + deobfuscator.getSourceTree("none/h$k"); + deobfuscator.getSourceTree("none/h$k$l"); + deobfuscator.getSourceTree("none/m"); + deobfuscator.getSourceTree("none/m$n"); + deobfuscator.getSourceTree("none/m$n$o"); + deobfuscator.getSourceTree("none/m$p"); + deobfuscator.getSourceTree("none/m$p$q"); + deobfuscator.getSourceTree("none/m$p$q$r"); + deobfuscator.getSourceTree("none/m$p$q$s"); + } +} diff --git a/test/cuchaz/enigma/TestJarIndexDeobfed.java b/test/cuchaz/enigma/TestJarIndexDeobfed.java deleted file mode 100644 index f776e4f6..00000000 --- a/test/cuchaz/enigma/TestJarIndexDeobfed.java +++ /dev/null @@ -1,52 +0,0 @@ -package cuchaz.enigma; - - -import static cuchaz.enigma.TestEntryFactory.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -import java.util.jar.JarFile; - -import org.junit.BeforeClass; -import org.junit.Test; - -import cuchaz.enigma.analysis.JarIndex; - - -public class TestJarIndexDeobfed { - - private static JarIndex m_index; - - @BeforeClass - public static void beforeClass() - throws Exception { - m_index = new JarIndex(); - m_index.indexJar(new JarFile("build/testTranslation.deobf.jar"), true); - } - - @Test - public void obfEntries() { - assertThat(m_index.getObfClassEntries(), containsInAnyOrder( - newClass("cuchaz/enigma/inputs/Keep"), - newClass("none/a"), - newClass("none/b"), - newClass("none/c"), - newClass("none/d"), - newClass("none/d$e"), - newClass("none/f"), - newClass("none/g"), - newClass("none/h"), - newClass("none/h$i"), - newClass("none/h$i$j"), - newClass("none/h$k"), - newClass("none/h$k$l"), - newClass("none/m"), - newClass("none/m$n"), - newClass("none/m$n$o"), - newClass("none/m$p"), - newClass("none/m$p$q"), - newClass("none/m$p$q$r"), - newClass("none/m$p$q$s") - )); - } -} -- cgit v1.2.3 From 09e41e6dcce1698f9bd626c4f745a63af5077fe9 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 28 Feb 2015 11:17:39 -0500 Subject: update version string to 0.8 beta --- src/cuchaz/enigma/Constants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 3bf39995..db147781 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -12,7 +12,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.7 beta"; + public static final String Version = "0.8 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024 * 1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte -- cgit v1.2.3 -- cgit v1.2.3 From e0ff85569297808a14ab677e65a3ace8f2f56a14 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 28 Feb 2015 12:09:48 -0500 Subject: ignore more harmless exceptions from the buggy highlight painter system --- src/cuchaz/enigma/ExceptionIgnorer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cuchaz/enigma/ExceptionIgnorer.java b/src/cuchaz/enigma/ExceptionIgnorer.java index 37c67da7..1bb58243 100644 --- a/src/cuchaz/enigma/ExceptionIgnorer.java +++ b/src/cuchaz/enigma/ExceptionIgnorer.java @@ -10,9 +10,9 @@ public class ExceptionIgnorer { StackTraceElement[] stackTrace = t.getStackTrace(); if (stackTrace.length > 1) { - // does this stack frame match javax.swing.text.DefaultHighlighter.paint() ? + // does this stack frame match javax.swing.text.DefaultHighlighter.paint*() ? StackTraceElement frame = stackTrace[1]; - if (frame.getClassName().equals("javax.swing.text.DefaultHighlighter") && frame.getMethodName().equals("paint")) { + if (frame.getClassName().equals("javax.swing.text.DefaultHighlighter") && frame.getMethodName().startsWith("paint")) { return true; } } -- cgit v1.2.3 From 741e3472f76d959645ee0e025547d69a03e5b6f2 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 28 Feb 2015 18:00:25 -0500 Subject: fix up conversion tool to handle Minecraft 1.8.3 --- src/cuchaz/enigma/convert/ClassForest.java | 50 ++++ src/cuchaz/enigma/convert/ClassIdentifier.java | 41 +++ src/cuchaz/enigma/convert/ClassIdentity.java | 125 +++++---- src/cuchaz/enigma/convert/ClassMatch.java | 72 +++++ src/cuchaz/enigma/convert/ClassMatcher.java | 356 +++++++++++-------------- src/cuchaz/enigma/convert/ClassMatching.java | 200 +++++++------- src/cuchaz/enigma/convert/ClassNamer.java | 10 +- src/cuchaz/enigma/mapping/Translator.java | 1 + 8 files changed, 486 insertions(+), 369 deletions(-) create mode 100644 src/cuchaz/enigma/convert/ClassForest.java create mode 100644 src/cuchaz/enigma/convert/ClassIdentifier.java create mode 100644 src/cuchaz/enigma/convert/ClassMatch.java diff --git a/src/cuchaz/enigma/convert/ClassForest.java b/src/cuchaz/enigma/convert/ClassForest.java new file mode 100644 index 00000000..e113eeb9 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassForest.java @@ -0,0 +1,50 @@ +package cuchaz.enigma.convert; + +import java.util.Collection; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; + +import cuchaz.enigma.mapping.ClassEntry; + + +public class ClassForest { + + private ClassIdentifier m_identifier; + private Multimap m_forest; + + public ClassForest(ClassIdentifier identifier) { + m_identifier = identifier; + m_forest = HashMultimap.create(); + } + + public ClassIdentifier getIdentifier() { + return m_identifier; + } + + public void addAll(Iterable entries) { + for (ClassEntry entry : entries) { + add(entry); + } + } + + private void add(ClassEntry entry) { + m_forest.put(m_identifier.identify(entry), entry); + } + + public Collection identities() { + return m_forest.keySet(); + } + + public Collection classes() { + return m_forest.values(); + } + + public Collection getClasses(ClassIdentity identity) { + return m_forest.get(identity); + } + + public boolean containsIdentity(ClassIdentity identity) { + return m_forest.containsKey(identity); + } +} diff --git a/src/cuchaz/enigma/convert/ClassIdentifier.java b/src/cuchaz/enigma/convert/ClassIdentifier.java new file mode 100644 index 00000000..bdbf11b2 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassIdentifier.java @@ -0,0 +1,41 @@ +package cuchaz.enigma.convert; + +import java.util.Map; +import java.util.jar.JarFile; + +import javassist.CtClass; + +import com.beust.jcommander.internal.Maps; + +import cuchaz.enigma.TranslatingTypeLoader; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; +import cuchaz.enigma.mapping.ClassEntry; + + +public class ClassIdentifier { + + private JarIndex m_index; + private SidedClassNamer m_namer; + private boolean m_useReferences; + private TranslatingTypeLoader m_loader; + private Map m_cache; + + public ClassIdentifier(JarFile jar, JarIndex index, SidedClassNamer namer, boolean useReferences) { + m_index = index; + m_namer = namer; + m_useReferences = useReferences; + m_loader = new TranslatingTypeLoader(jar, index); + m_cache = Maps.newHashMap(); + } + + public ClassIdentity identify(ClassEntry classEntry) { + ClassIdentity identity = m_cache.get(classEntry); + if (identity == null) { + CtClass c = m_loader.loadClass(classEntry.getName()); + identity = new ClassIdentity(c, m_namer, m_index, m_useReferences); + m_cache.put(classEntry, identity); + } + return identity; + } +} diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index b5140124..3736a533 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -52,9 +52,10 @@ import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassNameReplacer; import cuchaz.enigma.mapping.Entry; -import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Signature; +import cuchaz.enigma.mapping.Type; public class ClassIdentity { @@ -68,7 +69,45 @@ public class ClassIdentity { private Multiset m_implements; private Multiset m_implementations; private Multiset m_references; - + + private final ClassNameReplacer m_classNameReplacer = new ClassNameReplacer() { + + private Map m_classNames = Maps.newHashMap(); + + @Override + public String replace(String className) { + + // classes not in the none package can be passed through + ClassEntry classEntry = new ClassEntry(className); + if (!classEntry.getPackageName().equals(Constants.NonePackage)) { + return className; + } + + // is this class ourself? + if (className.equals(m_classEntry.getName())) { + return "CSelf"; + } + + // try the namer + if (m_namer != null) { + String newName = m_namer.getName(className); + if (newName != null) { + return newName; + } + } + + // otherwise, use local naming + if (!m_classNames.containsKey(className)) { + m_classNames.put(className, getNewClassName()); + } + return m_classNames.get(className); + } + + private String getNewClassName() { + return String.format("C%03d", m_classNames.size()); + } + }; + public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) { m_namer = namer; @@ -77,7 +116,7 @@ public class ClassIdentity { m_classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); m_fields = HashMultiset.create(); for (CtField field : c.getDeclaredFields()) { - m_fields.add(scrubSignature(field.getSignature())); + m_fields.add(scrubType(field.getSignature())); } m_methods = HashMultiset.create(); for (CtMethod method : c.getDeclaredMethods()) { @@ -93,11 +132,11 @@ public class ClassIdentity { } m_extends = ""; if (c.getClassFile().getSuperclass() != null) { - m_extends = scrubClassName(c.getClassFile().getSuperclass()); + m_extends = scrubClassName(Descriptor.toJvmName(c.getClassFile().getSuperclass())); } m_implements = HashMultiset.create(); for (String interfaceName : c.getClassFile().getInterfaces()) { - m_implements.add(scrubClassName(interfaceName)); + m_implements.add(scrubClassName(Descriptor.toJvmName(interfaceName))); } // stuff from the jar index @@ -132,9 +171,14 @@ public class ClassIdentity { private void addReference(EntryReference reference) { if (reference.context.getSignature() != null) { - m_references.add(String.format("%s_%s", scrubClassName(reference.context.getClassName()), scrubSignature(reference.context.getSignature()))); + m_references.add(String.format("%s_%s", + scrubClassName(reference.context.getClassName()), + scrubSignature(reference.context.getSignature()) + )); } else { - m_references.add(String.format("%s_", scrubClassName(reference.context.getClassName()))); + m_references.add(String.format("%s_", + scrubClassName(reference.context.getClassName()) + )); } } @@ -194,52 +238,27 @@ public class ClassIdentity { } private String scrubClassName(String className) { - return scrubSignature("L" + Descriptor.toJvmName(className) + ";"); + return m_classNameReplacer.replace(className); + } + + private String scrubType(String typeName) { + return scrubType(new Type(typeName)).toString(); + } + + private Type scrubType(Type type) { + if (type.hasClass()) { + return new Type(type, m_classNameReplacer); + } else { + return type; + } } private String scrubSignature(String signature) { - return scrubSignature(new Signature(signature)); + return scrubSignature(new Signature(signature)).toString(); } - private String scrubSignature(Signature signature) { - - return new Signature(signature, new ClassNameReplacer() { - - private Map m_classNames = Maps.newHashMap(); - - @Override - public String replace(String className) { - - // classes not in the none package can be passed through - ClassEntry classEntry = new ClassEntry(className); - if (!classEntry.getPackageName().equals(Constants.NonePackage)) { - return className; - } - - // is this class ourself? - if (className.equals(m_classEntry.getName())) { - return "CSelf"; - } - - // try the namer - if (m_namer != null) { - String newName = m_namer.getName(className); - if (newName != null) { - return newName; - } - } - - // otherwise, use local naming - if (!m_classNames.containsKey(className)) { - m_classNames.put(className, getNewClassName()); - } - return m_classNames.get(className); - } - - private String getNewClassName() { - return String.format("C%03d", m_classNames.size()); - } - }).toString(); + private Signature scrubSignature(Signature signature) { + return new Signature(signature, m_classNameReplacer); } private boolean isClassMatchedUniquely(String className) { @@ -284,7 +303,7 @@ public class ClassIdentity { behavior.instrument(new ExprEditor() { @Override public void edit(MethodCall call) { - updateHashWithString(digest, scrubClassName(call.getClassName())); + updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); updateHashWithString(digest, scrubSignature(call.getSignature())); if (isClassMatchedUniquely(call.getClassName())) { updateHashWithString(digest, call.getMethodName()); @@ -293,8 +312,8 @@ public class ClassIdentity { @Override public void edit(FieldAccess access) { - updateHashWithString(digest, scrubClassName(access.getClassName())); - updateHashWithString(digest, scrubSignature(access.getSignature())); + updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(access.getClassName()))); + updateHashWithString(digest, scrubType(access.getSignature())); if (isClassMatchedUniquely(access.getClassName())) { updateHashWithString(digest, access.getFieldName()); } @@ -302,13 +321,13 @@ public class ClassIdentity { @Override public void edit(ConstructorCall call) { - updateHashWithString(digest, scrubClassName(call.getClassName())); + updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); updateHashWithString(digest, scrubSignature(call.getSignature())); } @Override public void edit(NewExpr expr) { - updateHashWithString(digest, scrubClassName(expr.getClassName())); + updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(expr.getClassName()))); } }); diff --git a/src/cuchaz/enigma/convert/ClassMatch.java b/src/cuchaz/enigma/convert/ClassMatch.java new file mode 100644 index 00000000..9cecf701 --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassMatch.java @@ -0,0 +1,72 @@ +package cuchaz.enigma.convert; + +import java.util.Collection; +import java.util.Set; + +import com.google.common.collect.Sets; + +import cuchaz.enigma.Util; +import cuchaz.enigma.mapping.ClassEntry; + + +public class ClassMatch { + + public Set sourceClasses; + public Set destClasses; + + public ClassMatch(Collection sourceClasses, Collection destClasses) { + this.sourceClasses = Sets.newHashSet(sourceClasses); + this.destClasses = Sets.newHashSet(destClasses); + } + + public ClassMatch(ClassEntry sourceClass, ClassEntry destClass) { + this.sourceClasses = Sets.newHashSet(sourceClass); + this.destClasses = Sets.newHashSet(destClass); + } + + public boolean isMatched() { + return sourceClasses.size() > 0 && destClasses.size() > 0; + } + + public boolean isAmbiguous() { + return sourceClasses.size() > 1 || destClasses.size() > 1; + } + + public ClassEntry getUniqueSource() { + if (sourceClasses.size() != 1) { + throw new IllegalStateException("Match has ambiguous source!"); + } + return sourceClasses.iterator().next(); + } + + public ClassEntry getUniqueDest() { + if (destClasses.size() != 1) { + throw new IllegalStateException("Match has ambiguous source!"); + } + return destClasses.iterator().next(); + } + + public Set intersectSourceClasses(Set classes) { + Set intersection = Sets.newHashSet(sourceClasses); + intersection.retainAll(classes); + return intersection; + } + + @Override + public int hashCode() { + return Util.combineHashesOrdered(sourceClasses, destClasses); + } + + @Override + public boolean equals(Object other) { + if (other instanceof ClassMatch) { + return equals((ClassMatch)other); + } + return false; + } + + public boolean equals(ClassMatch other) { + return this.sourceClasses.equals(other.sourceClasses) + && this.destClasses.equals(other.destClasses); + } +} diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index 224d0048..f43f5b20 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -26,9 +26,6 @@ import java.util.Map; import java.util.Set; import java.util.jar.JarFile; -import javassist.CtBehavior; -import javassist.CtClass; - import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; @@ -37,12 +34,10 @@ import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; -import cuchaz.enigma.TranslatingTypeLoader; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; -import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsReader; @@ -52,18 +47,23 @@ import cuchaz.enigma.mapping.MethodMapping; public class ClassMatcher { - public static void main(String[] args) throws IOException, MappingParseException { - // TEMP - JarFile sourceJar = new JarFile(new File("input/1.8-pre3.jar")); - JarFile destJar = new JarFile(new File("input/1.8.jar")); - File inMappingsFile = new File("../Enigma Mappings/1.8-pre3.mappings"); - File outMappingsFile = new File("../Enigma Mappings/1.8.mappings"); + public static void main(String[] args) + throws IOException, MappingParseException { + + // setup files + File home = new File(System.getProperty("user.home")); + JarFile sourceJar = new JarFile(new File(home, ".minecraft/versions/1.8/1.8.jar")); + JarFile destJar = new JarFile(new File(home, ".minecraft/versions/1.8.3/1.8.3.jar")); + File inMappingsFile = new File("../Enigma Mappings/1.8.mappings"); + File outMappingsFile = new File("../Enigma Mappings/1.8.3.mappings"); // define a matching to use when the automated system cannot find a match Map fallbackMatching = Maps.newHashMap(); + /* fallbackMatching.put("none/ayb", "none/ayf"); fallbackMatching.put("none/ayd", "none/ayd"); fallbackMatching.put("none/bgk", "unknown/bgk"); + */ // do the conversion Mappings mappings = new MappingsReader().read(new FileReader(inMappingsFile)); @@ -77,6 +77,7 @@ public class ClassMatcher { } private static void convertMappings(JarFile sourceJar, JarFile destJar, Mappings mappings, Map fallbackMatching) { + // index jars System.out.println("Indexing source jar..."); JarIndex sourceIndex = new JarIndex(); @@ -84,48 +85,88 @@ public class ClassMatcher { System.out.println("Indexing dest jar..."); JarIndex destIndex = new JarIndex(); destIndex.indexJar(destJar, false); - TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader(sourceJar, sourceIndex); - TranslatingTypeLoader destLoader = new TranslatingTypeLoader(destJar, destIndex); // compute the matching - ClassMatching matching = computeMatching(sourceIndex, sourceLoader, destIndex, destLoader); - Map>> matchingIndex = matching.getIndex(); + ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex); // get all the obf class names used in the mappings - Set usedClassNames = mappings.getAllObfClassNames(); - Set allClassNames = Sets.newHashSet(); - for (ClassEntry classEntry : sourceIndex.getObfClassEntries()) { - allClassNames.add(classEntry.getName()); + Set usedClasses = Sets.newHashSet(); + for (String className : mappings.getAllObfClassNames()) { + usedClasses.add(new ClassEntry(className)); } - usedClassNames.retainAll(allClassNames); - System.out.println("Used " + usedClassNames.size() + " classes in the mappings"); + System.out.println("Mappings reference " + usedClasses.size() + " classes"); - // probabilistically match the non-uniquely-matched source classes - for (Map.Entry> entry : matchingIndex.values()) { - ClassIdentity sourceClass = entry.getKey(); - List destClasses = entry.getValue(); - - // skip classes that are uniquely matched - if (destClasses.size() == 1) { + // see what the used classes map to + BiMap uniqueUsedMatches = HashBiMap.create(); + Map ambiguousUsedMatches = Maps.newHashMap(); + Set unmatchedUsedClasses = Sets.newHashSet(); + for (ClassMatch match : matching.matches()) { + Set matchUsedClasses = match.intersectSourceClasses(usedClasses); + if (matchUsedClasses.isEmpty()) { continue; } - - // skip classes that aren't used in the mappings - if (!usedClassNames.contains(sourceClass.getClassEntry().getName())) { - continue; + + // classify the match + if (!match.isMatched()) { + // unmatched + unmatchedUsedClasses.addAll(matchUsedClasses); + } else { + if (match.isAmbiguous()) { + // ambiguously matched + for (ClassEntry matchUsedClass : matchUsedClasses) { + ambiguousUsedMatches.put(matchUsedClass, match); + } + } else { + // uniquely matched + uniqueUsedMatches.put(match.getUniqueSource(), match.getUniqueDest()); + } } + } + + // get unmatched dest classes + Set unmatchedDestClasses = Sets.newHashSet(); + for (ClassMatch match : matching.matches()) { + if (!match.isMatched()) { + unmatchedDestClasses.addAll(match.destClasses); + } + } + + // warn about the ambiguous used matches + if (ambiguousUsedMatches.size() > 0) { + System.out.println(String.format("%d source classes have ambiguous mappings", ambiguousUsedMatches.size())); + List ambiguousMatchesList = Lists.newArrayList(Sets.newHashSet(ambiguousUsedMatches.values())); + Collections.sort(ambiguousMatchesList, new Comparator() { + @Override + public int compare(ClassMatch a, ClassMatch b) { + String aName = a.sourceClasses.iterator().next().getName(); + String bName = b.sourceClasses.iterator().next().getName(); + return aName.compareTo(bName); + } + }); + for (ClassMatch match : ambiguousMatchesList) { + System.out.println("Ambiguous matching:"); + System.out.println("\tSource: " + getClassNames(match.sourceClasses)); + System.out.println("\tDest: " + getClassNames(match.destClasses)); + } + } + + // warn about unmatched used classes + for (ClassEntry unmatchedUsedClass : unmatchedUsedClasses) { + System.out.println("No exact match for source class " + unmatchedUsedClass.getClassEntry()); - System.out.println("No exact match for source class " + sourceClass.getClassEntry()); - - // find the closest classes - Multimap scoredMatches = ArrayListMultimap.create(); - for (ClassIdentity c : destClasses) { - scoredMatches.put(sourceClass.getMatchScore(c), c); + // rank all the unmatched dest classes against the used class + ClassIdentity sourceIdentity = matching.getSourceIdentifier().identify(unmatchedUsedClass); + Multimap scoredDestClasses = ArrayListMultimap.create(); + for (ClassEntry unmatchedDestClass : unmatchedDestClasses) { + ClassIdentity destIdentity = matching.getDestIdentifier().identify(unmatchedDestClass); + scoredDestClasses.put(sourceIdentity.getMatchScore(destIdentity), unmatchedDestClass); } - List scores = new ArrayList(scoredMatches.keySet()); + + List scores = new ArrayList(scoredDestClasses.keySet()); Collections.sort(scores, Collections.reverseOrder()); - printScoredMatches(sourceClass.getMaxMatchScore(), scores, scoredMatches); + printScoredMatches(sourceIdentity.getMaxMatchScore(), scores, scoredDestClasses); + /* TODO: re-enable auto-pick logic // does the best match have a non-zero score and the same name? int bestScore = scores.get(0); Collection bestMatches = scoredMatches.get(bestScore); @@ -138,85 +179,45 @@ public class ClassMatcher { destClasses.add(bestMatch); } } + */ } - // group the matching into unique and non-unique matches - BiMap matchedClassNames = HashBiMap.create(); - Set unmatchedSourceClassNames = Sets.newHashSet(); - for (String className : usedClassNames) { - // is there a match for this class? - Map.Entry> entry = matchingIndex.get(className); - ClassIdentity sourceClass = entry.getKey(); - List matches = entry.getValue(); - - if (matches.size() == 1) { - // unique match! We're good to go! - matchedClassNames.put(sourceClass.getClassEntry().getName(), matches.get(0).getClassEntry().getName()); - } else { - // no match, check the fallback matching - String fallbackMatch = fallbackMatching.get(className); - if (fallbackMatch != null) { - matchedClassNames.put(sourceClass.getClassEntry().getName(), fallbackMatch); - } else { - unmatchedSourceClassNames.add(className); - } - } - } - - // report unmatched classes - if (!unmatchedSourceClassNames.isEmpty()) { - System.err.println("ERROR: there were unmatched classes!"); - for (String className : unmatchedSourceClassNames) { - System.err.println("\t" + className); - } - return; - } - - // get the class name changes from the matched class names - Map classChanges = Maps.newHashMap(); - for (Map.Entry entry : matchedClassNames.entrySet()) { - if (!entry.getKey().equals(entry.getValue())) { - classChanges.put(entry.getKey(), entry.getValue()); - System.out.println(String.format("Class change: %s -> %s", entry.getKey(), entry.getValue())); - /* DEBUG - System.out.println(String.format("\n%s\n%s", - new ClassIdentity(sourceLoader.loadClass(entry.getKey()), null, sourceIndex, false, false), - new ClassIdentity( destLoader.loadClass(entry.getValue()), null, destIndex, false, false) - )); - */ - } + // bail if there were unmatched classes + if (!unmatchedUsedClasses.isEmpty()) { + throw new Error("There were " + unmatchedUsedClasses.size() + " unmatched classes!"); } // sort the changes so classes are renamed in the correct order // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b - LinkedHashMap orderedClassChanges = Maps.newLinkedHashMap(); - int numChangesLeft = classChanges.size(); - while (!classChanges.isEmpty()) { - Iterator> iter = classChanges.entrySet().iterator(); + BiMap unsortedChanges = HashBiMap.create(uniqueUsedMatches); + LinkedHashMap sortedChanges = Maps.newLinkedHashMap(); + int numChangesLeft = unsortedChanges.size(); + while (!unsortedChanges.isEmpty()) { + Iterator> iter = unsortedChanges.entrySet().iterator(); while (iter.hasNext()) { - Map.Entry entry = iter.next(); - if (classChanges.get(entry.getValue()) == null) { - orderedClassChanges.put(entry.getKey(), entry.getValue()); + Map.Entry change = iter.next(); + if (unsortedChanges.containsKey(change.getValue())) { + sortedChanges.put(change.getKey(), change.getValue()); iter.remove(); } } // did we remove any changes? - if (numChangesLeft - classChanges.size() > 0) { + if (numChangesLeft - unsortedChanges.size() > 0) { // keep going - numChangesLeft = classChanges.size(); + numChangesLeft = unsortedChanges.size(); } else { // can't sort anymore. There must be a loop break; } } - if (classChanges.size() > 0) { - throw new Error(String.format("Unable to sort %d/%d class changes!", classChanges.size(), matchedClassNames.size())); + if (!unsortedChanges.isEmpty()) { + throw new Error(String.format("Unable to sort %d/%d class changes!", unsortedChanges.size(), uniqueUsedMatches.size())); } // convert the mappings in the correct class order - for (Map.Entry entry : orderedClassChanges.entrySet()) { - mappings.renameObfClass(entry.getKey(), entry.getValue()); + for (Map.Entry entry : sortedChanges.entrySet()) { + mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); } // check the method matches @@ -238,6 +239,7 @@ public class ClassMatcher { if (!destIndex.containsObfBehavior(methodEntry)) { System.err.println("WARNING: method doesn't match: " + methodEntry); + /* TODO: show methods if needed // show the available methods System.err.println("\tAvailable dest methods:"); CtClass c = destLoader.loadClass(classMapping.getObfFullName()); @@ -250,6 +252,7 @@ public class ClassMatcher { for (CtBehavior behavior : c.getDeclaredBehaviors()) { System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); } + */ } } } @@ -257,125 +260,72 @@ public class ClassMatcher { System.out.println("Done!"); } - public static ClassMatching computeMatching(JarIndex sourceIndex, TranslatingTypeLoader sourceLoader, JarIndex destIndex, TranslatingTypeLoader destLoader) { + public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex) { - System.out.println("Matching classes..."); + System.out.println("Iteratively matching classes..."); - ClassMatching matching = null; + ClassMatching lastMatching = null; + int round = 0; + SidedClassNamer sourceNamer = null; + SidedClassNamer destNamer = null; for (boolean useReferences : Arrays.asList(false, true)) { - int numMatches = 0; - do { - SidedClassNamer sourceNamer = null; - SidedClassNamer destNamer = null; - if (matching != null) { - // build a class namer - ClassNamer namer = new ClassNamer(matching.getUniqueMatches()); - sourceNamer = namer.getSourceNamer(); - destNamer = namer.getDestNamer(); - - // note the number of matches - numMatches = matching.getUniqueMatches().size(); - } + + int numUniqueMatchesLastTime = 0; + if (lastMatching != null) { + numUniqueMatchesLastTime = lastMatching.uniqueMatches().size(); + } + + while (true) { + + System.out.println("Round " + (++round) + " ..."); + + // init the matching with identity settings + ClassMatching matching = new ClassMatching( + new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences), + new ClassIdentifier(destJar, destIndex, destNamer, useReferences) + ); - // get the entries left to match - Set sourceClassEntries = Sets.newHashSet(); - Set destClassEntries = Sets.newHashSet(); - if (matching == null) { - sourceClassEntries.addAll(sourceIndex.getObfClassEntries()); - destClassEntries.addAll(destIndex.getObfClassEntries()); - matching = new ClassMatching(); + if (lastMatching == null) { + // search all classes + matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries()); } else { - for (Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet()) { - for (ClassIdentity c : entry.getKey()) { - sourceClassEntries.add(c.getClassEntry()); - matching.removeSource(c); - } - for (ClassIdentity c : entry.getValue()) { - destClassEntries.add(c.getClassEntry()); - matching.removeDest(c); - } - } - for (ClassIdentity c : matching.getUnmatchedSourceClasses()) { - sourceClassEntries.add(c.getClassEntry()); - matching.removeSource(c); - } - for (ClassIdentity c : matching.getUnmatchedDestClasses()) { - destClassEntries.add(c.getClassEntry()); - matching.removeDest(c); + // we already know about these matches + matching.addKnownMatches(lastMatching.uniqueMatches()); + + // search unmatched and ambiguously-matched classes + matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses()); + for (ClassMatch match : lastMatching.ambiguousMatches()) { + matching.match(match.sourceClasses, match.destClasses); } } + System.out.println(matching); + BiMap uniqueMatches = matching.uniqueMatches(); - // compute a matching for the classes - for (ClassEntry classEntry : sourceClassEntries) { - CtClass c = sourceLoader.loadClass(classEntry.getName()); - ClassIdentity sourceClass = new ClassIdentity(c, sourceNamer, sourceIndex, useReferences); - matching.addSource(sourceClass); - } - for (ClassEntry classEntry : destClassEntries) { - CtClass c = destLoader.loadClass(classEntry.getName()); - ClassIdentity destClass = new ClassIdentity(c, destNamer, destIndex, useReferences); - matching.matchDestClass(destClass); + // did we match anything new this time? + if (uniqueMatches.size() > numUniqueMatchesLastTime) { + numUniqueMatchesLastTime = uniqueMatches.size(); + lastMatching = matching; + } else { + break; } - // TEMP - System.out.println(matching); - } while (matching.getUniqueMatches().size() - numMatches > 0); - } - - // check the class matches - System.out.println("Checking class matches..."); - ClassNamer namer = new ClassNamer(matching.getUniqueMatches()); - SidedClassNamer sourceNamer = namer.getSourceNamer(); - SidedClassNamer destNamer = namer.getDestNamer(); - for (Map.Entry entry : matching.getUniqueMatches().entrySet()) { - - // check source - ClassIdentity sourceClass = entry.getKey(); - CtClass sourceC = sourceLoader.loadClass(sourceClass.getClassEntry().getName()); - assert (sourceC != null) : "Unable to load source class " + sourceClass.getClassEntry(); - assert (sourceClass.matches(sourceC)) : "Source " + sourceClass + " doesn't match " + new ClassIdentity(sourceC, sourceNamer, sourceIndex, false); - - // check dest - ClassIdentity destClass = entry.getValue(); - CtClass destC = destLoader.loadClass(destClass.getClassEntry().getName()); - assert (destC != null) : "Unable to load dest class " + destClass.getClassEntry(); - assert (destClass.matches(destC)) : "Dest " + destClass + " doesn't match " + new ClassIdentity(destC, destNamer, destIndex, false); - } - - // warn about the ambiguous matchings - List,List>> ambiguousMatches = new ArrayList,List>>(matching.getAmbiguousMatches().entrySet()); - Collections.sort(ambiguousMatches, new Comparator,List>>() { - @Override - public int compare(Map.Entry,List> a, Map.Entry,List> b) { - String aName = a.getKey().get(0).getClassEntry().getName(); - String bName = b.getKey().get(0).getClassEntry().getName(); - return aName.compareTo(bName); + // update the namers + ClassNamer namer = new ClassNamer(uniqueMatches); + sourceNamer = namer.getSourceNamer(); + destNamer = namer.getDestNamer(); } - }); - for (Map.Entry,List> entry : ambiguousMatches) { - System.out.println("Ambiguous matching:"); - System.out.println("\tSource: " + getClassNames(entry.getKey())); - System.out.println("\tDest: " + getClassNames(entry.getValue())); } - /* DEBUG - Map.Entry,List> entry = ambiguousMatches.get( 7 ); - for (ClassIdentity c : entry.getKey()) { - System.out.println(c); - } - for(ClassIdentity c : entry.getKey()) { - System.out.println(decompile(sourceLoader, c.getClassEntry())); - } - */ - - return matching; + return lastMatching; } - private static void printScoredMatches(int maxScore, List scores, Multimap scoredMatches) { + private static void printScoredMatches(int maxScore, List scores, Multimap scoredMatches) { int numScoredMatchesShown = 0; for (int score : scores) { - for (ClassIdentity scoredMatch : scoredMatches.get(score)) { - System.out.println(String.format("\tScore: %3d %3.0f%% %s", score, 100.0 * score / maxScore, scoredMatch.getClassEntry().getName())); + for (ClassEntry classEntry : scoredMatches.get(score)) { + System.out.println(String.format("\tScore: %3d %3.0f%% %s", + score, 100.0 * score / maxScore, classEntry.getName() + )); if (numScoredMatchesShown++ > 10) { return; } @@ -383,10 +333,10 @@ public class ClassMatcher { } } - private static List getClassNames(Collection classes) { + private static List getClassNames(Collection classes) { List out = Lists.newArrayList(); - for (ClassIdentity c : classes) { - out.add(c.getClassEntry().getName()); + for (ClassEntry c : classes) { + out.add(c.getName()); } Collections.sort(out); return out; diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java index 53b6f7f4..b94fd8bd 100644 --- a/src/cuchaz/enigma/convert/ClassMatching.java +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -10,164 +10,146 @@ ******************************************************************************/ package cuchaz.enigma.convert; -import java.util.AbstractMap; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; -import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ClassEntry; public class ClassMatching { - private Multimap m_sourceClasses; - private Multimap m_matchedDestClasses; - private List m_unmatchedDestClasses; - - public ClassMatching() { - m_sourceClasses = ArrayListMultimap.create(); - m_matchedDestClasses = ArrayListMultimap.create(); - m_unmatchedDestClasses = Lists.newArrayList(); - } + private ClassForest m_sourceClasses; + private ClassForest m_destClasses; + private BiMap m_knownMatches; - public void addSource(ClassIdentity c) { - m_sourceClasses.put(c, c); + public ClassMatching(ClassIdentifier sourceIdentifier, ClassIdentifier destIdentifier) { + m_sourceClasses = new ClassForest(sourceIdentifier); + m_destClasses = new ClassForest(destIdentifier); + m_knownMatches = HashBiMap.create(); } - public void matchDestClass(ClassIdentity destClass) { - Collection matchedSourceClasses = m_sourceClasses.get(destClass); - if (matchedSourceClasses.isEmpty()) { - // no match - m_unmatchedDestClasses.add(destClass); - } else { - // found a match - m_matchedDestClasses.put(destClass, destClass); - - // DEBUG - ClassIdentity sourceClass = matchedSourceClasses.iterator().next(); - assert (sourceClass.hashCode() == destClass.hashCode()); - assert (sourceClass.equals(destClass)); - } + public void addKnownMatches(BiMap knownMatches) { + m_knownMatches.putAll(knownMatches); } - public void removeSource(ClassIdentity sourceClass) { - m_sourceClasses.remove(sourceClass, sourceClass); + public void match(Iterable sourceClasses, Iterable destClasses) { + m_sourceClasses.addAll(sourceClasses); + m_destClasses.addAll(destClasses); } - public void removeDest(ClassIdentity destClass) { - m_matchedDestClasses.remove(destClass, destClass); - m_unmatchedDestClasses.remove(destClass); + public Collection matches() { + List matches = Lists.newArrayList(); + for (Entry entry : m_knownMatches.entrySet()) { + matches.add(new ClassMatch( + entry.getKey(), + entry.getValue() + )); + } + for (ClassIdentity identity : m_sourceClasses.identities()) { + matches.add(new ClassMatch( + m_sourceClasses.getClasses(identity), + m_destClasses.getClasses(identity) + )); + } + for (ClassIdentity identity : m_destClasses.identities()) { + if (!m_sourceClasses.containsIdentity(identity)) { + matches.add(new ClassMatch( + new ArrayList(), + m_destClasses.getClasses(identity) + )); + } + } + return matches; } - public List getSourceClasses() { - return new ArrayList(m_sourceClasses.values()); + public Collection sourceClasses() { + Set classes = Sets.newHashSet(); + for (ClassMatch match : matches()) { + classes.addAll(match.sourceClasses); + } + return classes; } - public List getDestClasses() { - List classes = Lists.newArrayList(); - classes.addAll(m_matchedDestClasses.values()); - classes.addAll(m_unmatchedDestClasses); + public Collection destClasses() { + Set classes = Sets.newHashSet(); + for (ClassMatch match : matches()) { + classes.addAll(match.destClasses); + } return classes; } - public BiMap getUniqueMatches() { - BiMap uniqueMatches = HashBiMap.create(); - for (ClassIdentity sourceClass : m_sourceClasses.keySet()) { - Collection matchedSourceClasses = m_sourceClasses.get(sourceClass); - Collection matchedDestClasses = m_matchedDestClasses.get(sourceClass); - if (matchedSourceClasses.size() == 1 && matchedDestClasses.size() == 1) { - ClassIdentity matchedSourceClass = matchedSourceClasses.iterator().next(); - ClassIdentity matchedDestClass = matchedDestClasses.iterator().next(); - uniqueMatches.put(matchedSourceClass, matchedDestClass); + public BiMap uniqueMatches() { + BiMap uniqueMatches = HashBiMap.create(); + for (ClassMatch match : matches()) { + if (match.isMatched() && !match.isAmbiguous()) { + uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); } } return uniqueMatches; } - public BiMap,List> getAmbiguousMatches() { - BiMap,List> ambiguousMatches = HashBiMap.create(); - for (ClassIdentity sourceClass : m_sourceClasses.keySet()) { - Collection matchedSourceClasses = m_sourceClasses.get(sourceClass); - Collection matchedDestClasses = m_matchedDestClasses.get(sourceClass); - if (matchedSourceClasses.size() > 1 && matchedDestClasses.size() > 1) { - ambiguousMatches.put( - new ArrayList(matchedSourceClasses), - new ArrayList(matchedDestClasses) - ); + public Collection ambiguousMatches() { + List ambiguousMatches = Lists.newArrayList(); + for (ClassMatch match : matches()) { + if (match.isMatched() && match.isAmbiguous()) { + ambiguousMatches.add(match); } } return ambiguousMatches; } - public int getNumAmbiguousSourceMatches() { - int num = 0; - for (Map.Entry,List> entry : getAmbiguousMatches().entrySet()) { - num += entry.getKey().size(); - } - return num; - } - - public int getNumAmbiguousDestMatches() { - int num = 0; - for (Map.Entry,List> entry : getAmbiguousMatches().entrySet()) { - num += entry.getValue().size(); + public Collection unmatchedSourceClasses() { + List classes = Lists.newArrayList(); + for (ClassMatch match : matches()) { + if (!match.isMatched() && !match.sourceClasses.isEmpty()) { + classes.addAll(match.sourceClasses); + } } - return num; + return classes; } - public List getUnmatchedSourceClasses() { - List classes = Lists.newArrayList(); - for (ClassIdentity sourceClass : getSourceClasses()) { - if (m_matchedDestClasses.get(sourceClass).isEmpty()) { - classes.add(sourceClass); + public Collection unmatchedDestClasses() { + List classes = Lists.newArrayList(); + for (ClassMatch match : matches()) { + if (!match.isMatched() && !match.destClasses.isEmpty()) { + classes.addAll(match.destClasses); } } return classes; } - public List getUnmatchedDestClasses() { - return new ArrayList(m_unmatchedDestClasses); + public ClassIdentifier getSourceIdentifier() { + return m_sourceClasses.getIdentifier(); } - public Map>> getIndex() { - Map>> conversion = Maps.newHashMap(); - for (Map.Entry entry : getUniqueMatches().entrySet()) { - conversion.put( - entry.getKey().getClassEntry().getName(), - new AbstractMap.SimpleEntry>(entry.getKey(), Arrays.asList(entry.getValue())) - ); - } - for (Map.Entry,List> entry : getAmbiguousMatches().entrySet()) { - for (ClassIdentity sourceClass : entry.getKey()) { - conversion.put( - sourceClass.getClassEntry().getName(), - new AbstractMap.SimpleEntry>(sourceClass, entry.getValue()) - ); - } - } - for (ClassIdentity sourceClass : getUnmatchedSourceClasses()) { - conversion.put( - sourceClass.getClassEntry().getName(), - new AbstractMap.SimpleEntry>(sourceClass, getUnmatchedDestClasses()) - ); - } - return conversion; + public ClassIdentifier getDestIdentifier() { + return m_destClasses.getIdentifier(); } @Override public String toString() { + + // count the ambiguous classes + int numAmbiguousSource = 0; + int numAmbiguousDest = 0; + for (ClassMatch match : ambiguousMatches()) { + numAmbiguousSource += match.sourceClasses.size(); + numAmbiguousDest += match.destClasses.size(); + } + StringBuilder buf = new StringBuilder(); - buf.append(String.format("%12s%8s%8s\n", "", "Source", "Dest")); - buf.append(String.format("%12s%8d%8d\n", "Classes", getSourceClasses().size(), getDestClasses().size())); - buf.append(String.format("%12s%8d%8d\n", "Unique", getUniqueMatches().size(), getUniqueMatches().size())); - buf.append(String.format("%12s%8d%8d\n", "Ambiguous", getNumAmbiguousSourceMatches(), getNumAmbiguousDestMatches())); - buf.append(String.format("%12s%8d%8d\n", "Unmatched", getUnmatchedSourceClasses().size(), getUnmatchedDestClasses().size())); + buf.append(String.format("%20s%8s%8s\n", "", "Source", "Dest")); + buf.append(String.format("%20s%8d%8d\n", "Classes", sourceClasses().size(), destClasses().size())); + buf.append(String.format("%20s%8d%8d\n", "Uniquely matched", uniqueMatches().size(), uniqueMatches().size())); + buf.append(String.format("%20s%8d%8d\n", "Ambiguously matched", numAmbiguousSource, numAmbiguousDest)); + buf.append(String.format("%20s%8d%8d\n", "Unmatched", unmatchedSourceClasses().size(), unmatchedDestClasses().size())); return buf.toString(); } } diff --git a/src/cuchaz/enigma/convert/ClassNamer.java b/src/cuchaz/enigma/convert/ClassNamer.java index 1b6e81c8..6423a189 100644 --- a/src/cuchaz/enigma/convert/ClassNamer.java +++ b/src/cuchaz/enigma/convert/ClassNamer.java @@ -15,6 +15,8 @@ import java.util.Map; import com.google.common.collect.BiMap; import com.google.common.collect.Maps; +import cuchaz.enigma.mapping.ClassEntry; + public class ClassNamer { public interface SidedClassNamer { @@ -24,15 +26,15 @@ public class ClassNamer { private Map m_sourceNames; private Map m_destNames; - public ClassNamer(BiMap mappings) { + public ClassNamer(BiMap mappings) { // convert the identity mappings to name maps m_sourceNames = Maps.newHashMap(); m_destNames = Maps.newHashMap(); int i = 0; - for (Map.Entry entry : mappings.entrySet()) { + for (Map.Entry entry : mappings.entrySet()) { String name = String.format("M%04d", i++); - m_sourceNames.put(entry.getKey().getClassEntry().getName(), name); - m_destNames.put(entry.getValue().getClassEntry().getName(), name); + m_sourceNames.put(entry.getKey().getName(), name); + m_destNames.put(entry.getValue().getName(), name); } } diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index d9850324..d3b6e771 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -27,6 +27,7 @@ public class Translator { public Translator() { m_direction = null; m_classes = Maps.newHashMap(); + m_index = new TranslationIndex(); } public Translator(TranslationDirection direction, Map classes, TranslationIndex index) { -- cgit v1.2.3 From 6a4bc21a63513bf5dd1ddcf72e19e6d503697342 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 28 Feb 2015 19:45:52 -0500 Subject: switch to better-maintained version of JSyntaxPane --- build.py | 2 +- src/cuchaz/enigma/gui/Gui.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build.py b/build.py index 1f80d504..c2caa299 100644 --- a/build.py +++ b/build.py @@ -31,7 +31,7 @@ LibDeps = [ ssjb.ivy.Dep("org.bitbucket.mstrobel:procyon-decompiler:0.5.28-enigma") ] StandaloneDeps = LibDeps + [ - ssjb.ivy.Dep("de.sciss:jsyntaxpane:1.0.0") + ssjb.ivy.Dep("de.sciss:syntaxpane:1.1.4") ] ProguardDep = ssjb.ivy.Dep("net.sf.proguard:proguard-base:5.1") TestDeps = [ diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 00cff595..a214243f 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -65,8 +65,6 @@ import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; -import jsyntaxpane.DefaultSyntaxKit; - import com.google.common.collect.Lists; import cuchaz.enigma.Constants; @@ -90,6 +88,7 @@ import cuchaz.enigma.mapping.IllegalNameException; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Signature; +import de.sciss.syntaxpane.DefaultSyntaxKit; public class Gui { @@ -262,7 +261,7 @@ public class Gui { // turn off token highlighting (it's wrong most of the time anyway...) DefaultSyntaxKit kit = (DefaultSyntaxKit)m_editor.getEditorKit(); - kit.toggleComponent(m_editor, "jsyntaxpane.components.TokenMarker"); + kit.toggleComponent(m_editor, "de.sciss.syntaxpane.components.TokenMarker"); // init editor popup menu JPopupMenu popupMenu = new JPopupMenu(); -- cgit v1.2.3 From 88671184e20b3ad3791125cf96c83ca048cb2861 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 28 Feb 2015 23:36:47 -0500 Subject: refactor converter a bit for upcoming convert gui --- src/cuchaz/enigma/ConvertMain.java | 70 +++++ src/cuchaz/enigma/convert/ClassForest.java | 4 - src/cuchaz/enigma/convert/ClassMatcher.java | 356 ----------------------- src/cuchaz/enigma/convert/ClassMatching.java | 8 - src/cuchaz/enigma/convert/MappingsConverter.java | 180 ++++++++++++ src/cuchaz/enigma/convert/Matches.java | 89 ++++++ src/cuchaz/enigma/convert/MatchesReader.java | 45 +++ src/cuchaz/enigma/convert/MatchesWriter.java | 41 +++ src/cuchaz/enigma/gui/MatchingGui.java | 140 +++++++++ 9 files changed, 565 insertions(+), 368 deletions(-) create mode 100644 src/cuchaz/enigma/ConvertMain.java delete mode 100644 src/cuchaz/enigma/convert/ClassMatcher.java create mode 100644 src/cuchaz/enigma/convert/MappingsConverter.java create mode 100644 src/cuchaz/enigma/convert/Matches.java create mode 100644 src/cuchaz/enigma/convert/MatchesReader.java create mode 100644 src/cuchaz/enigma/convert/MatchesWriter.java create mode 100644 src/cuchaz/enigma/gui/MatchingGui.java diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java new file mode 100644 index 00000000..7ba47617 --- /dev/null +++ b/src/cuchaz/enigma/ConvertMain.java @@ -0,0 +1,70 @@ +package cuchaz.enigma; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.jar.JarFile; + +import cuchaz.enigma.convert.MappingsConverter; +import cuchaz.enigma.convert.Matches; +import cuchaz.enigma.convert.MatchesReader; +import cuchaz.enigma.convert.MatchesWriter; +import cuchaz.enigma.gui.MatchingGui; +import cuchaz.enigma.mapping.MappingParseException; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsReader; + + +public class ConvertMain { + + public static void main(String[] args) + throws IOException, MappingParseException { + + // init files + File home = new File(System.getProperty("user.home")); + JarFile sourceJar = new JarFile(new File(home, ".minecraft/versions/1.8/1.8.jar")); + JarFile destJar = new JarFile(new File(home, ".minecraft/versions/1.8.3/1.8.3.jar")); + File inMappingsFile = new File("../Enigma Mappings/1.8.mappings"); + File outMappingsFile = new File("../Enigma Mappings/1.8.3.mappings"); + Mappings mappings = new MappingsReader().read(new FileReader(inMappingsFile)); + File matchingFile = new File(inMappingsFile.getName() + ".matching"); + + //computeMatches(matchingFile, sourceJar, destJar, mappings); + editMatches(matchingFile, sourceJar, destJar, mappings); + //convertMappings(outMappingsFile, mappings, matchingFile); + + /* TODO + // write out the converted mappings + FileWriter writer = new FileWriter(outMappingsFile); + new MappingsWriter().write(writer, mappings); + writer.close(); + System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); + */ + } + + private static void computeMatches(File matchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) + throws IOException { + Matches matches = MappingsConverter.computeMatches(sourceJar, destJar, mappings); + MatchesWriter.write(matches, matchingFile); + System.out.println("Wrote:\n\t" + matchingFile.getAbsolutePath()); + } + + private static void editMatches(File matchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) + throws IOException { + System.out.println("Reading matches..."); + Matches matches = MatchesReader.read(matchingFile); + System.out.println("Indexing source jar..."); + Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); + sourceDeobfuscator.setMappings(mappings); + System.out.println("Indexing dest jar..."); + Deobfuscator destDeobfuscator = new Deobfuscator(destJar); + System.out.println("Starting GUI..."); + new MatchingGui(matches, sourceDeobfuscator, destDeobfuscator); + } + + private static void convertMappings(File outMappingsFile, Mappings mappings, File matchingFile) + throws IOException { + Matches matches = MatchesReader.read(matchingFile); + MappingsConverter.convertMappings(mappings, matches.getUniqueMatches()); + } +} diff --git a/src/cuchaz/enigma/convert/ClassForest.java b/src/cuchaz/enigma/convert/ClassForest.java index e113eeb9..36731393 100644 --- a/src/cuchaz/enigma/convert/ClassForest.java +++ b/src/cuchaz/enigma/convert/ClassForest.java @@ -18,10 +18,6 @@ public class ClassForest { m_forest = HashMultimap.create(); } - public ClassIdentifier getIdentifier() { - return m_identifier; - } - public void addAll(Iterable entries) { for (ClassEntry entry : entries) { add(entry); diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java deleted file mode 100644 index f43f5b20..00000000 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ /dev/null @@ -1,356 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.convert; - -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.jar.JarFile; - -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; - -import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.ClassMapping; -import cuchaz.enigma.mapping.MappingParseException; -import cuchaz.enigma.mapping.Mappings; -import cuchaz.enigma.mapping.MappingsReader; -import cuchaz.enigma.mapping.MappingsWriter; -import cuchaz.enigma.mapping.MethodEntry; -import cuchaz.enigma.mapping.MethodMapping; - -public class ClassMatcher { - - public static void main(String[] args) - throws IOException, MappingParseException { - - // setup files - File home = new File(System.getProperty("user.home")); - JarFile sourceJar = new JarFile(new File(home, ".minecraft/versions/1.8/1.8.jar")); - JarFile destJar = new JarFile(new File(home, ".minecraft/versions/1.8.3/1.8.3.jar")); - File inMappingsFile = new File("../Enigma Mappings/1.8.mappings"); - File outMappingsFile = new File("../Enigma Mappings/1.8.3.mappings"); - - // define a matching to use when the automated system cannot find a match - Map fallbackMatching = Maps.newHashMap(); - /* - fallbackMatching.put("none/ayb", "none/ayf"); - fallbackMatching.put("none/ayd", "none/ayd"); - fallbackMatching.put("none/bgk", "unknown/bgk"); - */ - - // do the conversion - Mappings mappings = new MappingsReader().read(new FileReader(inMappingsFile)); - convertMappings(sourceJar, destJar, mappings, fallbackMatching); - - // write out the converted mappings - FileWriter writer = new FileWriter(outMappingsFile); - new MappingsWriter().write(writer, mappings); - writer.close(); - System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); - } - - private static void convertMappings(JarFile sourceJar, JarFile destJar, Mappings mappings, Map fallbackMatching) { - - // index jars - System.out.println("Indexing source jar..."); - JarIndex sourceIndex = new JarIndex(); - sourceIndex.indexJar(sourceJar, false); - System.out.println("Indexing dest jar..."); - JarIndex destIndex = new JarIndex(); - destIndex.indexJar(destJar, false); - - // compute the matching - ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex); - - // get all the obf class names used in the mappings - Set usedClasses = Sets.newHashSet(); - for (String className : mappings.getAllObfClassNames()) { - usedClasses.add(new ClassEntry(className)); - } - System.out.println("Mappings reference " + usedClasses.size() + " classes"); - - // see what the used classes map to - BiMap uniqueUsedMatches = HashBiMap.create(); - Map ambiguousUsedMatches = Maps.newHashMap(); - Set unmatchedUsedClasses = Sets.newHashSet(); - for (ClassMatch match : matching.matches()) { - Set matchUsedClasses = match.intersectSourceClasses(usedClasses); - if (matchUsedClasses.isEmpty()) { - continue; - } - - // classify the match - if (!match.isMatched()) { - // unmatched - unmatchedUsedClasses.addAll(matchUsedClasses); - } else { - if (match.isAmbiguous()) { - // ambiguously matched - for (ClassEntry matchUsedClass : matchUsedClasses) { - ambiguousUsedMatches.put(matchUsedClass, match); - } - } else { - // uniquely matched - uniqueUsedMatches.put(match.getUniqueSource(), match.getUniqueDest()); - } - } - } - - // get unmatched dest classes - Set unmatchedDestClasses = Sets.newHashSet(); - for (ClassMatch match : matching.matches()) { - if (!match.isMatched()) { - unmatchedDestClasses.addAll(match.destClasses); - } - } - - // warn about the ambiguous used matches - if (ambiguousUsedMatches.size() > 0) { - System.out.println(String.format("%d source classes have ambiguous mappings", ambiguousUsedMatches.size())); - List ambiguousMatchesList = Lists.newArrayList(Sets.newHashSet(ambiguousUsedMatches.values())); - Collections.sort(ambiguousMatchesList, new Comparator() { - @Override - public int compare(ClassMatch a, ClassMatch b) { - String aName = a.sourceClasses.iterator().next().getName(); - String bName = b.sourceClasses.iterator().next().getName(); - return aName.compareTo(bName); - } - }); - for (ClassMatch match : ambiguousMatchesList) { - System.out.println("Ambiguous matching:"); - System.out.println("\tSource: " + getClassNames(match.sourceClasses)); - System.out.println("\tDest: " + getClassNames(match.destClasses)); - } - } - - // warn about unmatched used classes - for (ClassEntry unmatchedUsedClass : unmatchedUsedClasses) { - System.out.println("No exact match for source class " + unmatchedUsedClass.getClassEntry()); - - // rank all the unmatched dest classes against the used class - ClassIdentity sourceIdentity = matching.getSourceIdentifier().identify(unmatchedUsedClass); - Multimap scoredDestClasses = ArrayListMultimap.create(); - for (ClassEntry unmatchedDestClass : unmatchedDestClasses) { - ClassIdentity destIdentity = matching.getDestIdentifier().identify(unmatchedDestClass); - scoredDestClasses.put(sourceIdentity.getMatchScore(destIdentity), unmatchedDestClass); - } - - List scores = new ArrayList(scoredDestClasses.keySet()); - Collections.sort(scores, Collections.reverseOrder()); - printScoredMatches(sourceIdentity.getMaxMatchScore(), scores, scoredDestClasses); - - /* TODO: re-enable auto-pick logic - // does the best match have a non-zero score and the same name? - int bestScore = scores.get(0); - Collection bestMatches = scoredMatches.get(bestScore); - if (bestScore > 0 && bestMatches.size() == 1) { - ClassIdentity bestMatch = bestMatches.iterator().next(); - if (bestMatch.getClassEntry().equals(sourceClass.getClassEntry())) { - // use it - System.out.println("\tAutomatically choosing likely match: " + bestMatch.getClassEntry().getName()); - destClasses.clear(); - destClasses.add(bestMatch); - } - } - */ - } - - // bail if there were unmatched classes - if (!unmatchedUsedClasses.isEmpty()) { - throw new Error("There were " + unmatchedUsedClasses.size() + " unmatched classes!"); - } - - // sort the changes so classes are renamed in the correct order - // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b - BiMap unsortedChanges = HashBiMap.create(uniqueUsedMatches); - LinkedHashMap sortedChanges = Maps.newLinkedHashMap(); - int numChangesLeft = unsortedChanges.size(); - while (!unsortedChanges.isEmpty()) { - Iterator> iter = unsortedChanges.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry change = iter.next(); - if (unsortedChanges.containsKey(change.getValue())) { - sortedChanges.put(change.getKey(), change.getValue()); - iter.remove(); - } - } - - // did we remove any changes? - if (numChangesLeft - unsortedChanges.size() > 0) { - // keep going - numChangesLeft = unsortedChanges.size(); - } else { - // can't sort anymore. There must be a loop - break; - } - } - if (!unsortedChanges.isEmpty()) { - throw new Error(String.format("Unable to sort %d/%d class changes!", unsortedChanges.size(), uniqueUsedMatches.size())); - } - - // convert the mappings in the correct class order - for (Map.Entry entry : sortedChanges.entrySet()) { - mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); - } - - // check the method matches - System.out.println("Checking methods..."); - for (ClassMapping classMapping : mappings.classes()) { - ClassEntry classEntry = new ClassEntry(classMapping.getObfFullName()); - for (MethodMapping methodMapping : classMapping.methods()) { - - // skip constructors - if (methodMapping.getObfName().equals("")) { - continue; - } - - MethodEntry methodEntry = new MethodEntry( - classEntry, - methodMapping.getObfName(), - methodMapping.getObfSignature() - ); - if (!destIndex.containsObfBehavior(methodEntry)) { - System.err.println("WARNING: method doesn't match: " + methodEntry); - - /* TODO: show methods if needed - // show the available methods - System.err.println("\tAvailable dest methods:"); - CtClass c = destLoader.loadClass(classMapping.getObfFullName()); - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); - } - - System.err.println("\tAvailable source methods:"); - c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfFullName())); - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); - } - */ - } - } - } - - System.out.println("Done!"); - } - - public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex) { - - System.out.println("Iteratively matching classes..."); - - ClassMatching lastMatching = null; - int round = 0; - SidedClassNamer sourceNamer = null; - SidedClassNamer destNamer = null; - for (boolean useReferences : Arrays.asList(false, true)) { - - int numUniqueMatchesLastTime = 0; - if (lastMatching != null) { - numUniqueMatchesLastTime = lastMatching.uniqueMatches().size(); - } - - while (true) { - - System.out.println("Round " + (++round) + " ..."); - - // init the matching with identity settings - ClassMatching matching = new ClassMatching( - new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences), - new ClassIdentifier(destJar, destIndex, destNamer, useReferences) - ); - - if (lastMatching == null) { - // search all classes - matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries()); - } else { - // we already know about these matches - matching.addKnownMatches(lastMatching.uniqueMatches()); - - // search unmatched and ambiguously-matched classes - matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses()); - for (ClassMatch match : lastMatching.ambiguousMatches()) { - matching.match(match.sourceClasses, match.destClasses); - } - } - System.out.println(matching); - BiMap uniqueMatches = matching.uniqueMatches(); - - // did we match anything new this time? - if (uniqueMatches.size() > numUniqueMatchesLastTime) { - numUniqueMatchesLastTime = uniqueMatches.size(); - lastMatching = matching; - } else { - break; - } - - // update the namers - ClassNamer namer = new ClassNamer(uniqueMatches); - sourceNamer = namer.getSourceNamer(); - destNamer = namer.getDestNamer(); - } - } - - return lastMatching; - } - - private static void printScoredMatches(int maxScore, List scores, Multimap scoredMatches) { - int numScoredMatchesShown = 0; - for (int score : scores) { - for (ClassEntry classEntry : scoredMatches.get(score)) { - System.out.println(String.format("\tScore: %3d %3.0f%% %s", - score, 100.0 * score / maxScore, classEntry.getName() - )); - if (numScoredMatchesShown++ > 10) { - return; - } - } - } - } - - private static List getClassNames(Collection classes) { - List out = Lists.newArrayList(); - for (ClassEntry c : classes) { - out.add(c.getName()); - } - Collections.sort(out); - return out; - } - - /* DEBUG - private static String decompile(TranslatingTypeLoader loader, ClassEntry classEntry) { - PlainTextOutput output = new PlainTextOutput(); - DecompilerSettings settings = DecompilerSettings.javaDefaults(); - settings.setForceExplicitImports(true); - settings.setShowSyntheticMembers(true); - settings.setTypeLoader(loader); - Decompiler.decompile(classEntry.getName(), output, settings); - return output.toString(); - } - */ -} diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java index b94fd8bd..9f931300 100644 --- a/src/cuchaz/enigma/convert/ClassMatching.java +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -125,14 +125,6 @@ public class ClassMatching { return classes; } - public ClassIdentifier getSourceIdentifier() { - return m_sourceClasses.getIdentifier(); - } - - public ClassIdentifier getDestIdentifier() { - return m_destClasses.getIdentifier(); - } - @Override public String toString() { diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java new file mode 100644 index 00000000..d0f9382a --- /dev/null +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.convert; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.jar.JarFile; + +import com.google.common.collect.BiMap; +import com.google.common.collect.Maps; + +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Mappings; + +public class MappingsConverter { + + public static Matches computeMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { + + // index jars + System.out.println("Indexing source jar..."); + JarIndex sourceIndex = new JarIndex(); + sourceIndex.indexJar(sourceJar, false); + System.out.println("Indexing dest jar..."); + JarIndex destIndex = new JarIndex(); + destIndex.indexJar(destJar, false); + + // compute the matching + ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex); + return new Matches(matching.matches()); + } + + public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex) { + + System.out.println("Iteratively matching classes"); + + ClassMatching lastMatching = null; + int round = 0; + SidedClassNamer sourceNamer = null; + SidedClassNamer destNamer = null; + for (boolean useReferences : Arrays.asList(false, true)) { + + int numUniqueMatchesLastTime = 0; + if (lastMatching != null) { + numUniqueMatchesLastTime = lastMatching.uniqueMatches().size(); + } + + while (true) { + + System.out.println("Round " + (++round) + "..."); + + // init the matching with identity settings + ClassMatching matching = new ClassMatching( + new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences), + new ClassIdentifier(destJar, destIndex, destNamer, useReferences) + ); + + if (lastMatching == null) { + // search all classes + matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries()); + } else { + // we already know about these matches + matching.addKnownMatches(lastMatching.uniqueMatches()); + + // search unmatched and ambiguously-matched classes + matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses()); + for (ClassMatch match : lastMatching.ambiguousMatches()) { + matching.match(match.sourceClasses, match.destClasses); + } + } + System.out.println(matching); + BiMap uniqueMatches = matching.uniqueMatches(); + + // did we match anything new this time? + if (uniqueMatches.size() > numUniqueMatchesLastTime) { + numUniqueMatchesLastTime = uniqueMatches.size(); + lastMatching = matching; + } else { + break; + } + + // update the namers + ClassNamer namer = new ClassNamer(uniqueMatches); + sourceNamer = namer.getSourceNamer(); + destNamer = namer.getDestNamer(); + } + } + + return lastMatching; + } + + public static void convertMappings(Mappings mappings, BiMap changes) { + + // sort the changes so classes are renamed in the correct order + // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b + LinkedHashMap sortedChanges = Maps.newLinkedHashMap(); + int numChangesLeft = changes.size(); + while (!changes.isEmpty()) { + Iterator> iter = changes.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry change = iter.next(); + if (changes.containsKey(change.getValue())) { + sortedChanges.put(change.getKey(), change.getValue()); + iter.remove(); + } + } + + // did we remove any changes? + if (numChangesLeft - changes.size() > 0) { + // keep going + numChangesLeft = changes.size(); + } else { + // can't sort anymore. There must be a loop + break; + } + } + if (!changes.isEmpty()) { + throw new Error("Unable to sort class changes! There must be a cycle."); + } + + // convert the mappings in the correct class order + for (Map.Entry entry : sortedChanges.entrySet()) { + mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); + } + } + + /* TODO: after we get a mapping, check to see that the other entries match + public static void checkMethods() { + + // check the method matches + System.out.println("Checking methods..."); + for (ClassMapping classMapping : mappings.classes()) { + ClassEntry classEntry = new ClassEntry(classMapping.getObfFullName()); + for (MethodMapping methodMapping : classMapping.methods()) { + + // skip constructors + if (methodMapping.getObfName().equals("")) { + continue; + } + + MethodEntry methodEntry = new MethodEntry( + classEntry, + methodMapping.getObfName(), + methodMapping.getObfSignature() + ); + if (!destIndex.containsObfBehavior(methodEntry)) { + System.err.println("WARNING: method doesn't match: " + methodEntry); + + // TODO: show methods if needed + // show the available methods + System.err.println("\tAvailable dest methods:"); + CtClass c = destLoader.loadClass(classMapping.getObfFullName()); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); + } + + System.err.println("\tAvailable source methods:"); + c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfFullName())); + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); + } + } + } + } + + System.out.println("Done!"); + } + */ +} diff --git a/src/cuchaz/enigma/convert/Matches.java b/src/cuchaz/enigma/convert/Matches.java new file mode 100644 index 00000000..75ecc2a8 --- /dev/null +++ b/src/cuchaz/enigma/convert/Matches.java @@ -0,0 +1,89 @@ +package cuchaz.enigma.convert; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ClassEntry; + + +public class Matches implements Iterable { + + Collection m_matches; + BiMap m_uniqueMatches; + Map m_ambiguousMatchesBySource; + Map m_ambiguousMatchesByDest; + Set m_unmatchedSourceClasses; + Set m_unmatchedDestClasses; + + public Matches() { + this(new ArrayList()); + } + + public Matches(Collection matches) { + m_matches = matches; + m_uniqueMatches = HashBiMap.create(); + m_ambiguousMatchesBySource = Maps.newHashMap(); + m_ambiguousMatchesByDest = Maps.newHashMap(); + m_unmatchedSourceClasses = Sets.newHashSet(); + m_unmatchedDestClasses = Sets.newHashSet(); + + for (ClassMatch match : matches) { + indexMatch(match); + } + } + + public void add(ClassMatch match) { + m_matches.add(match); + indexMatch(match); + } + + public int size() { + return m_matches.size(); + } + + @Override + public Iterator iterator() { + return m_matches.iterator(); + } + + private void indexMatch(ClassMatch match) { + if (!match.isMatched()) { + // unmatched + m_unmatchedSourceClasses.addAll(match.sourceClasses); + m_unmatchedDestClasses.addAll(match.destClasses); + } else { + if (match.isAmbiguous()) { + // ambiguously matched + for (ClassEntry entry : match.sourceClasses) { + m_ambiguousMatchesBySource.put(entry, match); + } + for (ClassEntry entry : match.destClasses) { + m_ambiguousMatchesByDest.put(entry, match); + } + } else { + // uniquely matched + m_uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); + } + } + } + + public BiMap getUniqueMatches() { + return m_uniqueMatches; + } + + public Set getUnmatchedSourceClasses() { + return m_unmatchedSourceClasses; + } + + public Set getUnmatchedDestClasses() { + return m_unmatchedDestClasses; + } +} diff --git a/src/cuchaz/enigma/convert/MatchesReader.java b/src/cuchaz/enigma/convert/MatchesReader.java new file mode 100644 index 00000000..808f8d0a --- /dev/null +++ b/src/cuchaz/enigma/convert/MatchesReader.java @@ -0,0 +1,45 @@ +package cuchaz.enigma.convert; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +import com.beust.jcommander.internal.Lists; + +import cuchaz.enigma.mapping.ClassEntry; + + +public class MatchesReader { + + public static Matches read(File file) + throws IOException { + try (BufferedReader in = new BufferedReader(new FileReader(file))) { + Matches matches = new Matches(); + String line = null; + while ((line = in.readLine()) != null) { + matches.add(readMatch(line)); + } + return matches; + } + } + + private static ClassMatch readMatch(String line) + throws IOException { + String[] sides = line.split(":", 2); + return new ClassMatch(readClasses(sides[0]), readClasses(sides[1])); + } + + private static Collection readClasses(String in) { + List entries = Lists.newArrayList(); + for (String className : in.split(",")) { + className = className.trim(); + if (className.length() > 0) { + entries.add(new ClassEntry(className)); + } + } + return entries; + } +} diff --git a/src/cuchaz/enigma/convert/MatchesWriter.java b/src/cuchaz/enigma/convert/MatchesWriter.java new file mode 100644 index 00000000..49ffb6d7 --- /dev/null +++ b/src/cuchaz/enigma/convert/MatchesWriter.java @@ -0,0 +1,41 @@ +package cuchaz.enigma.convert; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import cuchaz.enigma.mapping.ClassEntry; + + +public class MatchesWriter { + + public static void write(Matches matches, File file) + throws IOException { + try (FileWriter out = new FileWriter(file)) { + for (ClassMatch match : matches) { + writeMatch(out, match); + } + } + } + + private static void writeMatch(FileWriter out, ClassMatch match) + throws IOException { + writeClasses(out, match.sourceClasses); + out.write(":"); + writeClasses(out, match.destClasses); + out.write("\n"); + } + + private static void writeClasses(FileWriter out, Iterable classes) + throws IOException { + boolean isFirst = true; + for (ClassEntry entry : classes) { + if (isFirst) { + isFirst = false; + } else { + out.write(","); + } + out.write(entry.toString()); + } + } +} diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java new file mode 100644 index 00000000..53c767ac --- /dev/null +++ b/src/cuchaz/enigma/gui/MatchingGui.java @@ -0,0 +1,140 @@ +package cuchaz.enigma.gui; + +import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.convert.Matches; + + +public class MatchingGui { + + public MatchingGui(Matches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + // TODO Auto-generated constructor stub + } + + + /* TODO: see if we can use any of this here + public static doTheThings() { + + // get all the obf class names used in the mappings + Set usedClasses = Sets.newHashSet(); + for (String className : mappings.getAllObfClassNames()) { + usedClasses.add(new ClassEntry(className)); + } + System.out.println(String.format("Mappings reference %d/%d classes", + usedClasses.size(), sourceIndex.getObfClassEntries().size() + )); + + // get the used matches + Collection matches = matching.matches(); + Matches usedMatches = new Matches(); + for (ClassMatch match : matching.matches()) { + if (!match.intersectSourceClasses(usedClasses).isEmpty()) { + usedMatches.add(match); + } + } + System.out.println(String.format("Mappings reference %d/%d match groups", + usedMatches.size(), matches.size() + )); + + // see what the used classes map to + BiMap uniqueUsedMatches = HashBiMap.create(); + Map ambiguousUsedMatches = Maps.newHashMap(); + Set unmatchedUsedClasses = Sets.newHashSet(); + for (ClassMatch match : matching.matches()) { + Set matchUsedClasses = match.intersectSourceClasses(usedClasses); + if (matchUsedClasses.isEmpty()) { + continue; + } + + usedMatches.add(match); + + // classify the match + if (!match.isMatched()) { + // unmatched + unmatchedUsedClasses.addAll(matchUsedClasses); + } else { + if (match.isAmbiguous()) { + // ambiguously matched + for (ClassEntry matchUsedClass : matchUsedClasses) { + ambiguousUsedMatches.put(matchUsedClass, match); + } + } else { + // uniquely matched + uniqueUsedMatches.put(match.getUniqueSource(), match.getUniqueDest()); + } + } + } + + // get unmatched dest classes + Set unmatchedDestClasses = Sets.newHashSet(); + for (ClassMatch match : matching.matches()) { + if (!match.isMatched()) { + unmatchedDestClasses.addAll(match.destClasses); + } + } + + // warn about the ambiguous used matches + if (ambiguousUsedMatches.size() > 0) { + System.out.println(String.format("%d source classes have ambiguous mappings", ambiguousUsedMatches.size())); + List ambiguousMatchesList = Lists.newArrayList(Sets.newHashSet(ambiguousUsedMatches.values())); + Collections.sort(ambiguousMatchesList, new Comparator() { + @Override + public int compare(ClassMatch a, ClassMatch b) { + String aName = a.sourceClasses.iterator().next().getName(); + String bName = b.sourceClasses.iterator().next().getName(); + return aName.compareTo(bName); + } + }); + for (ClassMatch match : ambiguousMatchesList) { + System.out.println("Ambiguous matching:"); + System.out.println("\tSource: " + getClassNames(match.sourceClasses)); + System.out.println("\tDest: " + getClassNames(match.destClasses)); + } + } + + // warn about unmatched used classes + for (ClassEntry unmatchedUsedClass : unmatchedUsedClasses) { + System.out.println("No exact match for source class " + unmatchedUsedClass.getClassEntry()); + + // rank all the unmatched dest classes against the used class + ClassIdentity sourceIdentity = matching.getSourceIdentifier().identify(unmatchedUsedClass); + Multimap scoredDestClasses = ArrayListMultimap.create(); + for (ClassEntry unmatchedDestClass : unmatchedDestClasses) { + ClassIdentity destIdentity = matching.getDestIdentifier().identify(unmatchedDestClass); + scoredDestClasses.put(sourceIdentity.getMatchScore(destIdentity), unmatchedDestClass); + } + + List scores = new ArrayList(scoredDestClasses.keySet()); + Collections.sort(scores, Collections.reverseOrder()); + printScoredMatches(sourceIdentity.getMaxMatchScore(), scores, scoredDestClasses); + } + + // bail if there were unmatched classes + if (!unmatchedUsedClasses.isEmpty()) { + throw new Error("There were " + unmatchedUsedClasses.size() + " unmatched classes!"); + } + } + + private static void printScoredMatches(int maxScore, List scores, Multimap scoredMatches) { + int numScoredMatchesShown = 0; + for (int score : scores) { + for (ClassEntry classEntry : scoredMatches.get(score)) { + System.out.println(String.format("\tScore: %3d %3.0f%% %s", + score, 100.0 * score / maxScore, classEntry.getName() + )); + if (numScoredMatchesShown++ > 10) { + return; + } + } + } + } + + private static List getClassNames(Collection classes) { + List out = Lists.newArrayList(); + for (ClassEntry c : classes) { + out.add(c.getName()); + } + Collections.sort(out); + return out; + } + */ +} -- cgit v1.2.3 From 54d17da93c6708e54c296d63783a60f1c024797b Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 2 Mar 2015 01:01:51 -0500 Subject: finished most of the matching gui --- src/cuchaz/enigma/ConvertMain.java | 1 + src/cuchaz/enigma/Deobfuscator.java | 4 + src/cuchaz/enigma/convert/MappingsConverter.java | 61 ++++ src/cuchaz/enigma/convert/Matches.java | 26 ++ src/cuchaz/enigma/gui/ClassSelector.java | 19 +- src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 3 + src/cuchaz/enigma/gui/DecoratedClassEntry.java | 20 ++ src/cuchaz/enigma/gui/MatchingGui.java | 413 +++++++++++++++++----- 8 files changed, 446 insertions(+), 101 deletions(-) create mode 100644 src/cuchaz/enigma/gui/DecoratedClassEntry.java diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index 7ba47617..4fc58e87 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -58,6 +58,7 @@ public class ConvertMain { sourceDeobfuscator.setMappings(mappings); System.out.println("Indexing dest jar..."); Deobfuscator destDeobfuscator = new Deobfuscator(destJar); + destDeobfuscator.setMappings(MappingsConverter.newMappings(matches, mappings, sourceDeobfuscator, destDeobfuscator)); System.out.println("Starting GUI..."); new MatchingGui(matches, sourceDeobfuscator, destDeobfuscator); } diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 0b7808da..e5d0e3d9 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -99,6 +99,10 @@ public class Deobfuscator { setMappings(new Mappings()); } + public JarFile getJar() { + return m_jar; + } + public String getJarName() { return m_jar.getName(); } diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index d0f9382a..aa067d4d 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -11,17 +11,25 @@ package cuchaz.enigma.convert; import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.jar.JarFile; +import com.beust.jcommander.internal.Lists; import com.google.common.collect.BiMap; +import com.google.common.collect.HashMultimap; import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; import cuchaz.enigma.mapping.Mappings; public class MappingsConverter { @@ -100,6 +108,59 @@ public class MappingsConverter { return lastMatching; } + public static Mappings newMappings(Matches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + + // sort the unique matches by size of inner class chain + Multimap> matchesByDestChainSize = HashMultimap.create(); + for (Entry match : matches.getUniqueMatches().entrySet()) { + int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size(); + matchesByDestChainSize.put(chainSize, match); + } + + // build the mappings (in order of small-to-large inner chains) + Mappings newMappings = new Mappings(); + List chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet()); + Collections.sort(chainSizes); + for (int chainSize : chainSizes) { + for (Entry match : matchesByDestChainSize.get(chainSize)) { + + // get class info + ClassEntry sourceClassEntry = match.getKey(); + ClassEntry deobfClassEntry = sourceDeobfuscator.deobfuscateEntry(sourceClassEntry); + ClassEntry destClassEntry = match.getValue(); + List destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(destClassEntry); + + // find out where to make the dest class mapping + if (destClassChain.size() == 1) { + // not an inner class, add directly to mappings + newMappings.addClassMapping(new ClassMapping(destClassEntry.getName(), deobfClassEntry.getName())); + } else { + // inner class, find the outer class mapping + ClassMapping destMapping = null; + for (int i=0; i changes) { // sort the changes so classes are renamed in the correct order diff --git a/src/cuchaz/enigma/convert/Matches.java b/src/cuchaz/enigma/convert/Matches.java index 75ecc2a8..5faa923c 100644 --- a/src/cuchaz/enigma/convert/Matches.java +++ b/src/cuchaz/enigma/convert/Matches.java @@ -17,6 +17,8 @@ import cuchaz.enigma.mapping.ClassEntry; public class Matches implements Iterable { Collection m_matches; + Map m_matchesBySource; + Map m_matchesByDest; BiMap m_uniqueMatches; Map m_ambiguousMatchesBySource; Map m_ambiguousMatchesByDest; @@ -29,6 +31,8 @@ public class Matches implements Iterable { public Matches(Collection matches) { m_matches = matches; + m_matchesBySource = Maps.newHashMap(); + m_matchesByDest = Maps.newHashMap(); m_uniqueMatches = HashBiMap.create(); m_ambiguousMatchesBySource = Maps.newHashMap(); m_ambiguousMatchesByDest = Maps.newHashMap(); @@ -73,6 +77,12 @@ public class Matches implements Iterable { m_uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); } } + for (ClassEntry entry : match.sourceClasses) { + m_matchesBySource.put(entry, match); + } + for (ClassEntry entry : match.destClasses) { + m_matchesByDest.put(entry, match); + } } public BiMap getUniqueMatches() { @@ -86,4 +96,20 @@ public class Matches implements Iterable { public Set getUnmatchedDestClasses() { return m_unmatchedDestClasses; } + + public Set getAmbiguouslyMatchedSourceClasses() { + return m_ambiguousMatchesBySource.keySet(); + } + + public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { + return m_ambiguousMatchesBySource.get(sourceClass); + } + + public ClassMatch getMatchBySource(ClassEntry sourceClass) { + return m_matchesBySource.get(sourceClass); + } + + public ClassMatch getMatchByDest(ClassEntry destClass) { + return m_matchesByDest.get(destClass); + } } diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java index 654bfbed..e5f550bb 100644 --- a/src/cuchaz/enigma/gui/ClassSelector.java +++ b/src/cuchaz/enigma/gui/ClassSelector.java @@ -41,21 +41,32 @@ public class ClassSelector extends JTree { public static Comparator ObfuscatedClassEntryComparator; public static Comparator DeobfuscatedClassEntryComparator; + private static String getClassEntryDisplayName(ClassEntry entry) { + if (entry instanceof DecoratedClassEntry) { + return ((DecoratedClassEntry)entry).getDecoration() + entry.getName(); + } + return entry.getName(); + } + static { ObfuscatedClassEntryComparator = new Comparator() { @Override public int compare(ClassEntry a, ClassEntry b) { - if (a.getName().length() != b.getName().length()) { - return a.getName().length() - b.getName().length(); + String aname = getClassEntryDisplayName(a); + String bname = getClassEntryDisplayName(b); + if (aname.length() != bname.length()) { + return aname.length() - bname.length(); } - return a.getName().compareTo(b.getName()); + return aname.compareTo(bname); } }; DeobfuscatedClassEntryComparator = new Comparator() { @Override public int compare(ClassEntry a, ClassEntry b) { - return a.getName().compareTo(b.getName()); + String aname = getClassEntryDisplayName(a); + String bname = getClassEntryDisplayName(b); + return aname.compareTo(bname); } }; } diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java index 66e931b4..b3d7b899 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java @@ -30,6 +30,9 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode { @Override public String toString() { + if (m_classEntry instanceof DecoratedClassEntry) { + return ((DecoratedClassEntry)m_classEntry).getDecoration() + m_classEntry.getSimpleName(); + } return m_classEntry.getSimpleName(); } } diff --git a/src/cuchaz/enigma/gui/DecoratedClassEntry.java b/src/cuchaz/enigma/gui/DecoratedClassEntry.java new file mode 100644 index 00000000..dd8b4fa3 --- /dev/null +++ b/src/cuchaz/enigma/gui/DecoratedClassEntry.java @@ -0,0 +1,20 @@ +package cuchaz.enigma.gui; + +import cuchaz.enigma.mapping.ClassEntry; + + +public class DecoratedClassEntry extends ClassEntry { + + private static final long serialVersionUID = -8798725308554217105L; + + private String m_decoration; + + public DecoratedClassEntry(ClassEntry other, String decoration) { + super(other); + m_decoration = decoration; + } + + public String getDecoration() { + return m_decoration; + } +} diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java index 53c767ac..e9dff164 100644 --- a/src/cuchaz/enigma/gui/MatchingGui.java +++ b/src/cuchaz/enigma/gui/MatchingGui.java @@ -1,133 +1,352 @@ package cuchaz.enigma.gui; +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.WindowConstants; + +import com.beust.jcommander.internal.Lists; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + +import cuchaz.enigma.Constants; import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.convert.ClassIdentifier; +import cuchaz.enigma.convert.ClassIdentity; +import cuchaz.enigma.convert.ClassMatch; +import cuchaz.enigma.convert.ClassNamer; import cuchaz.enigma.convert.Matches; +import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; +import cuchaz.enigma.mapping.ClassEntry; +import de.sciss.syntaxpane.DefaultSyntaxKit; public class MatchingGui { - - public MatchingGui(Matches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { - // TODO Auto-generated constructor stub + + private static enum SourceType { + Matched { + + @Override + public Collection getSourceClasses(Matches matches) { + return matches.getUniqueMatches().keySet(); + } + }, + Unmatched { + + @Override + public Collection getSourceClasses(Matches matches) { + return matches.getUnmatchedSourceClasses(); + } + }, + Ambiguous { + + @Override + public Collection getSourceClasses(Matches matches) { + return matches.getAmbiguouslyMatchedSourceClasses(); + } + }; + + public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { + JRadioButton button = new JRadioButton(name(), this == getDefault()); + button.setActionCommand(name()); + button.addActionListener(listener); + group.add(button); + return button; + } + + public abstract Collection getSourceClasses(Matches matches); + + public static SourceType getDefault() { + return values()[0]; + } } + // controls + private JFrame m_frame; + private ClassSelector m_sourceClasses; + private ClassSelector m_destClasses; + private JEditorPane m_sourceReader; + private JEditorPane m_destReader; + private JLabel m_sourceClassLabel; + private JLabel m_destClassLabel; + private JButton m_matchButton; + + private Matches m_matches; + private Deobfuscator m_sourceDeobfuscator; + private Deobfuscator m_destDeobfuscator; + private ClassEntry m_sourceClass; + private ClassEntry m_destClass; - /* TODO: see if we can use any of this here - public static doTheThings() { + public MatchingGui(Matches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { - // get all the obf class names used in the mappings - Set usedClasses = Sets.newHashSet(); - for (String className : mappings.getAllObfClassNames()) { - usedClasses.add(new ClassEntry(className)); - } - System.out.println(String.format("Mappings reference %d/%d classes", - usedClasses.size(), sourceIndex.getObfClassEntries().size() - )); - - // get the used matches - Collection matches = matching.matches(); - Matches usedMatches = new Matches(); - for (ClassMatch match : matching.matches()) { - if (!match.intersectSourceClasses(usedClasses).isEmpty()) { - usedMatches.add(match); + m_matches = matches; + m_sourceDeobfuscator = sourceDeobfuscator; + m_destDeobfuscator = destDeobfuscator; + + // init frame + m_frame = new JFrame(Constants.Name); + final Container pane = m_frame.getContentPane(); + pane.setLayout(new BorderLayout()); + + // init source side + JPanel sourcePanel = new JPanel(); + sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS)); + sourcePanel.setPreferredSize(new Dimension(200, 0)); + pane.add(sourcePanel, BorderLayout.WEST); + sourcePanel.add(new JLabel("Source Classes")); + + // init source type radios + JPanel sourceTypePanel = new JPanel(); + sourcePanel.add(sourceTypePanel); + sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); + ActionListener sourceTypeListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + setSourceType(SourceType.valueOf(event.getActionCommand())); } + }; + ButtonGroup sourceTypeButtons = new ButtonGroup(); + for (SourceType sourceType : SourceType.values()) { + sourceTypePanel.add(sourceType.newRadio(sourceTypeListener, sourceTypeButtons)); } - System.out.println(String.format("Mappings reference %d/%d match groups", - usedMatches.size(), matches.size() - )); - - // see what the used classes map to - BiMap uniqueUsedMatches = HashBiMap.create(); - Map ambiguousUsedMatches = Maps.newHashMap(); - Set unmatchedUsedClasses = Sets.newHashSet(); - for (ClassMatch match : matching.matches()) { - Set matchUsedClasses = match.intersectSourceClasses(usedClasses); - if (matchUsedClasses.isEmpty()) { - continue; + + m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); + m_sourceClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + setSourceClass(classEntry); } - - usedMatches.add(match); + }); + JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); + sourcePanel.add(sourceScroller); + + // init dest side + JPanel destPanel = new JPanel(); + destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS)); + destPanel.setPreferredSize(new Dimension(200, 0)); + pane.add(destPanel, BorderLayout.WEST); + destPanel.add(new JLabel("Destination Classes")); + + m_destClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); + m_destClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + setDestClass(classEntry); + } + }); + JScrollPane destScroller = new JScrollPane(m_destClasses); + destPanel.add(destScroller); + + // init source panels + DefaultSyntaxKit.initKit(); + m_sourceReader = new JEditorPane(); + m_sourceReader.setEditable(false); + m_sourceReader.setContentType("text/java"); + m_destReader = new JEditorPane(); + m_destReader.setEditable(false); + m_destReader.setContentType("text/java"); + + // init all the splits + JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader)); + splitLeft.setResizeWeight(0); // let the right side take all the slack + JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_destReader), destPanel); + splitRight.setResizeWeight(1); // let the left side take all the slack + JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); + splitCenter.setResizeWeight(0.5); // resize 50:50 + pane.add(splitCenter, BorderLayout.CENTER); + splitCenter.resetToPreferredSizes(); + + // init bottom panel + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new FlowLayout()); + + m_sourceClassLabel = new JLabel(); + m_sourceClassLabel.setPreferredSize(new Dimension(300, 0)); + m_destClassLabel = new JLabel(); + m_destClassLabel.setPreferredSize(new Dimension(300, 0)); + + m_matchButton = new JButton(); + m_matchButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + onMatchClick(); + } + }); + m_matchButton.setPreferredSize(new Dimension(140, 24)); + + bottomPanel.add(m_sourceClassLabel); + bottomPanel.add(m_matchButton); + bottomPanel.add(m_destClassLabel); + pane.add(bottomPanel, BorderLayout.SOUTH); + + // show the frame + pane.doLayout(); + m_frame.setSize(1024, 576); + m_frame.setMinimumSize(new Dimension(640, 480)); + m_frame.setVisible(true); + m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + // init state + setSourceType(SourceType.getDefault()); + updateMatchButton(); + } - // classify the match - if (!match.isMatched()) { - // unmatched - unmatchedUsedClasses.addAll(matchUsedClasses); + protected void setSourceType(SourceType val) { + // show the source classes + m_sourceClasses.setClasses(deobfuscateClasses(val.getSourceClasses(m_matches), m_sourceDeobfuscator)); + } + + private Collection deobfuscateClasses(Collection in, Deobfuscator deobfuscator) { + List out = Lists.newArrayList(); + for (ClassEntry entry : in) { + out.add(deobfuscator.deobfuscateEntry(entry)); + } + return out; + } + + protected void setSourceClass(ClassEntry classEntry) { + + // update the current source class + m_sourceClass = classEntry; + m_sourceClassLabel.setText(m_sourceClass != null ? m_sourceClass.getName() : ""); + + if (m_sourceClass != null) { + + // show the dest class(es) + ClassMatch match = m_matches.getMatchBySource(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); + assert(match != null); + if (match.destClasses.isEmpty()) { + m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator)); } else { - if (match.isAmbiguous()) { - // ambiguously matched - for (ClassEntry matchUsedClass : matchUsedClasses) { - ambiguousUsedMatches.put(matchUsedClass, match); - } - } else { - // uniquely matched - uniqueUsedMatches.put(match.getUniqueSource(), match.getUniqueDest()); - } + m_destClasses.setClasses(deobfuscateClasses(match.destClasses, m_destDeobfuscator)); } + m_destClasses.expandRow(0); } - // get unmatched dest classes - Set unmatchedDestClasses = Sets.newHashSet(); - for (ClassMatch match : matching.matches()) { - if (!match.isMatched()) { - unmatchedDestClasses.addAll(match.destClasses); - } + setDestClass(null); + readSource(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); + + updateMatchButton(); + } + + private Collection getLikelyMatches(ClassEntry sourceClass) { + + ClassEntry obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass); + + // set up identifiers + ClassNamer namer = new ClassNamer(m_matches.getUniqueMatches()); + ClassIdentifier sourceIdentifier = new ClassIdentifier( + m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(), + namer.getSourceNamer(), true + ); + ClassIdentifier destIdentifier = new ClassIdentifier( + m_destDeobfuscator.getJar(), m_destDeobfuscator.getJarIndex(), + namer.getDestNamer(), true + ); + + // rank all the unmatched dest classes against the source class + ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); + Multimap scoredDestClasses = ArrayListMultimap.create(); + for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { + ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); + float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) + /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); + scoredDestClasses.put(score, unmatchedDestClass); } - // warn about the ambiguous used matches - if (ambiguousUsedMatches.size() > 0) { - System.out.println(String.format("%d source classes have ambiguous mappings", ambiguousUsedMatches.size())); - List ambiguousMatchesList = Lists.newArrayList(Sets.newHashSet(ambiguousUsedMatches.values())); - Collections.sort(ambiguousMatchesList, new Comparator() { - @Override - public int compare(ClassMatch a, ClassMatch b) { - String aName = a.sourceClasses.iterator().next().getName(); - String bName = b.sourceClasses.iterator().next().getName(); - return aName.compareTo(bName); + // sort by scores + List scores = new ArrayList(scoredDestClasses.keySet()); + Collections.sort(scores, Collections.reverseOrder()); + + // collect the scored classes in order + List scoredClasses = Lists.newArrayList(); + for (float score : scores) { + for (ClassEntry classEntry : scoredDestClasses.get(score)) { + scoredClasses.add(new DecoratedClassEntry(classEntry, String.format("%.0f%% ", score))); + if (scoredClasses.size() > 10) { + return scoredClasses; } - }); - for (ClassMatch match : ambiguousMatchesList) { - System.out.println("Ambiguous matching:"); - System.out.println("\tSource: " + getClassNames(match.sourceClasses)); - System.out.println("\tDest: " + getClassNames(match.destClasses)); } } + return scoredClasses; + } + + protected void setDestClass(ClassEntry classEntry) { - // warn about unmatched used classes - for (ClassEntry unmatchedUsedClass : unmatchedUsedClasses) { - System.out.println("No exact match for source class " + unmatchedUsedClass.getClassEntry()); - - // rank all the unmatched dest classes against the used class - ClassIdentity sourceIdentity = matching.getSourceIdentifier().identify(unmatchedUsedClass); - Multimap scoredDestClasses = ArrayListMultimap.create(); - for (ClassEntry unmatchedDestClass : unmatchedDestClasses) { - ClassIdentity destIdentity = matching.getDestIdentifier().identify(unmatchedDestClass); - scoredDestClasses.put(sourceIdentity.getMatchScore(destIdentity), unmatchedDestClass); - } - - List scores = new ArrayList(scoredDestClasses.keySet()); - Collections.sort(scores, Collections.reverseOrder()); - printScoredMatches(sourceIdentity.getMaxMatchScore(), scores, scoredDestClasses); - } + // update the current source class + m_destClass = classEntry; + m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : ""); + + readSource(m_destClass, m_destDeobfuscator, m_destReader); + + updateMatchButton(); + } + + protected void readSource(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { - // bail if there were unmatched classes - if (!unmatchedUsedClasses.isEmpty()) { - throw new Error("There were " + unmatchedUsedClasses.size() + " unmatched classes!"); + if (classEntry == null) { + reader.setText(null); + return; } + + reader.setText("(decompiling...)"); + + // run decompiler in a separate thread to keep ui responsive + new Thread() { + @Override + public void run() { + + // get the outermost class + ClassEntry obfClassEntry = deobfuscator.obfuscateEntry(classEntry); + List classChain = deobfuscator.getJarIndex().getObfClassChain(obfClassEntry); + ClassEntry obfOutermostClassEntry = classChain.get(0); + + // decompile it + reader.setText(deobfuscator.getSource(deobfuscator.getSourceTree(obfOutermostClassEntry.getName()))); + } + }.start(); } - private static void printScoredMatches(int maxScore, List scores, Multimap scoredMatches) { - int numScoredMatchesShown = 0; - for (int score : scores) { - for (ClassEntry classEntry : scoredMatches.get(score)) { - System.out.println(String.format("\tScore: %3d %3.0f%% %s", - score, 100.0 * score / maxScore, classEntry.getName() - )); - if (numScoredMatchesShown++ > 10) { - return; - } + private void updateMatchButton() { + + boolean twoSelected = m_sourceClass != null && m_destClass != null; + boolean isMatched = twoSelected && m_matches.getUniqueMatches().containsKey(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); + + m_matchButton.setEnabled(twoSelected); + if (twoSelected) { + if (isMatched) { + m_matchButton.setText("Unmatch"); + } else { + m_matchButton.setText("Match"); } + } else { + m_matchButton.setText(""); } } + protected void onMatchClick() { + // TODO + } + + /* private static List getClassNames(Collection classes) { List out = Lists.newArrayList(); for (ClassEntry c : classes) { -- cgit v1.2.3 From e8d6cb9e1ab61357ac26eb93a0a67917ded8a7b5 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 2 Mar 2015 01:41:28 -0500 Subject: added simple renamer for local variable table --- src/cuchaz/enigma/TranslatingTypeLoader.java | 2 ++ .../enigma/bytecode/LocalVariableRenamer.java | 32 ++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/cuchaz/enigma/bytecode/LocalVariableRenamer.java diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 94ad6ebe..7b57cfa7 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -36,6 +36,7 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.bytecode.ClassRenamer; import cuchaz.enigma.bytecode.ClassTranslator; import cuchaz.enigma.bytecode.InnerClassWriter; +import cuchaz.enigma.bytecode.LocalVariableRenamer; import cuchaz.enigma.bytecode.MethodParameterWriter; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Translator; @@ -232,6 +233,7 @@ public class TranslatingTypeLoader implements ITypeLoader { // do all kinds of deobfuscating transformations on the class new BridgeMarker(m_jarIndex).markBridges(c); new MethodParameterWriter(m_deobfuscatingTranslator).writeMethodArguments(c); + new LocalVariableRenamer().rename(c); new ClassTranslator(m_deobfuscatingTranslator).translate(c); return c; diff --git a/src/cuchaz/enigma/bytecode/LocalVariableRenamer.java b/src/cuchaz/enigma/bytecode/LocalVariableRenamer.java new file mode 100644 index 00000000..53f207ca --- /dev/null +++ b/src/cuchaz/enigma/bytecode/LocalVariableRenamer.java @@ -0,0 +1,32 @@ +package cuchaz.enigma.bytecode; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.bytecode.ByteArray; +import javassist.bytecode.ConstPool; +import javassist.bytecode.LocalVariableAttribute; + + +public class LocalVariableRenamer { + + public void rename(CtClass c) { + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + + // if there's a local variable table, just rename everything to v1, v2, v3, ... for now + LocalVariableAttribute table = (LocalVariableAttribute)behavior.getMethodInfo().getCodeAttribute().getAttribute(LocalVariableAttribute.tag); + if (table == null) { + continue; + } + + ConstPool constants = c.getClassFile().getConstPool(); + for (int i=0; i 0 && destClasses.size() > 0; } diff --git a/src/cuchaz/enigma/convert/Matches.java b/src/cuchaz/enigma/convert/Matches.java index 5faa923c..0b00b29e 100644 --- a/src/cuchaz/enigma/convert/Matches.java +++ b/src/cuchaz/enigma/convert/Matches.java @@ -48,6 +48,22 @@ public class Matches implements Iterable { m_matches.add(match); indexMatch(match); } + + public void remove(ClassMatch match) { + for (ClassEntry sourceClass : match.sourceClasses) { + m_matchesBySource.remove(sourceClass); + m_uniqueMatches.remove(sourceClass); + m_ambiguousMatchesBySource.remove(sourceClass); + m_unmatchedSourceClasses.remove(sourceClass); + } + for (ClassEntry destClass : match.sourceClasses) { + m_matchesByDest.remove(destClass); + m_uniqueMatches.inverse().remove(destClass); + m_ambiguousMatchesByDest.remove(destClass); + m_unmatchedDestClasses.remove(destClass); + } + m_matches.remove(match); + } public int size() { return m_matches.size(); @@ -112,4 +128,26 @@ public class Matches implements Iterable { public ClassMatch getMatchByDest(ClassEntry destClass) { return m_matchesByDest.get(destClass); } + + public void removeSource(ClassEntry sourceClass) { + ClassMatch match = m_matchesBySource.get(sourceClass); + if (match != null) { + remove(match); + match.sourceClasses.remove(sourceClass); + if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { + add(match); + } + } + } + + public void removeDest(ClassEntry destClass) { + ClassMatch match = m_matchesByDest.get(destClass); + if (match != null) { + remove(match); + match.destClasses.remove(destClass); + if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { + add(match); + } + } + } } diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java index e9dff164..e2d517ee 100644 --- a/src/cuchaz/enigma/gui/MatchingGui.java +++ b/src/cuchaz/enigma/gui/MatchingGui.java @@ -7,6 +7,7 @@ import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -25,6 +26,7 @@ import javax.swing.WindowConstants; import com.beust.jcommander.internal.Lists; import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.BiMap; import com.google.common.collect.Multimap; import cuchaz.enigma.Constants; @@ -33,6 +35,7 @@ import cuchaz.enigma.convert.ClassIdentifier; import cuchaz.enigma.convert.ClassIdentity; import cuchaz.enigma.convert.ClassMatch; import cuchaz.enigma.convert.ClassNamer; +import cuchaz.enigma.convert.MappingsConverter; import cuchaz.enigma.convert.Matches; import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; import cuchaz.enigma.mapping.ClassEntry; @@ -79,6 +82,10 @@ public class MatchingGui { } } + public static interface SaveListener { + public void save(Matches matches); + } + // controls private JFrame m_frame; private ClassSelector m_sourceClasses; @@ -94,6 +101,8 @@ public class MatchingGui { private Deobfuscator m_destDeobfuscator; private ClassEntry m_sourceClass; private ClassEntry m_destClass; + private SourceType m_sourceType; + private SaveListener m_saveListener; public MatchingGui(Matches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { @@ -179,17 +188,13 @@ public class MatchingGui { bottomPanel.setLayout(new FlowLayout()); m_sourceClassLabel = new JLabel(); - m_sourceClassLabel.setPreferredSize(new Dimension(300, 0)); + m_sourceClassLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); + m_sourceClassLabel.setPreferredSize(new Dimension(300, 24)); m_destClassLabel = new JLabel(); - m_destClassLabel.setPreferredSize(new Dimension(300, 0)); + m_destClassLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); + m_destClassLabel.setPreferredSize(new Dimension(300, 24)); m_matchButton = new JButton(); - m_matchButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - onMatchClick(); - } - }); m_matchButton.setPreferredSize(new Dimension(140, 24)); bottomPanel.add(m_sourceClassLabel); @@ -205,13 +210,29 @@ public class MatchingGui { m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); // init state + updateMappings(); setSourceType(SourceType.getDefault()); updateMatchButton(); + m_saveListener = null; + } + + public void setSaveListener(SaveListener val) { + m_saveListener = val; + } + + private void updateMappings() { + m_destDeobfuscator.setMappings(MappingsConverter.newMappings( + m_matches, + m_sourceDeobfuscator.getMappings(), + m_sourceDeobfuscator, + m_destDeobfuscator + )); } protected void setSourceType(SourceType val) { // show the source classes - m_sourceClasses.setClasses(deobfuscateClasses(val.getSourceClasses(m_matches), m_sourceDeobfuscator)); + m_sourceType = val; + m_sourceClasses.setClasses(deobfuscateClasses(m_sourceType.getSourceClasses(m_matches), m_sourceDeobfuscator)); } private Collection deobfuscateClasses(Collection in, Deobfuscator deobfuscator) { @@ -234,11 +255,24 @@ public class MatchingGui { ClassMatch match = m_matches.getMatchBySource(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); assert(match != null); if (match.destClasses.isEmpty()) { - m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator)); + + m_destClasses.setClasses(null); + + // run in a separate thread to keep ui responsive + new Thread() { + @Override + public void run() { + m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator)); + m_destClasses.expandRow(0); + } + }.start(); + } else { + m_destClasses.setClasses(deobfuscateClasses(match.destClasses, m_destDeobfuscator)); + m_destClasses.expandRow(0); + } - m_destClasses.expandRow(0); } setDestClass(null); @@ -309,7 +343,7 @@ public class MatchingGui { reader.setText("(decompiling...)"); - // run decompiler in a separate thread to keep ui responsive + // run in a separate thread to keep ui responsive new Thread() { @Override public void run() { @@ -327,33 +361,98 @@ public class MatchingGui { private void updateMatchButton() { + ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); + ClassEntry obfDest = m_destDeobfuscator.obfuscateEntry(m_destClass); + + BiMap uniqueMatches = m_matches.getUniqueMatches(); boolean twoSelected = m_sourceClass != null && m_destClass != null; - boolean isMatched = twoSelected && m_matches.getUniqueMatches().containsKey(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); + boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); + boolean canMatch = !uniqueMatches.containsKey(obfSource) && ! uniqueMatches.containsValue(obfDest); - m_matchButton.setEnabled(twoSelected); + deactivateButton(m_matchButton); if (twoSelected) { if (isMatched) { - m_matchButton.setText("Unmatch"); - } else { - m_matchButton.setText("Match"); + activateButton(m_matchButton, "Unmatch", new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + onUnmatchClick(); + } + }); + } else if (canMatch) { + activateButton(m_matchButton, "Match", new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + onMatchClick(); + } + }); } - } else { - m_matchButton.setText(""); } } - protected void onMatchClick() { - // TODO + private void deactivateButton(JButton button) { + button.setEnabled(false); + button.setText(""); + for (ActionListener listener : Arrays.asList(button.getActionListeners())) { + button.removeActionListener(listener); + } } - /* - private static List getClassNames(Collection classes) { - List out = Lists.newArrayList(); - for (ClassEntry c : classes) { - out.add(c.getName()); + private void activateButton(JButton button, String text, ActionListener newListener) { + button.setText(text); + button.setEnabled(true); + for (ActionListener listener : Arrays.asList(button.getActionListeners())) { + button.removeActionListener(listener); + } + button.addActionListener(newListener); + } + + private void onMatchClick() { + // precondition: source and dest classes are set correctly + + ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); + ClassEntry obfDest = m_destDeobfuscator.obfuscateEntry(m_destClass); + + // remove the classes from their match + m_matches.removeSource(obfSource); + m_matches.removeDest(obfDest); + + // add them as matched classes + m_matches.add(new ClassMatch(obfSource, obfDest)); + + // TEMP + System.out.println("Match: " + obfSource + " <-> " + obfDest); + + //save(); + updateMappings(); + setDestClass(null); + m_destClasses.setClasses(null); + updateMatchButton(); + setSourceType(m_sourceType); + } + + private void onUnmatchClick() { + // precondition: source and dest classes are set to a unique match + + ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); + + // remove the source to break the match, then add the source back as unmatched + m_matches.removeSource(obfSource); + m_matches.add(new ClassMatch(obfSource, null)); + + // TEMP + System.out.println("Unmatch: " + obfSource + " <-> " + m_destDeobfuscator.obfuscateEntry(m_destClass)); + + //save(); + updateMappings(); + setDestClass(null); + m_destClasses.setClasses(null); + updateMatchButton(); + setSourceType(m_sourceType); + } + + private void save() { + if (m_saveListener != null) { + m_saveListener.save(m_matches); } - Collections.sort(out); - return out; } - */ } -- cgit v1.2.3 From d1a041362a164e4469a4b725608c631bd0961c2e Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 7 Mar 2015 20:54:44 -0500 Subject: ui improvements --- src/cuchaz/enigma/convert/ClassForest.java | 8 +- src/cuchaz/enigma/convert/ClassIdentifier.java | 6 +- src/cuchaz/enigma/convert/ClassMatching.java | 12 +- src/cuchaz/enigma/convert/MappingsConverter.java | 10 +- src/cuchaz/enigma/convert/Matches.java | 2 +- src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 12 ++ .../enigma/gui/ClassSelectorPackageNode.java | 12 ++ src/cuchaz/enigma/gui/MatchingGui.java | 141 +++++++++++++++------ 8 files changed, 155 insertions(+), 48 deletions(-) diff --git a/src/cuchaz/enigma/convert/ClassForest.java b/src/cuchaz/enigma/convert/ClassForest.java index 36731393..a5ea0568 100644 --- a/src/cuchaz/enigma/convert/ClassForest.java +++ b/src/cuchaz/enigma/convert/ClassForest.java @@ -24,8 +24,12 @@ public class ClassForest { } } - private void add(ClassEntry entry) { - m_forest.put(m_identifier.identify(entry), entry); + public void add(ClassEntry entry) { + try { + m_forest.put(m_identifier.identify(entry), entry); + } catch (ClassNotFoundException ex) { + throw new Error("Unable to find class " + entry.getName()); + } } public Collection identities() { diff --git a/src/cuchaz/enigma/convert/ClassIdentifier.java b/src/cuchaz/enigma/convert/ClassIdentifier.java index bdbf11b2..da799cd0 100644 --- a/src/cuchaz/enigma/convert/ClassIdentifier.java +++ b/src/cuchaz/enigma/convert/ClassIdentifier.java @@ -29,10 +29,14 @@ public class ClassIdentifier { m_cache = Maps.newHashMap(); } - public ClassIdentity identify(ClassEntry classEntry) { + public ClassIdentity identify(ClassEntry classEntry) + throws ClassNotFoundException { ClassIdentity identity = m_cache.get(classEntry); if (identity == null) { CtClass c = m_loader.loadClass(classEntry.getName()); + if (c == null) { + throw new ClassNotFoundException(classEntry.getName()); + } identity = new ClassIdentity(c, m_namer, m_index, m_useReferences); m_cache.put(classEntry, identity); } diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java index 9f931300..d8973ac5 100644 --- a/src/cuchaz/enigma/convert/ClassMatching.java +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -40,8 +40,16 @@ public class ClassMatching { } public void match(Iterable sourceClasses, Iterable destClasses) { - m_sourceClasses.addAll(sourceClasses); - m_destClasses.addAll(destClasses); + for (ClassEntry sourceClass : sourceClasses) { + if (!m_knownMatches.containsKey(sourceClass)) { + m_sourceClasses.add(sourceClass); + } + } + for (ClassEntry destClass : destClasses) { + if (!m_knownMatches.containsValue(destClass)) { + m_destClasses.add(destClass); + } + } } public Collection matches() { diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index aa067d4d..f38723f7 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -45,11 +45,11 @@ public class MappingsConverter { destIndex.indexJar(destJar, false); // compute the matching - ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex); + ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null); return new Matches(matching.matches()); } - public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex) { + public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap knownMatches) { System.out.println("Iteratively matching classes"); @@ -74,11 +74,15 @@ public class MappingsConverter { new ClassIdentifier(destJar, destIndex, destNamer, useReferences) ); + if (knownMatches != null) { + matching.addKnownMatches(knownMatches); + } + if (lastMatching == null) { // search all classes matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries()); } else { - // we already know about these matches + // we already know about these matches from last time matching.addKnownMatches(lastMatching.uniqueMatches()); // search unmatched and ambiguously-matched classes diff --git a/src/cuchaz/enigma/convert/Matches.java b/src/cuchaz/enigma/convert/Matches.java index 0b00b29e..19bb155f 100644 --- a/src/cuchaz/enigma/convert/Matches.java +++ b/src/cuchaz/enigma/convert/Matches.java @@ -56,7 +56,7 @@ public class Matches implements Iterable { m_ambiguousMatchesBySource.remove(sourceClass); m_unmatchedSourceClasses.remove(sourceClass); } - for (ClassEntry destClass : match.sourceClasses) { + for (ClassEntry destClass : match.destClasses) { m_matchesByDest.remove(destClass); m_uniqueMatches.inverse().remove(destClass); m_ambiguousMatchesByDest.remove(destClass); diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java index b3d7b899..f0541888 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java @@ -35,4 +35,16 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode { } return m_classEntry.getSimpleName(); } + + @Override + public boolean equals(Object other) { + if (other instanceof ClassSelectorClassNode) { + return equals((ClassSelectorClassNode)other); + } + return false; + } + + public boolean equals(ClassSelectorClassNode other) { + return m_classEntry.equals(other.m_classEntry); + } } diff --git a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java index 451d3809..5685abbf 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java @@ -30,4 +30,16 @@ public class ClassSelectorPackageNode extends DefaultMutableTreeNode { public String toString() { return m_packageName; } + + @Override + public boolean equals(Object other) { + if (other instanceof ClassSelectorPackageNode) { + return equals((ClassSelectorPackageNode)other); + } + return false; + } + + public boolean equals(ClassSelectorPackageNode other) { + return m_packageName.equals(other.m_packageName); + } } diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java index e2d517ee..04dbd7a7 100644 --- a/src/cuchaz/enigma/gui/MatchingGui.java +++ b/src/cuchaz/enigma/gui/MatchingGui.java @@ -11,10 +11,12 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; @@ -22,9 +24,12 @@ import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JSplitPane; +import javax.swing.SwingConstants; import javax.swing.WindowConstants; +import javax.swing.tree.TreePath; import com.beust.jcommander.internal.Lists; +import com.beust.jcommander.internal.Maps; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.Multimap; @@ -34,6 +39,7 @@ import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.convert.ClassIdentifier; import cuchaz.enigma.convert.ClassIdentity; import cuchaz.enigma.convert.ClassMatch; +import cuchaz.enigma.convert.ClassMatching; import cuchaz.enigma.convert.ClassNamer; import cuchaz.enigma.convert.MappingsConverter; import cuchaz.enigma.convert.Matches; @@ -95,6 +101,8 @@ public class MatchingGui { private JLabel m_sourceClassLabel; private JLabel m_destClassLabel; private JButton m_matchButton; + private Map m_sourceTypeButtons; + private JCheckBox m_advanceCheck; private Matches m_matches; private Deobfuscator m_sourceDeobfuscator; @@ -133,8 +141,11 @@ public class MatchingGui { } }; ButtonGroup sourceTypeButtons = new ButtonGroup(); + m_sourceTypeButtons = Maps.newHashMap(); for (SourceType sourceType : SourceType.values()) { - sourceTypePanel.add(sourceType.newRadio(sourceTypeListener, sourceTypeButtons)); + JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); + m_sourceTypeButtons.put(sourceType, button); + sourceTypePanel.add(button); } m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); @@ -164,6 +175,15 @@ public class MatchingGui { JScrollPane destScroller = new JScrollPane(m_destClasses); destPanel.add(destScroller); + JButton autoMatchButton = new JButton("AutoMatch"); + autoMatchButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + autoMatch(); + } + }); + destPanel.add(autoMatchButton); + // init source panels DefaultSyntaxKit.initKit(); m_sourceReader = new JEditorPane(); @@ -188,18 +208,21 @@ public class MatchingGui { bottomPanel.setLayout(new FlowLayout()); m_sourceClassLabel = new JLabel(); - m_sourceClassLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); + m_sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT); m_sourceClassLabel.setPreferredSize(new Dimension(300, 24)); m_destClassLabel = new JLabel(); - m_destClassLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); + m_destClassLabel.setHorizontalAlignment(SwingConstants.LEFT); m_destClassLabel.setPreferredSize(new Dimension(300, 24)); m_matchButton = new JButton(); m_matchButton.setPreferredSize(new Dimension(140, 24)); + m_advanceCheck = new JCheckBox("Advance to next likely match"); + bottomPanel.add(m_sourceClassLabel); bottomPanel.add(m_matchButton); bottomPanel.add(m_destClassLabel); + bottomPanel.add(m_advanceCheck); pane.add(bottomPanel, BorderLayout.SOUTH); // show the frame @@ -210,7 +233,7 @@ public class MatchingGui { m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); // init state - updateMappings(); + updateDestMappings(); setSourceType(SourceType.getDefault()); updateMatchButton(); m_saveListener = null; @@ -220,7 +243,7 @@ public class MatchingGui { m_saveListener = val; } - private void updateMappings() { + private void updateDestMappings() { m_destDeobfuscator.setMappings(MappingsConverter.newMappings( m_matches, m_sourceDeobfuscator.getMappings(), @@ -230,9 +253,18 @@ public class MatchingGui { } protected void setSourceType(SourceType val) { + // show the source classes m_sourceType = val; m_sourceClasses.setClasses(deobfuscateClasses(m_sourceType.getSourceClasses(m_matches), m_sourceDeobfuscator)); + + // update counts + for (SourceType sourceType : SourceType.values()) { + m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", + sourceType.name(), + sourceType.getSourceClasses(m_matches).size() + )); + } } private Collection deobfuscateClasses(Collection in, Deobfuscator deobfuscator) { @@ -296,31 +328,37 @@ public class MatchingGui { namer.getDestNamer(), true ); - // rank all the unmatched dest classes against the source class - ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); - Multimap scoredDestClasses = ArrayListMultimap.create(); - for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { - ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); - float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) - /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); - scoredDestClasses.put(score, unmatchedDestClass); - } + try { + + // rank all the unmatched dest classes against the source class + ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); + Multimap scoredDestClasses = ArrayListMultimap.create(); + for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { + ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); + float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) + /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); + scoredDestClasses.put(score, unmatchedDestClass); + } - // sort by scores - List scores = new ArrayList(scoredDestClasses.keySet()); - Collections.sort(scores, Collections.reverseOrder()); - - // collect the scored classes in order - List scoredClasses = Lists.newArrayList(); - for (float score : scores) { - for (ClassEntry classEntry : scoredDestClasses.get(score)) { - scoredClasses.add(new DecoratedClassEntry(classEntry, String.format("%.0f%% ", score))); - if (scoredClasses.size() > 10) { - return scoredClasses; + // sort by scores + List scores = new ArrayList(scoredDestClasses.keySet()); + Collections.sort(scores, Collections.reverseOrder()); + + // collect the scored classes in order + List scoredClasses = Lists.newArrayList(); + for (float score : scores) { + for (ClassEntry classEntry : scoredDestClasses.get(score)) { + scoredClasses.add(new DecoratedClassEntry(classEntry, String.format("%2.0f%% ", score))); + if (scoredClasses.size() > 10) { + return scoredClasses; + } } } + return scoredClasses; + + } catch (ClassNotFoundException ex) { + throw new Error("Unable to find class " + ex.getMessage()); } - return scoredClasses; } protected void setDestClass(ClassEntry classEntry) { @@ -419,15 +457,18 @@ public class MatchingGui { // add them as matched classes m_matches.add(new ClassMatch(obfSource, obfDest)); - // TEMP - System.out.println("Match: " + obfSource + " <-> " + obfDest); + // remember where we were in the source tree + TreePath path = m_sourceClasses.getSelectionPath(); - //save(); - updateMappings(); - setDestClass(null); - m_destClasses.setClasses(null); - updateMatchButton(); - setSourceType(m_sourceType); + save(); + updateMatches(); + + // put the tree back to where it was + m_sourceClasses.expandPath(path); + + if (m_advanceCheck.isSelected()) { + + } } private void onUnmatchClick() { @@ -439,11 +480,12 @@ public class MatchingGui { m_matches.removeSource(obfSource); m_matches.add(new ClassMatch(obfSource, null)); - // TEMP - System.out.println("Unmatch: " + obfSource + " <-> " + m_destDeobfuscator.obfuscateEntry(m_destClass)); - - //save(); - updateMappings(); + save(); + updateMatches(); + } + + private void updateMatches() { + updateDestMappings(); setDestClass(null); m_destClasses.setClasses(null); updateMatchButton(); @@ -455,4 +497,25 @@ public class MatchingGui { m_saveListener.save(m_matches); } } + + private void autoMatch() { + + System.out.println("Automatching..."); + + // compute a new matching + ClassMatching matching = MappingsConverter.computeMatching( + m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(), + m_destDeobfuscator.getJar(), m_destDeobfuscator.getJarIndex(), + m_matches.getUniqueMatches() + ); + Matches newMatches = new Matches(matching.matches()); + System.out.println(String.format("Automatch found %d new matches", + newMatches.getUniqueMatches().size() - m_matches.getUniqueMatches().size() + )); + + // update the current matches + m_matches = newMatches; + save(); + updateMatches(); + } } -- cgit v1.2.3 From 42b2f3e6cf1e5c5c25055e1c05b083d099542b7a Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 8 Mar 2015 11:17:52 -0400 Subject: Closing branch: default --- src/cuchaz/enigma/gui/MatchingGui.java | 42 +++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java index 04dbd7a7..f1da25aa 100644 --- a/src/cuchaz/enigma/gui/MatchingGui.java +++ b/src/cuchaz/enigma/gui/MatchingGui.java @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Enumeration; import java.util.List; import java.util.Map; @@ -26,6 +27,7 @@ import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.SwingConstants; import javax.swing.WindowConstants; +import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import com.beust.jcommander.internal.Lists; @@ -276,7 +278,7 @@ public class MatchingGui { } protected void setSourceClass(ClassEntry classEntry) { - + // update the current source class m_sourceClass = classEntry; m_sourceClassLabel.setText(m_sourceClass != null ? m_sourceClass.getName() : ""); @@ -457,17 +459,11 @@ public class MatchingGui { // add them as matched classes m_matches.add(new ClassMatch(obfSource, obfDest)); - // remember where we were in the source tree - TreePath path = m_sourceClasses.getSelectionPath(); - save(); updateMatches(); - // put the tree back to where it was - m_sourceClasses.expandPath(path); - if (m_advanceCheck.isSelected()) { - + advance(); } } @@ -489,7 +485,33 @@ public class MatchingGui { setDestClass(null); m_destClasses.setClasses(null); updateMatchButton(); + + // remember where we were in the source tree + String packageName = null; + if (!m_sourceClasses.isSelectionEmpty()) { + packageName = m_sourceClasses.getSelectionPath().getParentPath().getLastPathComponent().toString(); + } + setSourceType(m_sourceType); + + if (packageName != null) { + // find the corresponding path in the new tree + TreePath path = null; + DefaultMutableTreeNode root = (DefaultMutableTreeNode)m_sourceClasses.getModel().getRoot(); + Enumeration children = root.children(); + while (children.hasMoreElements()) { + Object child = children.nextElement(); + if (child.toString().equals(packageName)) { + path = new TreePath(new Object[] {root, child}); + break; + } + } + + if (path != null) { + // put the tree back to where it was + m_sourceClasses.expandPath(path); + } + } } private void save() { @@ -518,4 +540,8 @@ public class MatchingGui { save(); updateMatches(); } + + private void advance() { + // TODO: find a likely match + } } -- cgit v1.2.3 From 61eb14f65e73a9b3d0ea6eca6b04da804a4ff61b Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 8 Mar 2015 13:49:44 -0400 Subject: lots of small tweaks and improvements --- src/cuchaz/enigma/analysis/SourceIndex.java | 4 + src/cuchaz/enigma/convert/ClassIdentity.java | 19 +- src/cuchaz/enigma/gui/ClassSelector.java | 142 +++++++++++++-- src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 4 +- src/cuchaz/enigma/gui/DecoratedClassEntry.java | 20 --- src/cuchaz/enigma/gui/Gui.java | 50 +----- src/cuchaz/enigma/gui/GuiTricks.java | 58 ++++++ src/cuchaz/enigma/gui/MatchingGui.java | 208 +++++++++++++++------- src/cuchaz/enigma/gui/ScoredClassEntry.java | 20 +++ 9 files changed, 378 insertions(+), 147 deletions(-) delete mode 100644 src/cuchaz/enigma/gui/DecoratedClassEntry.java create mode 100644 src/cuchaz/enigma/gui/ScoredClassEntry.java diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index e31b8032..b3fb751a 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -146,6 +146,10 @@ public class SourceIndex { return m_declarationToToken.values(); } + public Iterable declarations() { + return m_declarationToToken.keySet(); + } + public Token getDeclarationToken(Entry deobfEntry) { return m_declarationToToken.get(deobfEntry); } diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index 3736a533..d07e0a40 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -69,6 +69,7 @@ public class ClassIdentity { private Multiset m_implements; private Multiset m_implementations; private Multiset m_references; + private String m_outer; private final ClassNameReplacer m_classNameReplacer = new ClassNameReplacer() { @@ -167,6 +168,8 @@ public class ClassIdentity { } } } + + m_outer = EntryFactory.getClassEntry(c).getOuterClassName(); } private void addReference(EntryReference reference) { @@ -234,6 +237,9 @@ public class ClassIdentity { buf.append(reference); buf.append("\n"); } + buf.append("\touter "); + buf.append(m_outer); + buf.append("\n"); return buf.toString(); } @@ -402,13 +408,15 @@ public class ClassIdentity { } public int getMatchScore(ClassIdentity other) { - return getNumMatches(m_fields, other.m_fields) + return 2*getNumMatches(m_extends, other.m_extends) + + 2*getNumMatches(m_outer, other.m_outer) + + getNumMatches(m_fields, other.m_fields) + getNumMatches(m_methods, other.m_methods) + getNumMatches(m_constructors, other.m_constructors); } public int getMaxMatchScore() { - return m_fields.size() + m_methods.size() + m_constructors.size(); + return 2 + 2 + m_fields.size() + m_methods.size() + m_constructors.size(); } public boolean matches(CtClass c) { @@ -427,4 +435,11 @@ public class ClassIdentity { } return numMatches; } + + private int getNumMatches(String a, String b) { + if (a.equals(b)) { + return 1; + } + return 0; + } } diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java index e5f550bb..2a636754 100644 --- a/src/cuchaz/enigma/gui/ClassSelector.java +++ b/src/cuchaz/enigma/gui/ClassSelector.java @@ -15,6 +15,7 @@ import java.awt.event.MouseEvent; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.Enumeration; import java.util.List; import java.util.Map; @@ -41,19 +42,12 @@ public class ClassSelector extends JTree { public static Comparator ObfuscatedClassEntryComparator; public static Comparator DeobfuscatedClassEntryComparator; - private static String getClassEntryDisplayName(ClassEntry entry) { - if (entry instanceof DecoratedClassEntry) { - return ((DecoratedClassEntry)entry).getDecoration() + entry.getName(); - } - return entry.getName(); - } - static { ObfuscatedClassEntryComparator = new Comparator() { @Override public int compare(ClassEntry a, ClassEntry b) { - String aname = getClassEntryDisplayName(a); - String bname = getClassEntryDisplayName(b); + String aname = a.getName(); + String bname = a.getName(); if (aname.length() != bname.length()) { return aname.length() - bname.length(); } @@ -64,9 +58,13 @@ public class ClassSelector extends JTree { DeobfuscatedClassEntryComparator = new Comparator() { @Override public int compare(ClassEntry a, ClassEntry b) { - String aname = getClassEntryDisplayName(a); - String bname = getClassEntryDisplayName(b); - return aname.compareTo(bname); + if (a instanceof ScoredClassEntry && b instanceof ScoredClassEntry) { + return Float.compare( + ((ScoredClassEntry)b).getScore(), + ((ScoredClassEntry)a).getScore() + ); + } + return a.getName().compareTo(b.getName()); } }; } @@ -172,4 +170,124 @@ public class ClassSelector extends JTree { // finally, update the tree control setModel(new DefaultTreeModel(root)); } + + public ClassEntry getSelectedClass() { + if (!isSelectionEmpty()) { + Object selectedNode = getSelectionPath().getLastPathComponent(); + if (selectedNode instanceof ClassSelectorClassNode) { + ClassSelectorClassNode classNode = (ClassSelectorClassNode)selectedNode; + return classNode.getClassEntry(); + } + } + return null; + } + + public String getSelectedPackage() { + if (!isSelectionEmpty()) { + Object selectedNode = getSelectionPath().getLastPathComponent(); + if (selectedNode instanceof ClassSelectorPackageNode) { + ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode)selectedNode; + return packageNode.getPackageName(); + } else if (selectedNode instanceof ClassSelectorClassNode) { + ClassSelectorClassNode classNode = (ClassSelectorClassNode)selectedNode; + return classNode.getClassEntry().getPackageName(); + } + } + return null; + } + + public Iterable packageNodes() { + List nodes = Lists.newArrayList(); + DefaultMutableTreeNode root = (DefaultMutableTreeNode)getModel().getRoot(); + Enumeration children = root.children(); + while (children.hasMoreElements()) { + ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode)children.nextElement(); + nodes.add(packageNode); + } + return nodes; + } + + public Iterable classNodes(ClassSelectorPackageNode packageNode) { + List nodes = Lists.newArrayList(); + Enumeration children = packageNode.children(); + while (children.hasMoreElements()) { + ClassSelectorClassNode classNode = (ClassSelectorClassNode)children.nextElement(); + nodes.add(classNode); + } + return nodes; + } + + public void expandPackage(String packageName) { + if (packageName == null) { + return; + } + for (ClassSelectorPackageNode packageNode : packageNodes()) { + if (packageNode.getPackageName().equals(packageName)) { + expandPath(new TreePath(new Object[] {getModel().getRoot(), packageNode})); + return; + } + } + } + + public void expandAll() { + for (ClassSelectorPackageNode packageNode : packageNodes()) { + expandPath(new TreePath(new Object[] {getModel().getRoot(), packageNode})); + } + } + + public ClassEntry getFirstClass() { + for (ClassSelectorPackageNode packageNode : packageNodes()) { + for (ClassSelectorClassNode classNode : classNodes(packageNode)) { + return classNode.getClassEntry(); + } + } + return null; + } + + public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { + for (ClassSelectorPackageNode packageNode : packageNodes()) { + if (packageNode.getPackageName().equals(entry.getPackageName())) { + return packageNode; + } + } + return null; + } + + public ClassEntry getNextClass(ClassEntry entry) { + boolean foundIt = false; + for (ClassSelectorPackageNode packageNode : packageNodes()) { + if (!foundIt) { + // skip to the package with our target in it + if (packageNode.getPackageName().equals(entry.getPackageName())) { + for (ClassSelectorClassNode classNode : classNodes(packageNode)) { + if (!foundIt) { + if (classNode.getClassEntry().equals(entry)) { + foundIt = true; + } + } else { + // return the next class + return classNode.getClassEntry(); + } + } + } + } else { + // return the next class + for (ClassSelectorClassNode classNode : classNodes(packageNode)) { + return classNode.getClassEntry(); + } + } + } + return null; + } + + public void setSelectionClass(ClassEntry classEntry) { + expandPackage(classEntry.getPackageName()); + for (ClassSelectorPackageNode packageNode : packageNodes()) { + for (ClassSelectorClassNode classNode : classNodes(packageNode)) { + if (classNode.getClassEntry().equals(classEntry)) { + setSelectionPath(new TreePath(new Object[] {getModel().getRoot(), packageNode, classNode})); + } + } + } + } } diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java index f0541888..6c1c9a0a 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java @@ -30,8 +30,8 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode { @Override public String toString() { - if (m_classEntry instanceof DecoratedClassEntry) { - return ((DecoratedClassEntry)m_classEntry).getDecoration() + m_classEntry.getSimpleName(); + if (m_classEntry instanceof ScoredClassEntry) { + return String.format("%d%% %s", (int)((ScoredClassEntry)m_classEntry).getScore(), m_classEntry.getSimpleName()); } return m_classEntry.getSimpleName(); } diff --git a/src/cuchaz/enigma/gui/DecoratedClassEntry.java b/src/cuchaz/enigma/gui/DecoratedClassEntry.java deleted file mode 100644 index dd8b4fa3..00000000 --- a/src/cuchaz/enigma/gui/DecoratedClassEntry.java +++ /dev/null @@ -1,20 +0,0 @@ -package cuchaz.enigma.gui; - -import cuchaz.enigma.mapping.ClassEntry; - - -public class DecoratedClassEntry extends ClassEntry { - - private static final long serialVersionUID = -8798725308554217105L; - - private String m_decoration; - - public DecoratedClassEntry(ClassEntry other, String decoration) { - super(other); - m_decoration = decoration; - } - - public String getDecoration() { - return m_decoration; - } -} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index a214243f..38a6ee9a 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -16,7 +16,6 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridLayout; -import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; @@ -54,8 +53,6 @@ import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; -import javax.swing.SwingUtilities; -import javax.swing.Timer; import javax.swing.WindowConstants; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; @@ -740,52 +737,7 @@ public class Gui { if (token == null) { throw new IllegalArgumentException("Token cannot be null!"); } - - // set the caret position to the token - m_editor.setCaretPosition(token.start); - m_editor.grabFocus(); - - try { - // make sure the token is visible in the scroll window - Rectangle start = m_editor.modelToView(token.start); - Rectangle end = m_editor.modelToView(token.end); - final Rectangle show = start.union(end); - show.grow(start.width * 10, start.height * 6); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - m_editor.scrollRectToVisible(show); - } - }); - } catch (BadLocationException ex) { - throw new Error(ex); - } - - // highlight the token momentarily - final Timer timer = new Timer(200, new ActionListener() { - private int m_counter = 0; - private Object m_highlight = null; - - @Override - public void actionPerformed(ActionEvent event) { - if (m_counter % 2 == 0) { - try { - m_highlight = m_editor.getHighlighter().addHighlight(token.start, token.end, m_selectionHighlightPainter); - } catch (BadLocationException ex) { - // don't care - } - } else if (m_highlight != null) { - m_editor.getHighlighter().removeHighlight(m_highlight); - } - - if (m_counter++ > 6) { - Timer timer = (Timer)event.getSource(); - timer.stop(); - } - } - }); - timer.start(); - + GuiTricks.navigateToToken(m_editor, token, m_selectionHighlightPainter); redraw(); } diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java index df9e2215..5a3a01de 100644 --- a/src/cuchaz/enigma/gui/GuiTricks.java +++ b/src/cuchaz/enigma/gui/GuiTricks.java @@ -11,11 +11,21 @@ package cuchaz.enigma.gui; import java.awt.Font; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import javax.swing.JComponent; +import javax.swing.JEditorPane; import javax.swing.JLabel; +import javax.swing.SwingUtilities; +import javax.swing.Timer; import javax.swing.ToolTipManager; +import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter.HighlightPainter; + +import cuchaz.enigma.analysis.Token; public class GuiTricks { @@ -33,4 +43,52 @@ public class GuiTricks { manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); manager.setInitialDelay(oldDelay); } + + public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { + + // set the caret position to the token + editor.setCaretPosition(token.start); + editor.grabFocus(); + + try { + // make sure the token is visible in the scroll window + Rectangle start = editor.modelToView(token.start); + Rectangle end = editor.modelToView(token.end); + final Rectangle show = start.union(end); + show.grow(start.width * 10, start.height * 6); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + editor.scrollRectToVisible(show); + } + }); + } catch (BadLocationException ex) { + throw new Error(ex); + } + + // highlight the token momentarily + final Timer timer = new Timer(200, new ActionListener() { + private int m_counter = 0; + private Object m_highlight = null; + + @Override + public void actionPerformed(ActionEvent event) { + if (m_counter % 2 == 0) { + try { + m_highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter); + } catch (BadLocationException ex) { + // don't care + } + } else if (m_highlight != null) { + editor.getHighlighter().removeHighlight(m_highlight); + } + + if (m_counter++ > 6) { + Timer timer = (Timer)event.getSource(); + timer.stop(); + } + } + }); + timer.start(); + } } diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java index f1da25aa..1e618d08 100644 --- a/src/cuchaz/enigma/gui/MatchingGui.java +++ b/src/cuchaz/enigma/gui/MatchingGui.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Enumeration; import java.util.List; import java.util.Map; @@ -27,17 +26,18 @@ import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.SwingConstants; import javax.swing.WindowConstants; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreePath; import com.beust.jcommander.internal.Lists; import com.beust.jcommander.internal.Maps; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.Multimap; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; import cuchaz.enigma.Constants; import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; import cuchaz.enigma.convert.ClassIdentifier; import cuchaz.enigma.convert.ClassIdentity; import cuchaz.enigma.convert.ClassMatch; @@ -47,6 +47,7 @@ import cuchaz.enigma.convert.MappingsConverter; import cuchaz.enigma.convert.Matches; import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; import de.sciss.syntaxpane.DefaultSyntaxKit; @@ -105,6 +106,7 @@ public class MatchingGui { private JButton m_matchButton; private Map m_sourceTypeButtons; private JCheckBox m_advanceCheck; + private SelectionHighlightPainter m_selectionHighlightPainter; private Matches m_matches; private Deobfuscator m_sourceDeobfuscator; @@ -188,12 +190,8 @@ public class MatchingGui { // init source panels DefaultSyntaxKit.initKit(); - m_sourceReader = new JEditorPane(); - m_sourceReader.setEditable(false); - m_sourceReader.setContentType("text/java"); - m_destReader = new JEditorPane(); - m_destReader.setEditable(false); - m_destReader.setContentType("text/java"); + m_sourceReader = makeReader(); + m_destReader = makeReader(); // init all the splits JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader)); @@ -220,6 +218,14 @@ public class MatchingGui { m_matchButton.setPreferredSize(new Dimension(140, 24)); m_advanceCheck = new JCheckBox("Advance to next likely match"); + m_advanceCheck.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (m_advanceCheck.isSelected()) { + advance(); + } + } + }); bottomPanel.add(m_sourceClassLabel); bottomPanel.add(m_matchButton); @@ -234,6 +240,8 @@ public class MatchingGui { m_frame.setVisible(true); m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + m_selectionHighlightPainter = new SelectionHighlightPainter(); + // init state updateDestMappings(); setSourceType(SourceType.getDefault()); @@ -241,6 +249,19 @@ public class MatchingGui { m_saveListener = null; } + private JEditorPane makeReader() { + + JEditorPane reader = new JEditorPane(); + reader.setEditable(false); + reader.setContentType("text/java"); + + // turn off token highlighting (it's wrong most of the time anyway...) + DefaultSyntaxKit kit = (DefaultSyntaxKit)reader.getEditorKit(); + kit.toggleComponent(reader, "de.sciss.syntaxpane.components.TokenMarker"); + + return reader; + } + public void setSaveListener(SaveListener val) { m_saveListener = val; } @@ -272,12 +293,24 @@ public class MatchingGui { private Collection deobfuscateClasses(Collection in, Deobfuscator deobfuscator) { List out = Lists.newArrayList(); for (ClassEntry entry : in) { - out.add(deobfuscator.deobfuscateEntry(entry)); + + ClassEntry deobf = deobfuscator.deobfuscateEntry(entry); + + // make sure we preserve any scores + if (entry instanceof ScoredClassEntry) { + deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry)entry).getScore()); + } + + out.add(deobf); } return out; } protected void setSourceClass(ClassEntry classEntry) { + setSourceClass(classEntry, null); + } + + protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { // update the current source class m_sourceClass = classEntry; @@ -297,20 +330,27 @@ public class MatchingGui { @Override public void run() { m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator)); - m_destClasses.expandRow(0); + m_destClasses.expandAll(); + + if (onGetDestClasses != null) { + onGetDestClasses.run(); + } } }.start(); } else { m_destClasses.setClasses(deobfuscateClasses(match.destClasses, m_destDeobfuscator)); - m_destClasses.expandRow(0); + m_destClasses.expandAll(); + if (onGetDestClasses != null) { + onGetDestClasses.run(); + } } } setDestClass(null); - readSource(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); + decompileClass(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); updateMatchButton(); } @@ -334,29 +374,14 @@ public class MatchingGui { // rank all the unmatched dest classes against the source class ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); - Multimap scoredDestClasses = ArrayListMultimap.create(); + List scoredDestClasses = Lists.newArrayList(); for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); - scoredDestClasses.put(score, unmatchedDestClass); - } - - // sort by scores - List scores = new ArrayList(scoredDestClasses.keySet()); - Collections.sort(scores, Collections.reverseOrder()); - - // collect the scored classes in order - List scoredClasses = Lists.newArrayList(); - for (float score : scores) { - for (ClassEntry classEntry : scoredDestClasses.get(score)) { - scoredClasses.add(new DecoratedClassEntry(classEntry, String.format("%2.0f%% ", score))); - if (scoredClasses.size() > 10) { - return scoredClasses; - } - } + scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score)); } - return scoredClasses; + return scoredDestClasses; } catch (ClassNotFoundException ex) { throw new Error("Unable to find class " + ex.getMessage()); @@ -369,12 +394,12 @@ public class MatchingGui { m_destClass = classEntry; m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : ""); - readSource(m_destClass, m_destDeobfuscator, m_destReader); + decompileClass(m_destClass, m_destDeobfuscator, m_destReader); updateMatchButton(); } - protected void readSource(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { + protected void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { if (classEntry == null) { reader.setText(null); @@ -389,12 +414,37 @@ public class MatchingGui { public void run() { // get the outermost class - ClassEntry obfClassEntry = deobfuscator.obfuscateEntry(classEntry); - List classChain = deobfuscator.getJarIndex().getObfClassChain(obfClassEntry); - ClassEntry obfOutermostClassEntry = classChain.get(0); + ClassEntry outermostClassEntry = classEntry; + while (outermostClassEntry.isInnerClass()) { + outermostClassEntry = outermostClassEntry.getOuterClassEntry(); + } // decompile it - reader.setText(deobfuscator.getSource(deobfuscator.getSourceTree(obfOutermostClassEntry.getName()))); + CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName()); + String source = deobfuscator.getSource(sourceTree); + reader.setText(source); + SourceIndex sourceIndex = deobfuscator.getSourceIndex(sourceTree, source); + + // navigate to the class declaration + Token token = sourceIndex.getDeclarationToken(classEntry); + if (token == null) { + // couldn't find the class declaration token, might be an anonymous class + // look for any declaration in that class instead + for (Entry entry : sourceIndex.declarations()) { + if (entry.getClassEntry().equals(classEntry)) { + token = sourceIndex.getDeclarationToken(entry); + break; + } + } + } + + if (token != null) { + GuiTricks.navigateToToken(reader, token, m_selectionHighlightPainter); + } else { + // couldn't find anything =( + System.out.println("Unable to find declaration in source for " + classEntry); + } + } }.start(); } @@ -459,11 +509,16 @@ public class MatchingGui { // add them as matched classes m_matches.add(new ClassMatch(obfSource, obfDest)); + ClassEntry nextClass = null; + if (m_advanceCheck.isSelected()) { + nextClass = m_sourceClasses.getNextClass(m_sourceClass); + } + save(); updateMatches(); - if (m_advanceCheck.isSelected()) { - advance(); + if (nextClass != null) { + advance(nextClass); } } @@ -487,31 +542,11 @@ public class MatchingGui { updateMatchButton(); // remember where we were in the source tree - String packageName = null; - if (!m_sourceClasses.isSelectionEmpty()) { - packageName = m_sourceClasses.getSelectionPath().getParentPath().getLastPathComponent().toString(); - } + String packageName = m_sourceClasses.getSelectedPackage(); setSourceType(m_sourceType); - if (packageName != null) { - // find the corresponding path in the new tree - TreePath path = null; - DefaultMutableTreeNode root = (DefaultMutableTreeNode)m_sourceClasses.getModel().getRoot(); - Enumeration children = root.children(); - while (children.hasMoreElements()) { - Object child = children.nextElement(); - if (child.toString().equals(packageName)) { - path = new TreePath(new Object[] {root, child}); - break; - } - } - - if (path != null) { - // put the tree back to where it was - m_sourceClasses.expandPath(path); - } - } + m_sourceClasses.expandPackage(packageName); } private void save() { @@ -542,6 +577,55 @@ public class MatchingGui { } private void advance() { - // TODO: find a likely match + advance(null); + } + + private void advance(ClassEntry sourceClass) { + + // make sure we have a source class + if (sourceClass == null) { + sourceClass = m_sourceClasses.getSelectedClass(); + if (sourceClass != null) { + sourceClass = m_sourceClasses.getNextClass(sourceClass); + } else { + sourceClass = m_sourceClasses.getFirstClass(); + } + } + + // set the source class + setSourceClass(sourceClass, new Runnable() { + @Override + public void run() { + + // then, pick the best dest class + ClassEntry firstClass = null; + ScoredClassEntry bestDestClass = null; + for (ClassSelectorPackageNode packageNode : m_destClasses.packageNodes()) { + for (ClassSelectorClassNode classNode : m_destClasses.classNodes(packageNode)) { + if (firstClass == null) { + firstClass = classNode.getClassEntry(); + } + if (classNode.getClassEntry() instanceof ScoredClassEntry) { + ScoredClassEntry scoredClass = (ScoredClassEntry)classNode.getClassEntry(); + if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { + bestDestClass = scoredClass; + } + } + } + } + + // pick the entry to show + ClassEntry destClass = null; + if (bestDestClass != null) { + destClass = bestDestClass; + } else if (firstClass != null) { + destClass = firstClass; + } + + setDestClass(destClass); + m_destClasses.setSelectionClass(destClass); + } + }); + m_sourceClasses.setSelectionClass(sourceClass); } } diff --git a/src/cuchaz/enigma/gui/ScoredClassEntry.java b/src/cuchaz/enigma/gui/ScoredClassEntry.java new file mode 100644 index 00000000..dd7ba619 --- /dev/null +++ b/src/cuchaz/enigma/gui/ScoredClassEntry.java @@ -0,0 +1,20 @@ +package cuchaz.enigma.gui; + +import cuchaz.enigma.mapping.ClassEntry; + + +public class ScoredClassEntry extends ClassEntry { + + private static final long serialVersionUID = -8798725308554217105L; + + private float m_score; + + public ScoredClassEntry(ClassEntry other, float score) { + super(other); + m_score = score; + } + + public float getScore() { + return m_score; + } +} -- cgit v1.2.3 From 4ceb8d490058e48df666bf7227ce020e60928be5 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 8 Mar 2015 20:48:30 -0400 Subject: more tweaks, improvements, and bug fixes --- src/cuchaz/enigma/ConvertMain.java | 22 +++++-- src/cuchaz/enigma/Deobfuscator.java | 25 ++++++-- src/cuchaz/enigma/convert/ClassIdentity.java | 25 +++++++- src/cuchaz/enigma/convert/MappingsConverter.java | 60 +++++++++++++++--- src/cuchaz/enigma/gui/MatchingGui.java | 79 ++++++++++++++---------- src/cuchaz/enigma/mapping/ArgumentMapping.java | 5 ++ src/cuchaz/enigma/mapping/ClassMapping.java | 11 ++++ src/cuchaz/enigma/mapping/FieldMapping.java | 26 ++++++++ src/cuchaz/enigma/mapping/MethodMapping.java | 16 ++++- src/cuchaz/enigma/mapping/Signature.java | 5 ++ src/cuchaz/enigma/mapping/Type.java | 13 ++++ 11 files changed, 233 insertions(+), 54 deletions(-) diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index cad49f58..975cdcc8 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -2,6 +2,7 @@ package cuchaz.enigma; import java.io.File; import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.util.jar.JarFile; @@ -14,6 +15,7 @@ import cuchaz.enigma.gui.MatchingGui.SaveListener; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.MappingsWriter; public class ConvertMain { @@ -32,7 +34,7 @@ public class ConvertMain { //computeMatches(matchingFile, sourceJar, destJar, mappings); editMatches(matchingFile, sourceJar, destJar, mappings); - //convertMappings(outMappingsFile, mappings, matchingFile); + //convertMappings(outMappingsFile, sourceJar, destJar, mappings, matchingFile); /* TODO // write out the converted mappings @@ -56,7 +58,7 @@ public class ConvertMain { Matches matches = MatchesReader.read(matchingFile); System.out.println("Indexing source jar..."); Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); - sourceDeobfuscator.setMappings(mappings); + sourceDeobfuscator.setMappings(mappings, false); System.out.println("Indexing dest jar..."); Deobfuscator destDeobfuscator = new Deobfuscator(destJar); System.out.println("Starting GUI..."); @@ -72,9 +74,21 @@ public class ConvertMain { }); } - private static void convertMappings(File outMappingsFile, Mappings mappings, File matchingFile) + private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File matchingFile) throws IOException { + System.out.println("Reading matches..."); Matches matches = MatchesReader.read(matchingFile); - MappingsConverter.convertMappings(mappings, matches.getUniqueMatches()); + System.out.println("Indexing source jar..."); + Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); + sourceDeobfuscator.setMappings(mappings); + System.out.println("Indexing dest jar..."); + Deobfuscator destDeobfuscator = new Deobfuscator(destJar); + + Mappings newMappings = MappingsConverter.newMappings(matches, mappings, sourceDeobfuscator, destDeobfuscator); + + try (FileWriter out = new FileWriter(outMappingsFile)) { + new MappingsWriter().write(out, newMappings); + } + System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath()); } } diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index e5d0e3d9..9b0d3db4 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -116,6 +116,10 @@ public class Deobfuscator { } public void setMappings(Mappings val) { + setMappings(val, true); + } + + public void setMappings(Mappings val, boolean warnAboutDrops) { if (val == null) { val = new Mappings(); } @@ -123,7 +127,10 @@ public class Deobfuscator { // drop mappings that don't match the jar RelatedMethodChecker relatedMethodChecker = new RelatedMethodChecker(m_jarIndex); for (ClassMapping classMapping : Lists.newArrayList(val.classes())) { - if (!checkClassMapping(relatedMethodChecker, classMapping)) { + if (!checkClassMapping(relatedMethodChecker, classMapping, warnAboutDrops)) { + if (warnAboutDrops) { + System.err.println("WARNING: unable to find class " + classMapping.getObfFullName() + ". dropping mapping"); + } val.removeClassMapping(classMapping); } } @@ -138,7 +145,7 @@ public class Deobfuscator { m_translatorCache.clear(); } - private boolean checkClassMapping(RelatedMethodChecker relatedMethodChecker, ClassMapping classMapping) { + private boolean checkClassMapping(RelatedMethodChecker relatedMethodChecker, ClassMapping classMapping, boolean warnAboutDrops) { // check the class ClassEntry classEntry = EntryFactory.getObfClassEntry(m_jarIndex, classMapping); @@ -150,7 +157,9 @@ public class Deobfuscator { for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { FieldEntry fieldEntry = new FieldEntry(classEntry, fieldMapping.getObfName(), fieldMapping.getObfType()); if (!m_jarIndex.containsObfField(fieldEntry)) { - System.err.println("WARNING: unable to find field " + fieldEntry + ". dropping mapping."); + if (warnAboutDrops) { + System.err.println("WARNING: unable to find field " + fieldEntry + ". dropping mapping."); + } classMapping.removeFieldMapping(fieldMapping); } } @@ -159,7 +168,9 @@ public class Deobfuscator { for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); if (!m_jarIndex.containsObfBehavior(obfBehaviorEntry)) { - System.err.println("WARNING: unable to find behavior " + obfBehaviorEntry + ". dropping mapping."); + if (warnAboutDrops) { + System.err.println("WARNING: unable to find behavior " + obfBehaviorEntry + ". dropping mapping."); + } classMapping.removeMethodMapping(methodMapping); } @@ -168,8 +179,10 @@ public class Deobfuscator { // check inner classes for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { - if (!checkClassMapping(relatedMethodChecker, innerClassMapping)) { - System.err.println("WARNING: unable to find inner class " + EntryFactory.getObfClassEntry(m_jarIndex, classMapping) + ". dropping mapping."); + if (!checkClassMapping(relatedMethodChecker, innerClassMapping, warnAboutDrops)) { + if (warnAboutDrops) { + System.err.println("WARNING: unable to find inner class " + EntryFactory.getObfClassEntry(m_jarIndex, classMapping) + ". dropping mapping."); + } classMapping.removeInnerClassMapping(innerClassMapping); } } diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index d07e0a40..35667b05 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -16,6 +16,7 @@ import java.security.NoSuchAlgorithmException; import java.util.Enumeration; import java.util.List; import java.util.Map; +import java.util.Set; import javassist.CannotCompileException; import javassist.CtBehavior; @@ -38,6 +39,7 @@ import com.google.common.collect.HashMultiset; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multiset; +import com.google.common.collect.Sets; import cuchaz.enigma.Constants; import cuchaz.enigma.Util; @@ -67,6 +69,7 @@ public class ClassIdentity { private String m_staticInitializer; private String m_extends; private Multiset m_implements; + private Set m_stringLiterals; private Multiset m_implementations; private Multiset m_references; private String m_outer; @@ -140,6 +143,14 @@ public class ClassIdentity { m_implements.add(scrubClassName(Descriptor.toJvmName(interfaceName))); } + m_stringLiterals = Sets.newHashSet(); + ConstPool constants = c.getClassFile().getConstPool(); + for (int i=1; i a, Set b) { + int numMatches = 0; + for (String val : a) { + if (b.contains(val)) { + numMatches++; + } + } + return numMatches; + } + private int getNumMatches(Multiset a, Multiset b) { int numMatches = 0; for (String val : a) { diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index f38723f7..5883878c 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -30,7 +30,10 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.ClassNameReplacer; +import cuchaz.enigma.mapping.FieldMapping; import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MethodMapping; public class MappingsConverter { @@ -129,15 +132,20 @@ public class MappingsConverter { for (Entry match : matchesByDestChainSize.get(chainSize)) { // get class info - ClassEntry sourceClassEntry = match.getKey(); - ClassEntry deobfClassEntry = sourceDeobfuscator.deobfuscateEntry(sourceClassEntry); - ClassEntry destClassEntry = match.getValue(); - List destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(destClassEntry); + ClassEntry obfSourceClassEntry = match.getKey(); + ClassEntry obfDestClassEntry = match.getValue(); + List destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry); + + ClassMapping sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry); + if (sourceMapping == null) { + // if this class was never deobfuscated, don't try to match it + continue; + } // find out where to make the dest class mapping if (destClassChain.size() == 1) { // not an inner class, add directly to mappings - newMappings.addClassMapping(new ClassMapping(destClassEntry.getName(), deobfClassEntry.getName())); + newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false)); } else { // inner class, find the outer class mapping ClassMapping destMapping = null; @@ -157,14 +165,52 @@ public class MappingsConverter { } } } - String deobfName = deobfClassEntry.isInnerClass() ? deobfClassEntry.getInnerClassName() : deobfClassEntry.getSimpleName(); - destMapping.addInnerClassMapping(new ClassMapping(destClassEntry.getName(), deobfName)); + destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true)); } } } return newMappings; } + private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping mapping, final Matches matches, boolean useSimpleName) { + + ClassNameReplacer replacer = new ClassNameReplacer() { + @Override + public String replace(String className) { + ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className)); + if (newClassEntry != null) { + return newClassEntry.getName(); + } + return null; + } + }; + + ClassMapping newMapping; + String deobfName = mapping.getDeobfName(); + if (deobfName != null) { + if (useSimpleName) { + deobfName = new ClassEntry(deobfName).getSimpleName(); + } + newMapping = new ClassMapping(newObfClass.getName(), deobfName); + } else { + newMapping = new ClassMapping(newObfClass.getName()); + } + + // copy fields + for (FieldMapping fieldMapping : mapping.fields()) { + // TODO: map field obf names too... + newMapping.addFieldMapping(new FieldMapping(fieldMapping, replacer)); + } + + // copy methods + for (MethodMapping methodMapping : mapping.methods()) { + // TODO: map method obf names too... + newMapping.addMethodMapping(new MethodMapping(methodMapping, replacer)); + } + + return newMapping; + } + public static void convertMappings(Mappings mappings, BiMap changes) { // sort the changes so classes are renamed in the correct order diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java index 1e618d08..85842c12 100644 --- a/src/cuchaz/enigma/gui/MatchingGui.java +++ b/src/cuchaz/enigma/gui/MatchingGui.java @@ -6,10 +6,8 @@ import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -29,9 +27,7 @@ import javax.swing.WindowConstants; import com.beust.jcommander.internal.Lists; import com.beust.jcommander.internal.Maps; -import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; -import com.google.common.collect.Multimap; import com.strobel.decompiler.languages.java.ast.CompilationUnit; import cuchaz.enigma.Constants; @@ -272,7 +268,7 @@ public class MatchingGui { m_sourceDeobfuscator.getMappings(), m_sourceDeobfuscator, m_destDeobfuscator - )); + ), false); } protected void setSourceType(SourceType val) { @@ -307,7 +303,18 @@ public class MatchingGui { } protected void setSourceClass(ClassEntry classEntry) { - setSourceClass(classEntry, null); + + Runnable onGetDestClasses = null; + if (m_advanceCheck.isSelected()) { + onGetDestClasses = new Runnable() { + @Override + public void run() { + pickBestDestClass(); + } + }; + } + + setSourceClass(classEntry, onGetDestClasses); } protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { @@ -596,36 +603,40 @@ public class MatchingGui { setSourceClass(sourceClass, new Runnable() { @Override public void run() { - - // then, pick the best dest class - ClassEntry firstClass = null; - ScoredClassEntry bestDestClass = null; - for (ClassSelectorPackageNode packageNode : m_destClasses.packageNodes()) { - for (ClassSelectorClassNode classNode : m_destClasses.classNodes(packageNode)) { - if (firstClass == null) { - firstClass = classNode.getClassEntry(); - } - if (classNode.getClassEntry() instanceof ScoredClassEntry) { - ScoredClassEntry scoredClass = (ScoredClassEntry)classNode.getClassEntry(); - if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { - bestDestClass = scoredClass; - } - } - } - } - - // pick the entry to show - ClassEntry destClass = null; - if (bestDestClass != null) { - destClass = bestDestClass; - } else if (firstClass != null) { - destClass = firstClass; - } - - setDestClass(destClass); - m_destClasses.setSelectionClass(destClass); + pickBestDestClass(); } }); m_sourceClasses.setSelectionClass(sourceClass); } + + private void pickBestDestClass() { + + // then, pick the best dest class + ClassEntry firstClass = null; + ScoredClassEntry bestDestClass = null; + for (ClassSelectorPackageNode packageNode : m_destClasses.packageNodes()) { + for (ClassSelectorClassNode classNode : m_destClasses.classNodes(packageNode)) { + if (firstClass == null) { + firstClass = classNode.getClassEntry(); + } + if (classNode.getClassEntry() instanceof ScoredClassEntry) { + ScoredClassEntry scoredClass = (ScoredClassEntry)classNode.getClassEntry(); + if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { + bestDestClass = scoredClass; + } + } + } + } + + // pick the entry to show + ClassEntry destClass = null; + if (bestDestClass != null) { + destClass = bestDestClass; + } else if (firstClass != null) { + destClass = firstClass; + } + + setDestClass(destClass); + m_destClasses.setSelectionClass(destClass); + } } diff --git a/src/cuchaz/enigma/mapping/ArgumentMapping.java b/src/cuchaz/enigma/mapping/ArgumentMapping.java index f4d8e774..9f366a04 100644 --- a/src/cuchaz/enigma/mapping/ArgumentMapping.java +++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java @@ -25,6 +25,11 @@ public class ArgumentMapping implements Serializable, Comparable { } } + // rename field types + for (FieldMapping fieldMapping : new ArrayList(m_fieldsByObf.values())) { + String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); + if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { + boolean wasRemoved = m_fieldsByObf.remove(oldFieldKey) != null; + assert (wasRemoved); + boolean wasAdded = m_fieldsByObf.put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; + assert (wasAdded); + } + } + // rename method signatures for (MethodMapping methodMapping : new ArrayList(m_methodsByObf.values())) { String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java index 14b20dd4..55b0a195 100644 --- a/src/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -26,6 +26,12 @@ public class FieldMapping implements Serializable, Comparable { m_obfType = obfType; } + public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) { + m_obfName = other.m_obfName; + m_deobfName = other.m_deobfName; + m_obfType = new Type(other.m_obfType, obfClassNameReplacer); + } + public String getObfName() { return m_obfName; } @@ -46,4 +52,24 @@ public class FieldMapping implements Serializable, Comparable { public int compareTo(FieldMapping other) { return (m_obfName + m_obfType).compareTo(other.m_obfName + other.m_obfType); } + + public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { + + // rename obf classes in the type + Type newType = new Type(m_obfType, new ClassNameReplacer() { + @Override + public String replace(String className) { + if (className.equals(oldObfClassName)) { + return newObfClassName; + } + return null; + } + }); + + if (!newType.equals(m_obfType)) { + m_obfType = newType; + return true; + } + return false; + } } diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index 1704428a..bf8a94f3 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -12,7 +12,9 @@ package cuchaz.enigma.mapping; import java.io.Serializable; import java.util.Map; -import java.util.TreeMap; +import java.util.Map.Entry; + +import com.google.common.collect.Maps; public class MethodMapping implements Serializable, Comparable { @@ -37,9 +39,19 @@ public class MethodMapping implements Serializable, Comparable { m_obfName = obfName; m_deobfName = NameValidator.validateMethodName(deobfName); m_obfSignature = obfSignature; - m_arguments = new TreeMap(); + m_arguments = Maps.newTreeMap(); } + public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { + m_obfName = other.m_obfName; + m_deobfName = other.m_deobfName; + m_obfSignature = new Signature(other.m_obfSignature, obfClassNameReplacer); + m_arguments = Maps.newTreeMap(); + for (Entry entry : other.m_arguments.entrySet()) { + m_arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); + } + } + public String getObfName() { return m_obfName; } diff --git a/src/cuchaz/enigma/mapping/Signature.java b/src/cuchaz/enigma/mapping/Signature.java index 273a77b9..ea83e40e 100644 --- a/src/cuchaz/enigma/mapping/Signature.java +++ b/src/cuchaz/enigma/mapping/Signature.java @@ -39,6 +39,11 @@ public class Signature implements Serializable { } } + public Signature(Signature other) { + m_argumentTypes = Lists.newArrayList(other.m_argumentTypes); + m_returnType = new Type(other.m_returnType); + } + public Signature(Signature other, ClassNameReplacer replacer) { m_argumentTypes = Lists.newArrayList(other.m_argumentTypes); for (int i=0; i mapping : checker.getDroppedClassMappings().entrySet()) { + System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); + } + for (java.util.Map.Entry mapping : checker.getDroppedInnerClassMappings().entrySet()) { + System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); + } + for (java.util.Map.Entry mapping : checker.getDroppedFieldMappings().entrySet()) { + System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); + } + for (java.util.Map.Entry mapping : checker.getDroppedMethodMappings().entrySet()) { + System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); } } // check for related method inconsistencies - if (relatedMethodChecker.hasProblems()) { - throw new Error("Related methods are inconsistent! Need to fix the mappings manually.\n" + relatedMethodChecker.getReport()); + if (checker.getRelatedMethodChecker().hasProblems()) { + throw new Error("Related methods are inconsistent! Need to fix the mappings manually.\n" + checker.getRelatedMethodChecker().getReport()); } m_mappings = val; @@ -145,51 +150,6 @@ public class Deobfuscator { m_translatorCache.clear(); } - private boolean checkClassMapping(RelatedMethodChecker relatedMethodChecker, ClassMapping classMapping, boolean warnAboutDrops) { - - // check the class - ClassEntry classEntry = EntryFactory.getObfClassEntry(m_jarIndex, classMapping); - if (!m_jarIndex.getObfClassEntries().contains(classEntry)) { - return false; - } - - // check the fields - for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { - FieldEntry fieldEntry = new FieldEntry(classEntry, fieldMapping.getObfName(), fieldMapping.getObfType()); - if (!m_jarIndex.containsObfField(fieldEntry)) { - if (warnAboutDrops) { - System.err.println("WARNING: unable to find field " + fieldEntry + ". dropping mapping."); - } - classMapping.removeFieldMapping(fieldMapping); - } - } - - // check methods - for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { - BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); - if (!m_jarIndex.containsObfBehavior(obfBehaviorEntry)) { - if (warnAboutDrops) { - System.err.println("WARNING: unable to find behavior " + obfBehaviorEntry + ". dropping mapping."); - } - classMapping.removeMethodMapping(methodMapping); - } - - relatedMethodChecker.checkMethod(classEntry, methodMapping); - } - - // check inner classes - for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { - if (!checkClassMapping(relatedMethodChecker, innerClassMapping, warnAboutDrops)) { - if (warnAboutDrops) { - System.err.println("WARNING: unable to find inner class " + EntryFactory.getObfClassEntry(m_jarIndex, classMapping) + ". dropping mapping."); - } - classMapping.removeInnerClassMapping(innerClassMapping); - } - } - - return true; - } - public Translator getTranslator(TranslationDirection direction) { Translator translator = m_translatorCache.get(direction); if (translator == null) { diff --git a/src/cuchaz/enigma/gui/ClassMatchingGui.java b/src/cuchaz/enigma/gui/ClassMatchingGui.java new file mode 100644 index 00000000..ff7cda99 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassMatchingGui.java @@ -0,0 +1,661 @@ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.SwingConstants; +import javax.swing.WindowConstants; + +import com.beust.jcommander.internal.Lists; +import com.beust.jcommander.internal.Maps; +import com.google.common.collect.BiMap; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.convert.ClassIdentifier; +import cuchaz.enigma.convert.ClassIdentity; +import cuchaz.enigma.convert.ClassMatch; +import cuchaz.enigma.convert.ClassMatching; +import cuchaz.enigma.convert.ClassNamer; +import cuchaz.enigma.convert.MappingsConverter; +import cuchaz.enigma.convert.Matches; +import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsChecker; +import de.sciss.syntaxpane.DefaultSyntaxKit; + + +public class ClassMatchingGui { + + private static enum SourceType { + Matched { + + @Override + public Collection getSourceClasses(Matches matches) { + return matches.getUniqueMatches().keySet(); + } + }, + Unmatched { + + @Override + public Collection getSourceClasses(Matches matches) { + return matches.getUnmatchedSourceClasses(); + } + }, + Ambiguous { + + @Override + public Collection getSourceClasses(Matches matches) { + return matches.getAmbiguouslyMatchedSourceClasses(); + } + }; + + public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { + JRadioButton button = new JRadioButton(name(), this == getDefault()); + button.setActionCommand(name()); + button.addActionListener(listener); + group.add(button); + return button; + } + + public abstract Collection getSourceClasses(Matches matches); + + public static SourceType getDefault() { + return values()[0]; + } + } + + public static interface SaveListener { + public void save(Matches matches); + } + + // controls + private JFrame m_frame; + private ClassSelector m_sourceClasses; + private ClassSelector m_destClasses; + private JEditorPane m_sourceReader; + private JEditorPane m_destReader; + private JLabel m_sourceClassLabel; + private JLabel m_destClassLabel; + private JButton m_matchButton; + private Map m_sourceTypeButtons; + private JCheckBox m_advanceCheck; + private SelectionHighlightPainter m_selectionHighlightPainter; + + private Matches m_matches; + private Deobfuscator m_sourceDeobfuscator; + private Deobfuscator m_destDeobfuscator; + private ClassEntry m_sourceClass; + private ClassEntry m_destClass; + private SourceType m_sourceType; + private SaveListener m_saveListener; + + public ClassMatchingGui(Matches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + + m_matches = matches; + m_sourceDeobfuscator = sourceDeobfuscator; + m_destDeobfuscator = destDeobfuscator; + + // init frame + m_frame = new JFrame(Constants.Name); + final Container pane = m_frame.getContentPane(); + pane.setLayout(new BorderLayout()); + + // init source side + JPanel sourcePanel = new JPanel(); + sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS)); + sourcePanel.setPreferredSize(new Dimension(200, 0)); + pane.add(sourcePanel, BorderLayout.WEST); + sourcePanel.add(new JLabel("Source Classes")); + + // init source type radios + JPanel sourceTypePanel = new JPanel(); + sourcePanel.add(sourceTypePanel); + sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); + ActionListener sourceTypeListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + setSourceType(SourceType.valueOf(event.getActionCommand())); + } + }; + ButtonGroup sourceTypeButtons = new ButtonGroup(); + m_sourceTypeButtons = Maps.newHashMap(); + for (SourceType sourceType : SourceType.values()) { + JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); + m_sourceTypeButtons.put(sourceType, button); + sourceTypePanel.add(button); + } + + m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); + m_sourceClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + setSourceClass(classEntry); + } + }); + JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); + sourcePanel.add(sourceScroller); + + // init dest side + JPanel destPanel = new JPanel(); + destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS)); + destPanel.setPreferredSize(new Dimension(200, 0)); + pane.add(destPanel, BorderLayout.WEST); + destPanel.add(new JLabel("Destination Classes")); + + m_destClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); + m_destClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + setDestClass(classEntry); + } + }); + JScrollPane destScroller = new JScrollPane(m_destClasses); + destPanel.add(destScroller); + + JButton autoMatchButton = new JButton("AutoMatch"); + autoMatchButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + autoMatch(); + } + }); + destPanel.add(autoMatchButton); + + // init source panels + DefaultSyntaxKit.initKit(); + m_sourceReader = makeReader(); + m_destReader = makeReader(); + + // init all the splits + JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader)); + splitLeft.setResizeWeight(0); // let the right side take all the slack + JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_destReader), destPanel); + splitRight.setResizeWeight(1); // let the left side take all the slack + JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); + splitCenter.setResizeWeight(0.5); // resize 50:50 + pane.add(splitCenter, BorderLayout.CENTER); + splitCenter.resetToPreferredSizes(); + + // init bottom panel + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new FlowLayout()); + + m_sourceClassLabel = new JLabel(); + m_sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT); + m_sourceClassLabel.setPreferredSize(new Dimension(300, 24)); + m_destClassLabel = new JLabel(); + m_destClassLabel.setHorizontalAlignment(SwingConstants.LEFT); + m_destClassLabel.setPreferredSize(new Dimension(300, 24)); + + m_matchButton = new JButton(); + m_matchButton.setPreferredSize(new Dimension(140, 24)); + + m_advanceCheck = new JCheckBox("Advance to next likely match"); + m_advanceCheck.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (m_advanceCheck.isSelected()) { + advance(); + } + } + }); + + bottomPanel.add(m_sourceClassLabel); + bottomPanel.add(m_matchButton); + bottomPanel.add(m_destClassLabel); + bottomPanel.add(m_advanceCheck); + pane.add(bottomPanel, BorderLayout.SOUTH); + + // show the frame + pane.doLayout(); + m_frame.setSize(1024, 576); + m_frame.setMinimumSize(new Dimension(640, 480)); + m_frame.setVisible(true); + m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + m_selectionHighlightPainter = new SelectionHighlightPainter(); + + // init state + updateDestMappings(); + setSourceType(SourceType.getDefault()); + updateMatchButton(); + m_saveListener = null; + } + + private JEditorPane makeReader() { + + JEditorPane reader = new JEditorPane(); + reader.setEditable(false); + reader.setContentType("text/java"); + + // turn off token highlighting (it's wrong most of the time anyway...) + DefaultSyntaxKit kit = (DefaultSyntaxKit)reader.getEditorKit(); + kit.toggleComponent(reader, "de.sciss.syntaxpane.components.TokenMarker"); + + return reader; + } + + public void setSaveListener(SaveListener val) { + m_saveListener = val; + } + + private void updateDestMappings() { + + Mappings newMappings = MappingsConverter.newMappings( + m_matches, + m_sourceDeobfuscator.getMappings(), + m_sourceDeobfuscator, + m_destDeobfuscator + ); + + // look for dropped mappings + MappingsChecker checker = new MappingsChecker(m_destDeobfuscator.getJarIndex()); + checker.dropBrokenMappings(newMappings); + + // count them + int numDroppedFields = checker.getDroppedFieldMappings().size(); + int numDroppedMethods = checker.getDroppedMethodMappings().size(); + System.out.println(String.format( + "%d mappings from matched classes don't match the dest jar:\n\t%5d fields\n\t%5d methods", + numDroppedFields + numDroppedMethods, + numDroppedFields, + numDroppedMethods + )); + + m_destDeobfuscator.setMappings(newMappings); + } + + protected void setSourceType(SourceType val) { + + // show the source classes + m_sourceType = val; + m_sourceClasses.setClasses(deobfuscateClasses(m_sourceType.getSourceClasses(m_matches), m_sourceDeobfuscator)); + + // update counts + for (SourceType sourceType : SourceType.values()) { + m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", + sourceType.name(), + sourceType.getSourceClasses(m_matches).size() + )); + } + } + + private Collection deobfuscateClasses(Collection in, Deobfuscator deobfuscator) { + List out = Lists.newArrayList(); + for (ClassEntry entry : in) { + + ClassEntry deobf = deobfuscator.deobfuscateEntry(entry); + + // make sure we preserve any scores + if (entry instanceof ScoredClassEntry) { + deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry)entry).getScore()); + } + + out.add(deobf); + } + return out; + } + + protected void setSourceClass(ClassEntry classEntry) { + + Runnable onGetDestClasses = null; + if (m_advanceCheck.isSelected()) { + onGetDestClasses = new Runnable() { + @Override + public void run() { + pickBestDestClass(); + } + }; + } + + setSourceClass(classEntry, onGetDestClasses); + } + + protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { + + // update the current source class + m_sourceClass = classEntry; + m_sourceClassLabel.setText(m_sourceClass != null ? m_sourceClass.getName() : ""); + + if (m_sourceClass != null) { + + // show the dest class(es) + ClassMatch match = m_matches.getMatchBySource(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); + assert(match != null); + if (match.destClasses.isEmpty()) { + + m_destClasses.setClasses(null); + + // run in a separate thread to keep ui responsive + new Thread() { + @Override + public void run() { + m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator)); + m_destClasses.expandAll(); + + if (onGetDestClasses != null) { + onGetDestClasses.run(); + } + } + }.start(); + + } else { + + m_destClasses.setClasses(deobfuscateClasses(match.destClasses, m_destDeobfuscator)); + m_destClasses.expandAll(); + + if (onGetDestClasses != null) { + onGetDestClasses.run(); + } + } + } + + setDestClass(null); + decompileClass(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); + + updateMatchButton(); + } + + private Collection getLikelyMatches(ClassEntry sourceClass) { + + ClassEntry obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass); + + // set up identifiers + ClassNamer namer = new ClassNamer(m_matches.getUniqueMatches()); + ClassIdentifier sourceIdentifier = new ClassIdentifier( + m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(), + namer.getSourceNamer(), true + ); + ClassIdentifier destIdentifier = new ClassIdentifier( + m_destDeobfuscator.getJar(), m_destDeobfuscator.getJarIndex(), + namer.getDestNamer(), true + ); + + try { + + // rank all the unmatched dest classes against the source class + ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); + List scoredDestClasses = Lists.newArrayList(); + for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { + ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); + float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) + /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); + scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score)); + } + return scoredDestClasses; + + } catch (ClassNotFoundException ex) { + throw new Error("Unable to find class " + ex.getMessage()); + } + } + + protected void setDestClass(ClassEntry classEntry) { + + // update the current source class + m_destClass = classEntry; + m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : ""); + + decompileClass(m_destClass, m_destDeobfuscator, m_destReader); + + updateMatchButton(); + } + + protected void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { + + if (classEntry == null) { + reader.setText(null); + return; + } + + reader.setText("(decompiling...)"); + + // run in a separate thread to keep ui responsive + new Thread() { + @Override + public void run() { + + // get the outermost class + ClassEntry outermostClassEntry = classEntry; + while (outermostClassEntry.isInnerClass()) { + outermostClassEntry = outermostClassEntry.getOuterClassEntry(); + } + + // decompile it + CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName()); + String source = deobfuscator.getSource(sourceTree); + reader.setText(source); + SourceIndex sourceIndex = deobfuscator.getSourceIndex(sourceTree, source); + + // navigate to the class declaration + Token token = sourceIndex.getDeclarationToken(classEntry); + if (token == null) { + // couldn't find the class declaration token, might be an anonymous class + // look for any declaration in that class instead + for (Entry entry : sourceIndex.declarations()) { + if (entry.getClassEntry().equals(classEntry)) { + token = sourceIndex.getDeclarationToken(entry); + break; + } + } + } + + if (token != null) { + GuiTricks.navigateToToken(reader, token, m_selectionHighlightPainter); + } else { + // couldn't find anything =( + System.out.println("Unable to find declaration in source for " + classEntry); + } + + } + }.start(); + } + + private void updateMatchButton() { + + ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); + ClassEntry obfDest = m_destDeobfuscator.obfuscateEntry(m_destClass); + + BiMap uniqueMatches = m_matches.getUniqueMatches(); + boolean twoSelected = m_sourceClass != null && m_destClass != null; + boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); + boolean canMatch = !uniqueMatches.containsKey(obfSource) && ! uniqueMatches.containsValue(obfDest); + + deactivateButton(m_matchButton); + if (twoSelected) { + if (isMatched) { + activateButton(m_matchButton, "Unmatch", new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + onUnmatchClick(); + } + }); + } else if (canMatch) { + activateButton(m_matchButton, "Match", new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + onMatchClick(); + } + }); + } + } + } + + private void deactivateButton(JButton button) { + button.setEnabled(false); + button.setText(""); + for (ActionListener listener : Arrays.asList(button.getActionListeners())) { + button.removeActionListener(listener); + } + } + + private void activateButton(JButton button, String text, ActionListener newListener) { + button.setText(text); + button.setEnabled(true); + for (ActionListener listener : Arrays.asList(button.getActionListeners())) { + button.removeActionListener(listener); + } + button.addActionListener(newListener); + } + + private void onMatchClick() { + // precondition: source and dest classes are set correctly + + ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); + ClassEntry obfDest = m_destDeobfuscator.obfuscateEntry(m_destClass); + + // remove the classes from their match + m_matches.removeSource(obfSource); + m_matches.removeDest(obfDest); + + // add them as matched classes + m_matches.add(new ClassMatch(obfSource, obfDest)); + + ClassEntry nextClass = null; + if (m_advanceCheck.isSelected()) { + nextClass = m_sourceClasses.getNextClass(m_sourceClass); + } + + save(); + updateMatches(); + + if (nextClass != null) { + advance(nextClass); + } + } + + private void onUnmatchClick() { + // precondition: source and dest classes are set to a unique match + + ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); + + // remove the source to break the match, then add the source back as unmatched + m_matches.removeSource(obfSource); + m_matches.add(new ClassMatch(obfSource, null)); + + save(); + updateMatches(); + } + + private void updateMatches() { + updateDestMappings(); + setDestClass(null); + m_destClasses.setClasses(null); + updateMatchButton(); + + // remember where we were in the source tree + String packageName = m_sourceClasses.getSelectedPackage(); + + setSourceType(m_sourceType); + + m_sourceClasses.expandPackage(packageName); + } + + private void save() { + if (m_saveListener != null) { + m_saveListener.save(m_matches); + } + } + + private void autoMatch() { + + System.out.println("Automatching..."); + + // compute a new matching + ClassMatching matching = MappingsConverter.computeMatching( + m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(), + m_destDeobfuscator.getJar(), m_destDeobfuscator.getJarIndex(), + m_matches.getUniqueMatches() + ); + Matches newMatches = new Matches(matching.matches()); + System.out.println(String.format("Automatch found %d new matches", + newMatches.getUniqueMatches().size() - m_matches.getUniqueMatches().size() + )); + + // update the current matches + m_matches = newMatches; + save(); + updateMatches(); + } + + private void advance() { + advance(null); + } + + private void advance(ClassEntry sourceClass) { + + // make sure we have a source class + if (sourceClass == null) { + sourceClass = m_sourceClasses.getSelectedClass(); + if (sourceClass != null) { + sourceClass = m_sourceClasses.getNextClass(sourceClass); + } else { + sourceClass = m_sourceClasses.getFirstClass(); + } + } + + // set the source class + setSourceClass(sourceClass, new Runnable() { + @Override + public void run() { + pickBestDestClass(); + } + }); + m_sourceClasses.setSelectionClass(sourceClass); + } + + private void pickBestDestClass() { + + // then, pick the best dest class + ClassEntry firstClass = null; + ScoredClassEntry bestDestClass = null; + for (ClassSelectorPackageNode packageNode : m_destClasses.packageNodes()) { + for (ClassSelectorClassNode classNode : m_destClasses.classNodes(packageNode)) { + if (firstClass == null) { + firstClass = classNode.getClassEntry(); + } + if (classNode.getClassEntry() instanceof ScoredClassEntry) { + ScoredClassEntry scoredClass = (ScoredClassEntry)classNode.getClassEntry(); + if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { + bestDestClass = scoredClass; + } + } + } + } + + // pick the entry to show + ClassEntry destClass = null; + if (bestDestClass != null) { + destClass = bestDestClass; + } else if (firstClass != null) { + destClass = firstClass; + } + + setDestClass(destClass); + m_destClasses.setSelectionClass(destClass); + } +} diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java deleted file mode 100644 index 85842c12..00000000 --- a/src/cuchaz/enigma/gui/MatchingGui.java +++ /dev/null @@ -1,642 +0,0 @@ -package cuchaz.enigma.gui; - -import java.awt.BorderLayout; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import javax.swing.BoxLayout; -import javax.swing.ButtonGroup; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JEditorPane; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JRadioButton; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.SwingConstants; -import javax.swing.WindowConstants; - -import com.beust.jcommander.internal.Lists; -import com.beust.jcommander.internal.Maps; -import com.google.common.collect.BiMap; -import com.strobel.decompiler.languages.java.ast.CompilationUnit; - -import cuchaz.enigma.Constants; -import cuchaz.enigma.Deobfuscator; -import cuchaz.enigma.analysis.SourceIndex; -import cuchaz.enigma.analysis.Token; -import cuchaz.enigma.convert.ClassIdentifier; -import cuchaz.enigma.convert.ClassIdentity; -import cuchaz.enigma.convert.ClassMatch; -import cuchaz.enigma.convert.ClassMatching; -import cuchaz.enigma.convert.ClassNamer; -import cuchaz.enigma.convert.MappingsConverter; -import cuchaz.enigma.convert.Matches; -import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.Entry; -import de.sciss.syntaxpane.DefaultSyntaxKit; - - -public class MatchingGui { - - private static enum SourceType { - Matched { - - @Override - public Collection getSourceClasses(Matches matches) { - return matches.getUniqueMatches().keySet(); - } - }, - Unmatched { - - @Override - public Collection getSourceClasses(Matches matches) { - return matches.getUnmatchedSourceClasses(); - } - }, - Ambiguous { - - @Override - public Collection getSourceClasses(Matches matches) { - return matches.getAmbiguouslyMatchedSourceClasses(); - } - }; - - public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { - JRadioButton button = new JRadioButton(name(), this == getDefault()); - button.setActionCommand(name()); - button.addActionListener(listener); - group.add(button); - return button; - } - - public abstract Collection getSourceClasses(Matches matches); - - public static SourceType getDefault() { - return values()[0]; - } - } - - public static interface SaveListener { - public void save(Matches matches); - } - - // controls - private JFrame m_frame; - private ClassSelector m_sourceClasses; - private ClassSelector m_destClasses; - private JEditorPane m_sourceReader; - private JEditorPane m_destReader; - private JLabel m_sourceClassLabel; - private JLabel m_destClassLabel; - private JButton m_matchButton; - private Map m_sourceTypeButtons; - private JCheckBox m_advanceCheck; - private SelectionHighlightPainter m_selectionHighlightPainter; - - private Matches m_matches; - private Deobfuscator m_sourceDeobfuscator; - private Deobfuscator m_destDeobfuscator; - private ClassEntry m_sourceClass; - private ClassEntry m_destClass; - private SourceType m_sourceType; - private SaveListener m_saveListener; - - public MatchingGui(Matches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { - - m_matches = matches; - m_sourceDeobfuscator = sourceDeobfuscator; - m_destDeobfuscator = destDeobfuscator; - - // init frame - m_frame = new JFrame(Constants.Name); - final Container pane = m_frame.getContentPane(); - pane.setLayout(new BorderLayout()); - - // init source side - JPanel sourcePanel = new JPanel(); - sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS)); - sourcePanel.setPreferredSize(new Dimension(200, 0)); - pane.add(sourcePanel, BorderLayout.WEST); - sourcePanel.add(new JLabel("Source Classes")); - - // init source type radios - JPanel sourceTypePanel = new JPanel(); - sourcePanel.add(sourceTypePanel); - sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); - ActionListener sourceTypeListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - setSourceType(SourceType.valueOf(event.getActionCommand())); - } - }; - ButtonGroup sourceTypeButtons = new ButtonGroup(); - m_sourceTypeButtons = Maps.newHashMap(); - for (SourceType sourceType : SourceType.values()) { - JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); - m_sourceTypeButtons.put(sourceType, button); - sourceTypePanel.add(button); - } - - m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); - m_sourceClasses.setListener(new ClassSelectionListener() { - @Override - public void onSelectClass(ClassEntry classEntry) { - setSourceClass(classEntry); - } - }); - JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); - sourcePanel.add(sourceScroller); - - // init dest side - JPanel destPanel = new JPanel(); - destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS)); - destPanel.setPreferredSize(new Dimension(200, 0)); - pane.add(destPanel, BorderLayout.WEST); - destPanel.add(new JLabel("Destination Classes")); - - m_destClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); - m_destClasses.setListener(new ClassSelectionListener() { - @Override - public void onSelectClass(ClassEntry classEntry) { - setDestClass(classEntry); - } - }); - JScrollPane destScroller = new JScrollPane(m_destClasses); - destPanel.add(destScroller); - - JButton autoMatchButton = new JButton("AutoMatch"); - autoMatchButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - autoMatch(); - } - }); - destPanel.add(autoMatchButton); - - // init source panels - DefaultSyntaxKit.initKit(); - m_sourceReader = makeReader(); - m_destReader = makeReader(); - - // init all the splits - JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader)); - splitLeft.setResizeWeight(0); // let the right side take all the slack - JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_destReader), destPanel); - splitRight.setResizeWeight(1); // let the left side take all the slack - JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); - splitCenter.setResizeWeight(0.5); // resize 50:50 - pane.add(splitCenter, BorderLayout.CENTER); - splitCenter.resetToPreferredSizes(); - - // init bottom panel - JPanel bottomPanel = new JPanel(); - bottomPanel.setLayout(new FlowLayout()); - - m_sourceClassLabel = new JLabel(); - m_sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT); - m_sourceClassLabel.setPreferredSize(new Dimension(300, 24)); - m_destClassLabel = new JLabel(); - m_destClassLabel.setHorizontalAlignment(SwingConstants.LEFT); - m_destClassLabel.setPreferredSize(new Dimension(300, 24)); - - m_matchButton = new JButton(); - m_matchButton.setPreferredSize(new Dimension(140, 24)); - - m_advanceCheck = new JCheckBox("Advance to next likely match"); - m_advanceCheck.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - if (m_advanceCheck.isSelected()) { - advance(); - } - } - }); - - bottomPanel.add(m_sourceClassLabel); - bottomPanel.add(m_matchButton); - bottomPanel.add(m_destClassLabel); - bottomPanel.add(m_advanceCheck); - pane.add(bottomPanel, BorderLayout.SOUTH); - - // show the frame - pane.doLayout(); - m_frame.setSize(1024, 576); - m_frame.setMinimumSize(new Dimension(640, 480)); - m_frame.setVisible(true); - m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - - m_selectionHighlightPainter = new SelectionHighlightPainter(); - - // init state - updateDestMappings(); - setSourceType(SourceType.getDefault()); - updateMatchButton(); - m_saveListener = null; - } - - private JEditorPane makeReader() { - - JEditorPane reader = new JEditorPane(); - reader.setEditable(false); - reader.setContentType("text/java"); - - // turn off token highlighting (it's wrong most of the time anyway...) - DefaultSyntaxKit kit = (DefaultSyntaxKit)reader.getEditorKit(); - kit.toggleComponent(reader, "de.sciss.syntaxpane.components.TokenMarker"); - - return reader; - } - - public void setSaveListener(SaveListener val) { - m_saveListener = val; - } - - private void updateDestMappings() { - m_destDeobfuscator.setMappings(MappingsConverter.newMappings( - m_matches, - m_sourceDeobfuscator.getMappings(), - m_sourceDeobfuscator, - m_destDeobfuscator - ), false); - } - - protected void setSourceType(SourceType val) { - - // show the source classes - m_sourceType = val; - m_sourceClasses.setClasses(deobfuscateClasses(m_sourceType.getSourceClasses(m_matches), m_sourceDeobfuscator)); - - // update counts - for (SourceType sourceType : SourceType.values()) { - m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", - sourceType.name(), - sourceType.getSourceClasses(m_matches).size() - )); - } - } - - private Collection deobfuscateClasses(Collection in, Deobfuscator deobfuscator) { - List out = Lists.newArrayList(); - for (ClassEntry entry : in) { - - ClassEntry deobf = deobfuscator.deobfuscateEntry(entry); - - // make sure we preserve any scores - if (entry instanceof ScoredClassEntry) { - deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry)entry).getScore()); - } - - out.add(deobf); - } - return out; - } - - protected void setSourceClass(ClassEntry classEntry) { - - Runnable onGetDestClasses = null; - if (m_advanceCheck.isSelected()) { - onGetDestClasses = new Runnable() { - @Override - public void run() { - pickBestDestClass(); - } - }; - } - - setSourceClass(classEntry, onGetDestClasses); - } - - protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { - - // update the current source class - m_sourceClass = classEntry; - m_sourceClassLabel.setText(m_sourceClass != null ? m_sourceClass.getName() : ""); - - if (m_sourceClass != null) { - - // show the dest class(es) - ClassMatch match = m_matches.getMatchBySource(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); - assert(match != null); - if (match.destClasses.isEmpty()) { - - m_destClasses.setClasses(null); - - // run in a separate thread to keep ui responsive - new Thread() { - @Override - public void run() { - m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator)); - m_destClasses.expandAll(); - - if (onGetDestClasses != null) { - onGetDestClasses.run(); - } - } - }.start(); - - } else { - - m_destClasses.setClasses(deobfuscateClasses(match.destClasses, m_destDeobfuscator)); - m_destClasses.expandAll(); - - if (onGetDestClasses != null) { - onGetDestClasses.run(); - } - } - } - - setDestClass(null); - decompileClass(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); - - updateMatchButton(); - } - - private Collection getLikelyMatches(ClassEntry sourceClass) { - - ClassEntry obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass); - - // set up identifiers - ClassNamer namer = new ClassNamer(m_matches.getUniqueMatches()); - ClassIdentifier sourceIdentifier = new ClassIdentifier( - m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(), - namer.getSourceNamer(), true - ); - ClassIdentifier destIdentifier = new ClassIdentifier( - m_destDeobfuscator.getJar(), m_destDeobfuscator.getJarIndex(), - namer.getDestNamer(), true - ); - - try { - - // rank all the unmatched dest classes against the source class - ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); - List scoredDestClasses = Lists.newArrayList(); - for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { - ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); - float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) - /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); - scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score)); - } - return scoredDestClasses; - - } catch (ClassNotFoundException ex) { - throw new Error("Unable to find class " + ex.getMessage()); - } - } - - protected void setDestClass(ClassEntry classEntry) { - - // update the current source class - m_destClass = classEntry; - m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : ""); - - decompileClass(m_destClass, m_destDeobfuscator, m_destReader); - - updateMatchButton(); - } - - protected void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { - - if (classEntry == null) { - reader.setText(null); - return; - } - - reader.setText("(decompiling...)"); - - // run in a separate thread to keep ui responsive - new Thread() { - @Override - public void run() { - - // get the outermost class - ClassEntry outermostClassEntry = classEntry; - while (outermostClassEntry.isInnerClass()) { - outermostClassEntry = outermostClassEntry.getOuterClassEntry(); - } - - // decompile it - CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName()); - String source = deobfuscator.getSource(sourceTree); - reader.setText(source); - SourceIndex sourceIndex = deobfuscator.getSourceIndex(sourceTree, source); - - // navigate to the class declaration - Token token = sourceIndex.getDeclarationToken(classEntry); - if (token == null) { - // couldn't find the class declaration token, might be an anonymous class - // look for any declaration in that class instead - for (Entry entry : sourceIndex.declarations()) { - if (entry.getClassEntry().equals(classEntry)) { - token = sourceIndex.getDeclarationToken(entry); - break; - } - } - } - - if (token != null) { - GuiTricks.navigateToToken(reader, token, m_selectionHighlightPainter); - } else { - // couldn't find anything =( - System.out.println("Unable to find declaration in source for " + classEntry); - } - - } - }.start(); - } - - private void updateMatchButton() { - - ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); - ClassEntry obfDest = m_destDeobfuscator.obfuscateEntry(m_destClass); - - BiMap uniqueMatches = m_matches.getUniqueMatches(); - boolean twoSelected = m_sourceClass != null && m_destClass != null; - boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); - boolean canMatch = !uniqueMatches.containsKey(obfSource) && ! uniqueMatches.containsValue(obfDest); - - deactivateButton(m_matchButton); - if (twoSelected) { - if (isMatched) { - activateButton(m_matchButton, "Unmatch", new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - onUnmatchClick(); - } - }); - } else if (canMatch) { - activateButton(m_matchButton, "Match", new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - onMatchClick(); - } - }); - } - } - } - - private void deactivateButton(JButton button) { - button.setEnabled(false); - button.setText(""); - for (ActionListener listener : Arrays.asList(button.getActionListeners())) { - button.removeActionListener(listener); - } - } - - private void activateButton(JButton button, String text, ActionListener newListener) { - button.setText(text); - button.setEnabled(true); - for (ActionListener listener : Arrays.asList(button.getActionListeners())) { - button.removeActionListener(listener); - } - button.addActionListener(newListener); - } - - private void onMatchClick() { - // precondition: source and dest classes are set correctly - - ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); - ClassEntry obfDest = m_destDeobfuscator.obfuscateEntry(m_destClass); - - // remove the classes from their match - m_matches.removeSource(obfSource); - m_matches.removeDest(obfDest); - - // add them as matched classes - m_matches.add(new ClassMatch(obfSource, obfDest)); - - ClassEntry nextClass = null; - if (m_advanceCheck.isSelected()) { - nextClass = m_sourceClasses.getNextClass(m_sourceClass); - } - - save(); - updateMatches(); - - if (nextClass != null) { - advance(nextClass); - } - } - - private void onUnmatchClick() { - // precondition: source and dest classes are set to a unique match - - ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); - - // remove the source to break the match, then add the source back as unmatched - m_matches.removeSource(obfSource); - m_matches.add(new ClassMatch(obfSource, null)); - - save(); - updateMatches(); - } - - private void updateMatches() { - updateDestMappings(); - setDestClass(null); - m_destClasses.setClasses(null); - updateMatchButton(); - - // remember where we were in the source tree - String packageName = m_sourceClasses.getSelectedPackage(); - - setSourceType(m_sourceType); - - m_sourceClasses.expandPackage(packageName); - } - - private void save() { - if (m_saveListener != null) { - m_saveListener.save(m_matches); - } - } - - private void autoMatch() { - - System.out.println("Automatching..."); - - // compute a new matching - ClassMatching matching = MappingsConverter.computeMatching( - m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(), - m_destDeobfuscator.getJar(), m_destDeobfuscator.getJarIndex(), - m_matches.getUniqueMatches() - ); - Matches newMatches = new Matches(matching.matches()); - System.out.println(String.format("Automatch found %d new matches", - newMatches.getUniqueMatches().size() - m_matches.getUniqueMatches().size() - )); - - // update the current matches - m_matches = newMatches; - save(); - updateMatches(); - } - - private void advance() { - advance(null); - } - - private void advance(ClassEntry sourceClass) { - - // make sure we have a source class - if (sourceClass == null) { - sourceClass = m_sourceClasses.getSelectedClass(); - if (sourceClass != null) { - sourceClass = m_sourceClasses.getNextClass(sourceClass); - } else { - sourceClass = m_sourceClasses.getFirstClass(); - } - } - - // set the source class - setSourceClass(sourceClass, new Runnable() { - @Override - public void run() { - pickBestDestClass(); - } - }); - m_sourceClasses.setSelectionClass(sourceClass); - } - - private void pickBestDestClass() { - - // then, pick the best dest class - ClassEntry firstClass = null; - ScoredClassEntry bestDestClass = null; - for (ClassSelectorPackageNode packageNode : m_destClasses.packageNodes()) { - for (ClassSelectorClassNode classNode : m_destClasses.classNodes(packageNode)) { - if (firstClass == null) { - firstClass = classNode.getClassEntry(); - } - if (classNode.getClassEntry() instanceof ScoredClassEntry) { - ScoredClassEntry scoredClass = (ScoredClassEntry)classNode.getClassEntry(); - if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { - bestDestClass = scoredClass; - } - } - } - } - - // pick the entry to show - ClassEntry destClass = null; - if (bestDestClass != null) { - destClass = bestDestClass; - } else if (firstClass != null) { - destClass = firstClass; - } - - setDestClass(destClass); - m_destClasses.setSelectionClass(destClass); - } -} diff --git a/src/cuchaz/enigma/mapping/MappingsChecker.java b/src/cuchaz/enigma/mapping/MappingsChecker.java new file mode 100644 index 00000000..c5ff7a7e --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsChecker.java @@ -0,0 +1,97 @@ +package cuchaz.enigma.mapping; + +import java.util.Map; + +import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Lists; + +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.analysis.RelatedMethodChecker; + + +public class MappingsChecker { + + private JarIndex m_index; + private RelatedMethodChecker m_relatedMethodChecker; + private Map m_droppedClassMappings; + private Map m_droppedInnerClassMappings; + private Map m_droppedFieldMappings; + private Map m_droppedMethodMappings; + + public MappingsChecker(JarIndex index) { + m_index = index; + m_relatedMethodChecker = new RelatedMethodChecker(m_index); + m_droppedClassMappings = Maps.newHashMap(); + m_droppedInnerClassMappings = Maps.newHashMap(); + m_droppedFieldMappings = Maps.newHashMap(); + m_droppedMethodMappings = Maps.newHashMap(); + } + + public RelatedMethodChecker getRelatedMethodChecker() { + return m_relatedMethodChecker; + } + + public Map getDroppedClassMappings() { + return m_droppedClassMappings; + } + + public Map getDroppedInnerClassMappings() { + return m_droppedInnerClassMappings; + } + + public Map getDroppedFieldMappings() { + return m_droppedFieldMappings; + } + + public Map getDroppedMethodMappings() { + return m_droppedMethodMappings; + } + + public void dropBrokenMappings(Mappings mappings) { + for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) { + if (!checkClassMapping(classMapping)) { + mappings.removeClassMapping(classMapping); + m_droppedClassMappings.put(EntryFactory.getObfClassEntry(m_index, classMapping), classMapping); + } + } + } + + private boolean checkClassMapping(ClassMapping classMapping) { + + // check the class + ClassEntry classEntry = EntryFactory.getObfClassEntry(m_index, classMapping); + if (!m_index.getObfClassEntries().contains(classEntry)) { + return false; + } + + // check the fields + for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { + FieldEntry obfFieldEntry = new FieldEntry(classEntry, fieldMapping.getObfName(), fieldMapping.getObfType()); + if (!m_index.containsObfField(obfFieldEntry)) { + classMapping.removeFieldMapping(fieldMapping); + m_droppedFieldMappings.put(obfFieldEntry, fieldMapping); + } + } + + // check methods + for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { + BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); + if (!m_index.containsObfBehavior(obfBehaviorEntry)) { + classMapping.removeMethodMapping(methodMapping); + m_droppedMethodMappings.put(obfBehaviorEntry, methodMapping); + } + + m_relatedMethodChecker.checkMethod(classEntry, methodMapping); + } + + // check inner classes + for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { + if (!checkClassMapping(innerClassMapping)) { + classMapping.removeInnerClassMapping(innerClassMapping); + m_droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(m_index, innerClassMapping), innerClassMapping); + } + } + + return true; + } +} -- cgit v1.2.3 From d6b2a223a7973941e5e4fb45c8ceec4885891496 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Mar 2015 12:53:11 -0400 Subject: starting on field matching gui --- src/cuchaz/enigma/ConvertMain.java | 136 +++++++++++++++---- src/cuchaz/enigma/convert/ClassMatches.java | 153 +++++++++++++++++++++ src/cuchaz/enigma/convert/FieldMatches.java | 35 +++++ src/cuchaz/enigma/convert/MappingsConverter.java | 8 +- src/cuchaz/enigma/convert/Matches.java | 153 --------------------- src/cuchaz/enigma/convert/MatchesReader.java | 8 +- src/cuchaz/enigma/convert/MatchesWriter.java | 6 +- src/cuchaz/enigma/gui/ClassMatchingGui.java | 145 ++++++-------------- src/cuchaz/enigma/gui/CodeReader.java | 164 +++++++++++++++++++++++ src/cuchaz/enigma/gui/FieldMatchingGui.java | 131 ++++++++++++++++++ src/cuchaz/enigma/gui/Gui.java | 2 +- src/cuchaz/enigma/gui/GuiTricks.java | 58 -------- 12 files changed, 645 insertions(+), 354 deletions(-) create mode 100644 src/cuchaz/enigma/convert/ClassMatches.java create mode 100644 src/cuchaz/enigma/convert/FieldMatches.java delete mode 100644 src/cuchaz/enigma/convert/Matches.java create mode 100644 src/cuchaz/enigma/gui/CodeReader.java create mode 100644 src/cuchaz/enigma/gui/FieldMatchingGui.java diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index 2afd9ca9..624eb40a 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -6,14 +6,16 @@ import java.io.FileWriter; import java.io.IOException; import java.util.jar.JarFile; +import cuchaz.enigma.convert.ClassMatches; +import cuchaz.enigma.convert.FieldMatches; import cuchaz.enigma.convert.MappingsConverter; -import cuchaz.enigma.convert.Matches; import cuchaz.enigma.convert.MatchesReader; import cuchaz.enigma.convert.MatchesWriter; import cuchaz.enigma.gui.ClassMatchingGui; -import cuchaz.enigma.gui.ClassMatchingGui.SaveListener; +import cuchaz.enigma.gui.FieldMatchingGui; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsChecker; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; @@ -30,11 +32,13 @@ public class ConvertMain { File inMappingsFile = new File("../Enigma Mappings/1.8.mappings"); File outMappingsFile = new File("../Enigma Mappings/1.8.3.mappings"); Mappings mappings = new MappingsReader().read(new FileReader(inMappingsFile)); - File matchingFile = new File(inMappingsFile.getName() + ".matching"); + File classMatchingFile = new File(inMappingsFile.getName() + ".class.matching"); + File fieldMatchingFile = new File(inMappingsFile.getName() + ".field.matching"); - //computeMatches(matchingFile, sourceJar, destJar, mappings); - editMatches(matchingFile, sourceJar, destJar, mappings); - //convertMappings(outMappingsFile, sourceJar, destJar, mappings, matchingFile); + //computeMatches(classMatchingFile, sourceJar, destJar, mappings); + //editClasssMatches(classMatchingFile, sourceJar, destJar, mappings); + //convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchingFile); + editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchingFile, fieldMatchingFile); /* TODO // write out the converted mappings @@ -45,28 +49,25 @@ public class ConvertMain { */ } - private static void computeMatches(File matchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) + private static void computeMatches(File classMatchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) throws IOException { - Matches matches = MappingsConverter.computeMatches(sourceJar, destJar, mappings); - MatchesWriter.write(matches, matchingFile); - System.out.println("Wrote:\n\t" + matchingFile.getAbsolutePath()); + ClassMatches classMatches = MappingsConverter.computeMatches(sourceJar, destJar, mappings); + MatchesWriter.writeClasses(classMatches, classMatchingFile); + System.out.println("Wrote:\n\t" + classMatchingFile.getAbsolutePath()); } - private static void editMatches(final File matchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) + private static void editClasssMatches(final File classMatchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) throws IOException { System.out.println("Reading matches..."); - Matches matches = MatchesReader.read(matchingFile); - System.out.println("Indexing source jar..."); - Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); - sourceDeobfuscator.setMappings(mappings); - System.out.println("Indexing dest jar..."); - Deobfuscator destDeobfuscator = new Deobfuscator(destJar); + ClassMatches classMatches = MatchesReader.readClasses(classMatchingFile); + Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); + deobfuscators.source.setMappings(mappings); System.out.println("Starting GUI..."); - new ClassMatchingGui(matches, sourceDeobfuscator, destDeobfuscator).setSaveListener(new SaveListener() { + new ClassMatchingGui(classMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(new ClassMatchingGui.SaveListener() { @Override - public void save(Matches matches) { + public void save(ClassMatches matches) { try { - MatchesWriter.write(matches, matchingFile); + MatchesWriter.writeClasses(matches, classMatchingFile); } catch (IOException ex) { throw new Error(ex); } @@ -74,21 +75,100 @@ public class ConvertMain { }); } - private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File matchingFile) + private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchingFile) throws IOException { System.out.println("Reading matches..."); - Matches matches = MatchesReader.read(matchingFile); - System.out.println("Indexing source jar..."); - Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); - sourceDeobfuscator.setMappings(mappings); - System.out.println("Indexing dest jar..."); - Deobfuscator destDeobfuscator = new Deobfuscator(destJar); + ClassMatches classMatches = MatchesReader.readClasses(classMatchingFile); + Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); + deobfuscators.source.setMappings(mappings); - Mappings newMappings = MappingsConverter.newMappings(matches, mappings, sourceDeobfuscator, destDeobfuscator); + Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.source); try (FileWriter out = new FileWriter(outMappingsFile)) { new MappingsWriter().write(out, newMappings); } System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath()); } + + private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchingFile, final File fieldMatchingFile) + throws IOException, MappingParseException { + + System.out.println("Reading matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchingFile); + FieldMatches fieldMatches; + if (fieldMatchingFile.exists() /* TEMP */ && false) { + // TODO + //fieldMatches = MatchesReader.readFields(fieldMatchingFile); + } else { + fieldMatches = new FieldMatches(); + } + + // prep deobfuscators + Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); + deobfuscators.source.setMappings(sourceMappings); + Mappings destMappings = new MappingsReader().read(new FileReader(destMappingsFile)); + MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); + checker.dropBrokenMappings(destMappings); + deobfuscators.dest.setMappings(destMappings); + + new FieldMatchingGui(classMatches, fieldMatches, checker.getDroppedFieldMappings(), deobfuscators.source, deobfuscators.dest).setSaveListener(new FieldMatchingGui.SaveListener() { + @Override + public void save(FieldMatches matches) { + /* TODO + try { + MatchesWriter.writeFields(matches, fieldMatchingFile); + } catch (IOException ex) { + throw new Error(ex); + } + */ + } + }); + } + + private static class Deobfuscators { + + public Deobfuscator source; + public Deobfuscator dest; + + public Deobfuscators(JarFile sourceJar, JarFile destJar) { + System.out.println("Indexing source jar..."); + IndexerThread sourceIndexer = new IndexerThread(sourceJar); + sourceIndexer.start(); + System.out.println("Indexing dest jar..."); + IndexerThread destIndexer = new IndexerThread(destJar); + destIndexer.start(); + sourceIndexer.joinOrBail(); + destIndexer.joinOrBail(); + source = sourceIndexer.deobfuscator; + dest = destIndexer.deobfuscator; + } + } + + private static class IndexerThread extends Thread { + + private JarFile m_jarFile; + public Deobfuscator deobfuscator; + + public IndexerThread(JarFile jarFile) { + m_jarFile = jarFile; + deobfuscator = null; + } + + public void joinOrBail() { + try { + join(); + } catch (InterruptedException ex) { + throw new Error(ex); + } + } + + @Override + public void run() { + try { + deobfuscator = new Deobfuscator(m_jarFile); + } catch (IOException ex) { + throw new Error(ex); + } + } + } } diff --git a/src/cuchaz/enigma/convert/ClassMatches.java b/src/cuchaz/enigma/convert/ClassMatches.java new file mode 100644 index 00000000..f8b2afdc --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassMatches.java @@ -0,0 +1,153 @@ +package cuchaz.enigma.convert; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ClassEntry; + + +public class ClassMatches implements Iterable { + + Collection m_matches; + Map m_matchesBySource; + Map m_matchesByDest; + BiMap m_uniqueMatches; + Map m_ambiguousMatchesBySource; + Map m_ambiguousMatchesByDest; + Set m_unmatchedSourceClasses; + Set m_unmatchedDestClasses; + + public ClassMatches() { + this(new ArrayList()); + } + + public ClassMatches(Collection matches) { + m_matches = matches; + m_matchesBySource = Maps.newHashMap(); + m_matchesByDest = Maps.newHashMap(); + m_uniqueMatches = HashBiMap.create(); + m_ambiguousMatchesBySource = Maps.newHashMap(); + m_ambiguousMatchesByDest = Maps.newHashMap(); + m_unmatchedSourceClasses = Sets.newHashSet(); + m_unmatchedDestClasses = Sets.newHashSet(); + + for (ClassMatch match : matches) { + indexMatch(match); + } + } + + public void add(ClassMatch match) { + m_matches.add(match); + indexMatch(match); + } + + public void remove(ClassMatch match) { + for (ClassEntry sourceClass : match.sourceClasses) { + m_matchesBySource.remove(sourceClass); + m_uniqueMatches.remove(sourceClass); + m_ambiguousMatchesBySource.remove(sourceClass); + m_unmatchedSourceClasses.remove(sourceClass); + } + for (ClassEntry destClass : match.destClasses) { + m_matchesByDest.remove(destClass); + m_uniqueMatches.inverse().remove(destClass); + m_ambiguousMatchesByDest.remove(destClass); + m_unmatchedDestClasses.remove(destClass); + } + m_matches.remove(match); + } + + public int size() { + return m_matches.size(); + } + + @Override + public Iterator iterator() { + return m_matches.iterator(); + } + + private void indexMatch(ClassMatch match) { + if (!match.isMatched()) { + // unmatched + m_unmatchedSourceClasses.addAll(match.sourceClasses); + m_unmatchedDestClasses.addAll(match.destClasses); + } else { + if (match.isAmbiguous()) { + // ambiguously matched + for (ClassEntry entry : match.sourceClasses) { + m_ambiguousMatchesBySource.put(entry, match); + } + for (ClassEntry entry : match.destClasses) { + m_ambiguousMatchesByDest.put(entry, match); + } + } else { + // uniquely matched + m_uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); + } + } + for (ClassEntry entry : match.sourceClasses) { + m_matchesBySource.put(entry, match); + } + for (ClassEntry entry : match.destClasses) { + m_matchesByDest.put(entry, match); + } + } + + public BiMap getUniqueMatches() { + return m_uniqueMatches; + } + + public Set getUnmatchedSourceClasses() { + return m_unmatchedSourceClasses; + } + + public Set getUnmatchedDestClasses() { + return m_unmatchedDestClasses; + } + + public Set getAmbiguouslyMatchedSourceClasses() { + return m_ambiguousMatchesBySource.keySet(); + } + + public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { + return m_ambiguousMatchesBySource.get(sourceClass); + } + + public ClassMatch getMatchBySource(ClassEntry sourceClass) { + return m_matchesBySource.get(sourceClass); + } + + public ClassMatch getMatchByDest(ClassEntry destClass) { + return m_matchesByDest.get(destClass); + } + + public void removeSource(ClassEntry sourceClass) { + ClassMatch match = m_matchesBySource.get(sourceClass); + if (match != null) { + remove(match); + match.sourceClasses.remove(sourceClass); + if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { + add(match); + } + } + } + + public void removeDest(ClassEntry destClass) { + ClassMatch match = m_matchesByDest.get(destClass); + if (match != null) { + remove(match); + match.destClasses.remove(destClass); + if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { + add(match); + } + } + } +} diff --git a/src/cuchaz/enigma/convert/FieldMatches.java b/src/cuchaz/enigma/convert/FieldMatches.java new file mode 100644 index 00000000..f78a8f55 --- /dev/null +++ b/src/cuchaz/enigma/convert/FieldMatches.java @@ -0,0 +1,35 @@ +package cuchaz.enigma.convert; + +import java.util.Collection; +import java.util.Set; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; + + +public class FieldMatches { + + private BiMap m_matches; + private Set m_unmatchedSourceFields; + + public FieldMatches() { + m_matches = HashBiMap.create(); + m_unmatchedSourceFields = Sets.newHashSet(); + } + + public void addUnmatchedSourceFields(Set fieldEntries) { + m_unmatchedSourceFields.addAll(fieldEntries); + } + + public Collection getSourceClassesWithUnmatchedFields() { + Set classEntries = Sets.newHashSet(); + for (FieldEntry fieldEntry : m_unmatchedSourceFields) { + classEntries.add(fieldEntry.getClassEntry()); + } + return classEntries; + } +} diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index 5883878c..667ee9de 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -37,7 +37,7 @@ import cuchaz.enigma.mapping.MethodMapping; public class MappingsConverter { - public static Matches computeMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { + public static ClassMatches computeMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { // index jars System.out.println("Indexing source jar..."); @@ -49,7 +49,7 @@ public class MappingsConverter { // compute the matching ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null); - return new Matches(matching.matches()); + return new ClassMatches(matching.matches()); } public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap knownMatches) { @@ -115,7 +115,7 @@ public class MappingsConverter { return lastMatching; } - public static Mappings newMappings(Matches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { // sort the unique matches by size of inner class chain Multimap> matchesByDestChainSize = HashMultimap.create(); @@ -172,7 +172,7 @@ public class MappingsConverter { return newMappings; } - private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping mapping, final Matches matches, boolean useSimpleName) { + private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping mapping, final ClassMatches matches, boolean useSimpleName) { ClassNameReplacer replacer = new ClassNameReplacer() { @Override diff --git a/src/cuchaz/enigma/convert/Matches.java b/src/cuchaz/enigma/convert/Matches.java deleted file mode 100644 index 19bb155f..00000000 --- a/src/cuchaz/enigma/convert/Matches.java +++ /dev/null @@ -1,153 +0,0 @@ -package cuchaz.enigma.convert; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import cuchaz.enigma.mapping.ClassEntry; - - -public class Matches implements Iterable { - - Collection m_matches; - Map m_matchesBySource; - Map m_matchesByDest; - BiMap m_uniqueMatches; - Map m_ambiguousMatchesBySource; - Map m_ambiguousMatchesByDest; - Set m_unmatchedSourceClasses; - Set m_unmatchedDestClasses; - - public Matches() { - this(new ArrayList()); - } - - public Matches(Collection matches) { - m_matches = matches; - m_matchesBySource = Maps.newHashMap(); - m_matchesByDest = Maps.newHashMap(); - m_uniqueMatches = HashBiMap.create(); - m_ambiguousMatchesBySource = Maps.newHashMap(); - m_ambiguousMatchesByDest = Maps.newHashMap(); - m_unmatchedSourceClasses = Sets.newHashSet(); - m_unmatchedDestClasses = Sets.newHashSet(); - - for (ClassMatch match : matches) { - indexMatch(match); - } - } - - public void add(ClassMatch match) { - m_matches.add(match); - indexMatch(match); - } - - public void remove(ClassMatch match) { - for (ClassEntry sourceClass : match.sourceClasses) { - m_matchesBySource.remove(sourceClass); - m_uniqueMatches.remove(sourceClass); - m_ambiguousMatchesBySource.remove(sourceClass); - m_unmatchedSourceClasses.remove(sourceClass); - } - for (ClassEntry destClass : match.destClasses) { - m_matchesByDest.remove(destClass); - m_uniqueMatches.inverse().remove(destClass); - m_ambiguousMatchesByDest.remove(destClass); - m_unmatchedDestClasses.remove(destClass); - } - m_matches.remove(match); - } - - public int size() { - return m_matches.size(); - } - - @Override - public Iterator iterator() { - return m_matches.iterator(); - } - - private void indexMatch(ClassMatch match) { - if (!match.isMatched()) { - // unmatched - m_unmatchedSourceClasses.addAll(match.sourceClasses); - m_unmatchedDestClasses.addAll(match.destClasses); - } else { - if (match.isAmbiguous()) { - // ambiguously matched - for (ClassEntry entry : match.sourceClasses) { - m_ambiguousMatchesBySource.put(entry, match); - } - for (ClassEntry entry : match.destClasses) { - m_ambiguousMatchesByDest.put(entry, match); - } - } else { - // uniquely matched - m_uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); - } - } - for (ClassEntry entry : match.sourceClasses) { - m_matchesBySource.put(entry, match); - } - for (ClassEntry entry : match.destClasses) { - m_matchesByDest.put(entry, match); - } - } - - public BiMap getUniqueMatches() { - return m_uniqueMatches; - } - - public Set getUnmatchedSourceClasses() { - return m_unmatchedSourceClasses; - } - - public Set getUnmatchedDestClasses() { - return m_unmatchedDestClasses; - } - - public Set getAmbiguouslyMatchedSourceClasses() { - return m_ambiguousMatchesBySource.keySet(); - } - - public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { - return m_ambiguousMatchesBySource.get(sourceClass); - } - - public ClassMatch getMatchBySource(ClassEntry sourceClass) { - return m_matchesBySource.get(sourceClass); - } - - public ClassMatch getMatchByDest(ClassEntry destClass) { - return m_matchesByDest.get(destClass); - } - - public void removeSource(ClassEntry sourceClass) { - ClassMatch match = m_matchesBySource.get(sourceClass); - if (match != null) { - remove(match); - match.sourceClasses.remove(sourceClass); - if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { - add(match); - } - } - } - - public void removeDest(ClassEntry destClass) { - ClassMatch match = m_matchesByDest.get(destClass); - if (match != null) { - remove(match); - match.destClasses.remove(destClass); - if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { - add(match); - } - } - } -} diff --git a/src/cuchaz/enigma/convert/MatchesReader.java b/src/cuchaz/enigma/convert/MatchesReader.java index 808f8d0a..b43535cb 100644 --- a/src/cuchaz/enigma/convert/MatchesReader.java +++ b/src/cuchaz/enigma/convert/MatchesReader.java @@ -14,19 +14,19 @@ import cuchaz.enigma.mapping.ClassEntry; public class MatchesReader { - public static Matches read(File file) + public static ClassMatches readClasses(File file) throws IOException { try (BufferedReader in = new BufferedReader(new FileReader(file))) { - Matches matches = new Matches(); + ClassMatches matches = new ClassMatches(); String line = null; while ((line = in.readLine()) != null) { - matches.add(readMatch(line)); + matches.add(readClassMatch(line)); } return matches; } } - private static ClassMatch readMatch(String line) + private static ClassMatch readClassMatch(String line) throws IOException { String[] sides = line.split(":", 2); return new ClassMatch(readClasses(sides[0]), readClasses(sides[1])); diff --git a/src/cuchaz/enigma/convert/MatchesWriter.java b/src/cuchaz/enigma/convert/MatchesWriter.java index 49ffb6d7..6658e2a3 100644 --- a/src/cuchaz/enigma/convert/MatchesWriter.java +++ b/src/cuchaz/enigma/convert/MatchesWriter.java @@ -9,16 +9,16 @@ import cuchaz.enigma.mapping.ClassEntry; public class MatchesWriter { - public static void write(Matches matches, File file) + public static void writeClasses(ClassMatches matches, File file) throws IOException { try (FileWriter out = new FileWriter(file)) { for (ClassMatch match : matches) { - writeMatch(out, match); + writeClassMatch(out, match); } } } - private static void writeMatch(FileWriter out, ClassMatch match) + private static void writeClassMatch(FileWriter out, ClassMatch match) throws IOException { writeClasses(out, match.sourceClasses); out.write(":"); diff --git a/src/cuchaz/enigma/gui/ClassMatchingGui.java b/src/cuchaz/enigma/gui/ClassMatchingGui.java index ff7cda99..b6744515 100644 --- a/src/cuchaz/enigma/gui/ClassMatchingGui.java +++ b/src/cuchaz/enigma/gui/ClassMatchingGui.java @@ -15,7 +15,6 @@ import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; -import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; @@ -28,22 +27,18 @@ import javax.swing.WindowConstants; import com.beust.jcommander.internal.Lists; import com.beust.jcommander.internal.Maps; import com.google.common.collect.BiMap; -import com.strobel.decompiler.languages.java.ast.CompilationUnit; import cuchaz.enigma.Constants; import cuchaz.enigma.Deobfuscator; -import cuchaz.enigma.analysis.SourceIndex; -import cuchaz.enigma.analysis.Token; import cuchaz.enigma.convert.ClassIdentifier; import cuchaz.enigma.convert.ClassIdentity; import cuchaz.enigma.convert.ClassMatch; +import cuchaz.enigma.convert.ClassMatches; import cuchaz.enigma.convert.ClassMatching; import cuchaz.enigma.convert.ClassNamer; import cuchaz.enigma.convert.MappingsConverter; -import cuchaz.enigma.convert.Matches; import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsChecker; import de.sciss.syntaxpane.DefaultSyntaxKit; @@ -55,21 +50,21 @@ public class ClassMatchingGui { Matched { @Override - public Collection getSourceClasses(Matches matches) { + public Collection getSourceClasses(ClassMatches matches) { return matches.getUniqueMatches().keySet(); } }, Unmatched { @Override - public Collection getSourceClasses(Matches matches) { + public Collection getSourceClasses(ClassMatches matches) { return matches.getUnmatchedSourceClasses(); } }, Ambiguous { @Override - public Collection getSourceClasses(Matches matches) { + public Collection getSourceClasses(ClassMatches matches) { return matches.getAmbiguouslyMatchedSourceClasses(); } }; @@ -82,7 +77,7 @@ public class ClassMatchingGui { return button; } - public abstract Collection getSourceClasses(Matches matches); + public abstract Collection getSourceClasses(ClassMatches matches); public static SourceType getDefault() { return values()[0]; @@ -90,23 +85,22 @@ public class ClassMatchingGui { } public static interface SaveListener { - public void save(Matches matches); + public void save(ClassMatches matches); } // controls private JFrame m_frame; private ClassSelector m_sourceClasses; private ClassSelector m_destClasses; - private JEditorPane m_sourceReader; - private JEditorPane m_destReader; + private CodeReader m_sourceReader; + private CodeReader m_destReader; private JLabel m_sourceClassLabel; private JLabel m_destClassLabel; private JButton m_matchButton; private Map m_sourceTypeButtons; private JCheckBox m_advanceCheck; - private SelectionHighlightPainter m_selectionHighlightPainter; - private Matches m_matches; + private ClassMatches m_classMatches; private Deobfuscator m_sourceDeobfuscator; private Deobfuscator m_destDeobfuscator; private ClassEntry m_sourceClass; @@ -114,14 +108,14 @@ public class ClassMatchingGui { private SourceType m_sourceType; private SaveListener m_saveListener; - public ClassMatchingGui(Matches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + public ClassMatchingGui(ClassMatches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { - m_matches = matches; + m_classMatches = matches; m_sourceDeobfuscator = sourceDeobfuscator; m_destDeobfuscator = destDeobfuscator; // init frame - m_frame = new JFrame(Constants.Name); + m_frame = new JFrame(Constants.Name + " - Class Matcher"); final Container pane = m_frame.getContentPane(); pane.setLayout(new BorderLayout()); @@ -188,8 +182,8 @@ public class ClassMatchingGui { // init source panels DefaultSyntaxKit.initKit(); - m_sourceReader = makeReader(); - m_destReader = makeReader(); + m_sourceReader = new CodeReader(); + m_destReader = new CodeReader(); // init all the splits JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader)); @@ -238,8 +232,6 @@ public class ClassMatchingGui { m_frame.setVisible(true); m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - m_selectionHighlightPainter = new SelectionHighlightPainter(); - // init state updateDestMappings(); setSourceType(SourceType.getDefault()); @@ -247,19 +239,6 @@ public class ClassMatchingGui { m_saveListener = null; } - private JEditorPane makeReader() { - - JEditorPane reader = new JEditorPane(); - reader.setEditable(false); - reader.setContentType("text/java"); - - // turn off token highlighting (it's wrong most of the time anyway...) - DefaultSyntaxKit kit = (DefaultSyntaxKit)reader.getEditorKit(); - kit.toggleComponent(reader, "de.sciss.syntaxpane.components.TokenMarker"); - - return reader; - } - public void setSaveListener(SaveListener val) { m_saveListener = val; } @@ -267,7 +246,7 @@ public class ClassMatchingGui { private void updateDestMappings() { Mappings newMappings = MappingsConverter.newMappings( - m_matches, + m_classMatches, m_sourceDeobfuscator.getMappings(), m_sourceDeobfuscator, m_destDeobfuscator @@ -294,13 +273,13 @@ public class ClassMatchingGui { // show the source classes m_sourceType = val; - m_sourceClasses.setClasses(deobfuscateClasses(m_sourceType.getSourceClasses(m_matches), m_sourceDeobfuscator)); + m_sourceClasses.setClasses(deobfuscateClasses(m_sourceType.getSourceClasses(m_classMatches), m_sourceDeobfuscator)); // update counts for (SourceType sourceType : SourceType.values()) { m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", sourceType.name(), - sourceType.getSourceClasses(m_matches).size() + sourceType.getSourceClasses(m_classMatches).size() )); } } @@ -345,7 +324,7 @@ public class ClassMatchingGui { if (m_sourceClass != null) { // show the dest class(es) - ClassMatch match = m_matches.getMatchBySource(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); + ClassMatch match = m_classMatches.getMatchBySource(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); assert(match != null); if (match.destClasses.isEmpty()) { @@ -376,7 +355,12 @@ public class ClassMatchingGui { } setDestClass(null); - decompileClass(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); + m_sourceReader.decompileClass(m_sourceClass, m_sourceDeobfuscator, new Runnable() { + @Override + public void run() { + m_sourceReader.navigateToClassDeclaration(m_sourceClass); + } + }); updateMatchButton(); } @@ -386,7 +370,7 @@ public class ClassMatchingGui { ClassEntry obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass); // set up identifiers - ClassNamer namer = new ClassNamer(m_matches.getUniqueMatches()); + ClassNamer namer = new ClassNamer(m_classMatches.getUniqueMatches()); ClassIdentifier sourceIdentifier = new ClassIdentifier( m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(), namer.getSourceNamer(), true @@ -401,7 +385,7 @@ public class ClassMatchingGui { // rank all the unmatched dest classes against the source class ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); List scoredDestClasses = Lists.newArrayList(); - for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { + for (ClassEntry unmatchedDestClass : m_classMatches.getUnmatchedDestClasses()) { ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); @@ -420,59 +404,14 @@ public class ClassMatchingGui { m_destClass = classEntry; m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : ""); - decompileClass(m_destClass, m_destDeobfuscator, m_destReader); - - updateMatchButton(); - } - - protected void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { - - if (classEntry == null) { - reader.setText(null); - return; - } - - reader.setText("(decompiling...)"); - - // run in a separate thread to keep ui responsive - new Thread() { + m_destReader.decompileClass(m_destClass, m_destDeobfuscator, new Runnable() { @Override public void run() { - - // get the outermost class - ClassEntry outermostClassEntry = classEntry; - while (outermostClassEntry.isInnerClass()) { - outermostClassEntry = outermostClassEntry.getOuterClassEntry(); - } - - // decompile it - CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName()); - String source = deobfuscator.getSource(sourceTree); - reader.setText(source); - SourceIndex sourceIndex = deobfuscator.getSourceIndex(sourceTree, source); - - // navigate to the class declaration - Token token = sourceIndex.getDeclarationToken(classEntry); - if (token == null) { - // couldn't find the class declaration token, might be an anonymous class - // look for any declaration in that class instead - for (Entry entry : sourceIndex.declarations()) { - if (entry.getClassEntry().equals(classEntry)) { - token = sourceIndex.getDeclarationToken(entry); - break; - } - } - } - - if (token != null) { - GuiTricks.navigateToToken(reader, token, m_selectionHighlightPainter); - } else { - // couldn't find anything =( - System.out.println("Unable to find declaration in source for " + classEntry); - } - + m_destReader.navigateToClassDeclaration(m_destClass); } - }.start(); + }); + + updateMatchButton(); } private void updateMatchButton() { @@ -480,7 +419,7 @@ public class ClassMatchingGui { ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); ClassEntry obfDest = m_destDeobfuscator.obfuscateEntry(m_destClass); - BiMap uniqueMatches = m_matches.getUniqueMatches(); + BiMap uniqueMatches = m_classMatches.getUniqueMatches(); boolean twoSelected = m_sourceClass != null && m_destClass != null; boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); boolean canMatch = !uniqueMatches.containsKey(obfSource) && ! uniqueMatches.containsValue(obfDest); @@ -529,11 +468,11 @@ public class ClassMatchingGui { ClassEntry obfDest = m_destDeobfuscator.obfuscateEntry(m_destClass); // remove the classes from their match - m_matches.removeSource(obfSource); - m_matches.removeDest(obfDest); + m_classMatches.removeSource(obfSource); + m_classMatches.removeDest(obfDest); // add them as matched classes - m_matches.add(new ClassMatch(obfSource, obfDest)); + m_classMatches.add(new ClassMatch(obfSource, obfDest)); ClassEntry nextClass = null; if (m_advanceCheck.isSelected()) { @@ -554,8 +493,8 @@ public class ClassMatchingGui { ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); // remove the source to break the match, then add the source back as unmatched - m_matches.removeSource(obfSource); - m_matches.add(new ClassMatch(obfSource, null)); + m_classMatches.removeSource(obfSource); + m_classMatches.add(new ClassMatch(obfSource, null)); save(); updateMatches(); @@ -577,7 +516,7 @@ public class ClassMatchingGui { private void save() { if (m_saveListener != null) { - m_saveListener.save(m_matches); + m_saveListener.save(m_classMatches); } } @@ -589,15 +528,15 @@ public class ClassMatchingGui { ClassMatching matching = MappingsConverter.computeMatching( m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(), m_destDeobfuscator.getJar(), m_destDeobfuscator.getJarIndex(), - m_matches.getUniqueMatches() + m_classMatches.getUniqueMatches() ); - Matches newMatches = new Matches(matching.matches()); + ClassMatches newMatches = new ClassMatches(matching.matches()); System.out.println(String.format("Automatch found %d new matches", - newMatches.getUniqueMatches().size() - m_matches.getUniqueMatches().size() + newMatches.getUniqueMatches().size() - m_classMatches.getUniqueMatches().size() )); // update the current matches - m_matches = newMatches; + m_classMatches = newMatches; save(); updateMatches(); } diff --git a/src/cuchaz/enigma/gui/CodeReader.java b/src/cuchaz/enigma/gui/CodeReader.java new file mode 100644 index 00000000..05feb59e --- /dev/null +++ b/src/cuchaz/enigma/gui/CodeReader.java @@ -0,0 +1,164 @@ +package cuchaz.enigma.gui; + +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JEditorPane; +import javax.swing.SwingUtilities; +import javax.swing.Timer; +import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter.HighlightPainter; + +import com.strobel.decompiler.languages.java.ast.CompilationUnit; + +import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import de.sciss.syntaxpane.DefaultSyntaxKit; + + +public class CodeReader extends JEditorPane { + + private static final long serialVersionUID = 3673180950485748810L; + + private static final Object m_lock = new Object(); + + private SelectionHighlightPainter m_highlightPainter; + private SourceIndex m_sourceIndex; + + public CodeReader() { + + setEditable(false); + setContentType("text/java"); + + // turn off token highlighting (it's wrong most of the time anyway...) + DefaultSyntaxKit kit = (DefaultSyntaxKit)getEditorKit(); + kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker"); + + m_highlightPainter = new SelectionHighlightPainter(); + m_sourceIndex = null; + } + + public void setCode(String code) { + // sadly, the java lexer is not thread safe, so we have to serialize all these calls + synchronized (m_lock) { + setText(code); + } + } + + public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) { + decompileClass(classEntry, deobfuscator, null); + } + + public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Runnable callback) { + + if (classEntry == null) { + setCode(null); + return; + } + + setCode("(decompiling...)"); + + // run decompilation in a separate thread to keep ui responsive + new Thread() { + @Override + public void run() { + + // get the outermost class + ClassEntry outermostClassEntry = classEntry; + while (outermostClassEntry.isInnerClass()) { + outermostClassEntry = outermostClassEntry.getOuterClassEntry(); + } + + // decompile it + CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName()); + String source = deobfuscator.getSource(sourceTree); + setCode(source); + m_sourceIndex = deobfuscator.getSourceIndex(sourceTree, source); + + if (callback != null) { + callback.run(); + } + } + }.start(); + } + + public void navigateToClassDeclaration(ClassEntry classEntry) { + + // navigate to the class declaration + Token token = m_sourceIndex.getDeclarationToken(classEntry); + if (token == null) { + // couldn't find the class declaration token, might be an anonymous class + // look for any declaration in that class instead + for (Entry entry : m_sourceIndex.declarations()) { + if (entry.getClassEntry().equals(classEntry)) { + token = m_sourceIndex.getDeclarationToken(entry); + break; + } + } + } + + if (token != null) { + navigateToToken(token); + } else { + // couldn't find anything =( + System.out.println("Unable to find declaration in source for " + classEntry); + } + } + + public void navigateToToken(final Token token) { + navigateToToken(this, token, m_highlightPainter); + } + + // HACKHACK: someday we can update the main GUI to use this code reader + public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { + + // set the caret position to the token + editor.setCaretPosition(token.start); + editor.grabFocus(); + + try { + // make sure the token is visible in the scroll window + Rectangle start = editor.modelToView(token.start); + Rectangle end = editor.modelToView(token.end); + final Rectangle show = start.union(end); + show.grow(start.width * 10, start.height * 6); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + editor.scrollRectToVisible(show); + } + }); + } catch (BadLocationException ex) { + throw new Error(ex); + } + + // highlight the token momentarily + final Timer timer = new Timer(200, new ActionListener() { + private int m_counter = 0; + private Object m_highlight = null; + + @Override + public void actionPerformed(ActionEvent event) { + if (m_counter % 2 == 0) { + try { + m_highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter); + } catch (BadLocationException ex) { + // don't care + } + } else if (m_highlight != null) { + editor.getHighlighter().removeHighlight(m_highlight); + } + + if (m_counter++ > 6) { + Timer timer = (Timer)event.getSource(); + timer.stop(); + } + } + }); + timer.start(); + } +} diff --git a/src/cuchaz/enigma/gui/FieldMatchingGui.java b/src/cuchaz/enigma/gui/FieldMatchingGui.java new file mode 100644 index 00000000..de9ba142 --- /dev/null +++ b/src/cuchaz/enigma/gui/FieldMatchingGui.java @@ -0,0 +1,131 @@ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.util.Map; + +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.WindowConstants; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.convert.ClassMatches; +import cuchaz.enigma.convert.FieldMatches; +import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.FieldMapping; +import de.sciss.syntaxpane.DefaultSyntaxKit; + + +public class FieldMatchingGui { + + public static interface SaveListener { + public void save(FieldMatches matches); + } + + // controls + private JFrame m_frame; + private ClassSelector m_sourceClasses; + private CodeReader m_sourceReader; + private CodeReader m_destReader; + + private ClassMatches m_classMatches; + private FieldMatches m_fieldMatches; + private Map m_droppedFieldMappings; + private Deobfuscator m_sourceDeobfuscator; + private Deobfuscator m_destDeobfuscator; + private SaveListener m_saveListener; + + public FieldMatchingGui(ClassMatches classMatches, FieldMatches fieldMatches, Map droppedFieldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + + m_classMatches = classMatches; + m_fieldMatches = fieldMatches; + m_droppedFieldMappings = droppedFieldMappings; + m_sourceDeobfuscator = sourceDeobfuscator; + m_destDeobfuscator = destDeobfuscator; + + // init frame + m_frame = new JFrame(Constants.Name + " - Field Matcher"); + final Container pane = m_frame.getContentPane(); + pane.setLayout(new BorderLayout()); + + // init classes side + JPanel classesPanel = new JPanel(); + classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS)); + classesPanel.setPreferredSize(new Dimension(200, 0)); + pane.add(classesPanel, BorderLayout.WEST); + classesPanel.add(new JLabel("Classes")); + + m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); + m_sourceClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + setSourceClass(classEntry); + } + }); + JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); + classesPanel.add(sourceScroller); + + // init fields side + JPanel fieldsPanel = new JPanel(); + fieldsPanel.setLayout(new BoxLayout(fieldsPanel, BoxLayout.PAGE_AXIS)); + fieldsPanel.setPreferredSize(new Dimension(200, 0)); + pane.add(fieldsPanel, BorderLayout.WEST); + fieldsPanel.add(new JLabel("Destination Fields")); + + // init readers + DefaultSyntaxKit.initKit(); + m_sourceReader = new CodeReader(); + m_destReader = new CodeReader(); + + // init all the splits + JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, new JScrollPane(m_sourceReader)); + splitLeft.setResizeWeight(0); // let the right side take all the slack + JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_destReader), fieldsPanel); + splitRight.setResizeWeight(1); // let the left side take all the slack + JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); + splitCenter.setResizeWeight(0.5); // resize 50:50 + pane.add(splitCenter, BorderLayout.CENTER); + splitCenter.resetToPreferredSizes(); + + // init bottom panel + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new FlowLayout()); + + // show the frame + pane.doLayout(); + m_frame.setSize(1024, 576); + m_frame.setMinimumSize(new Dimension(640, 480)); + m_frame.setVisible(true); + m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + // init state + m_saveListener = null; + m_fieldMatches.addUnmatchedSourceFields(droppedFieldMappings.keySet()); + m_sourceClasses.setClasses(m_fieldMatches.getSourceClassesWithUnmatchedFields()); + } + + public void setSaveListener(SaveListener val) { + m_saveListener = val; + } + + protected void setSourceClass(ClassEntry obfSourceClass) { + + // get the matched dest class + final ClassEntry obfDestClass = m_classMatches.getUniqueMatches().get(obfSourceClass); + if (obfDestClass == null) { + throw new Error("No matching dest class for source class: " + obfSourceClass); + } + + m_sourceReader.decompileClass(obfSourceClass, m_sourceDeobfuscator); + m_destReader.decompileClass(obfDestClass, m_destDeobfuscator); + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 38a6ee9a..ea05d255 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -737,7 +737,7 @@ public class Gui { if (token == null) { throw new IllegalArgumentException("Token cannot be null!"); } - GuiTricks.navigateToToken(m_editor, token, m_selectionHighlightPainter); + CodeReader.navigateToToken(m_editor, token, m_selectionHighlightPainter); redraw(); } diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java index 5a3a01de..df9e2215 100644 --- a/src/cuchaz/enigma/gui/GuiTricks.java +++ b/src/cuchaz/enigma/gui/GuiTricks.java @@ -11,21 +11,11 @@ package cuchaz.enigma.gui; import java.awt.Font; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import javax.swing.JComponent; -import javax.swing.JEditorPane; import javax.swing.JLabel; -import javax.swing.SwingUtilities; -import javax.swing.Timer; import javax.swing.ToolTipManager; -import javax.swing.text.BadLocationException; -import javax.swing.text.Highlighter.HighlightPainter; - -import cuchaz.enigma.analysis.Token; public class GuiTricks { @@ -43,52 +33,4 @@ public class GuiTricks { manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); manager.setInitialDelay(oldDelay); } - - public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { - - // set the caret position to the token - editor.setCaretPosition(token.start); - editor.grabFocus(); - - try { - // make sure the token is visible in the scroll window - Rectangle start = editor.modelToView(token.start); - Rectangle end = editor.modelToView(token.end); - final Rectangle show = start.union(end); - show.grow(start.width * 10, start.height * 6); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - editor.scrollRectToVisible(show); - } - }); - } catch (BadLocationException ex) { - throw new Error(ex); - } - - // highlight the token momentarily - final Timer timer = new Timer(200, new ActionListener() { - private int m_counter = 0; - private Object m_highlight = null; - - @Override - public void actionPerformed(ActionEvent event) { - if (m_counter % 2 == 0) { - try { - m_highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter); - } catch (BadLocationException ex) { - // don't care - } - } else if (m_highlight != null) { - editor.getHighlighter().removeHighlight(m_highlight); - } - - if (m_counter++ > 6) { - Timer timer = (Timer)event.getSource(); - timer.stop(); - } - } - }); - timer.start(); - } } -- cgit v1.2.3 From 1ad33bfe0a96b1b4a1f3c02cf2c054e8a101dfd8 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Mar 2015 20:08:15 -0400 Subject: field matcher is starting to be useful --- src/cuchaz/enigma/ConvertMain.java | 135 +++++++++++++---- src/cuchaz/enigma/analysis/JarIndex.java | 28 +++- src/cuchaz/enigma/convert/FieldMatches.java | 69 +++++++-- src/cuchaz/enigma/convert/MatchesReader.java | 40 +++++ src/cuchaz/enigma/convert/MatchesWriter.java | 38 +++++ src/cuchaz/enigma/gui/ClassMatchingGui.java | 3 - src/cuchaz/enigma/gui/CodeReader.java | 56 ++++++- src/cuchaz/enigma/gui/FieldMatchingGui.java | 212 +++++++++++++++++++++++---- src/cuchaz/enigma/mapping/EntryFactory.java | 16 ++ 9 files changed, 525 insertions(+), 72 deletions(-) diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index 624eb40a..a5a00e8b 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -4,8 +4,12 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.Set; import java.util.jar.JarFile; +import com.google.common.collect.BiMap; +import com.google.common.collect.Sets; + import cuchaz.enigma.convert.ClassMatches; import cuchaz.enigma.convert.FieldMatches; import cuchaz.enigma.convert.MappingsConverter; @@ -13,11 +17,18 @@ import cuchaz.enigma.convert.MatchesReader; import cuchaz.enigma.convert.MatchesWriter; import cuchaz.enigma.gui.ClassMatchingGui; import cuchaz.enigma.gui.FieldMatchingGui; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.ClassNameReplacer; +import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.FieldMapping; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsChecker; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; +import cuchaz.enigma.mapping.Type; public class ConvertMain { @@ -32,13 +43,14 @@ public class ConvertMain { File inMappingsFile = new File("../Enigma Mappings/1.8.mappings"); File outMappingsFile = new File("../Enigma Mappings/1.8.3.mappings"); Mappings mappings = new MappingsReader().read(new FileReader(inMappingsFile)); - File classMatchingFile = new File(inMappingsFile.getName() + ".class.matching"); - File fieldMatchingFile = new File(inMappingsFile.getName() + ".field.matching"); + File classMatchesFile = new File(inMappingsFile.getName() + ".class.matches"); + File fieldMatchesFile = new File(inMappingsFile.getName() + ".field.matches"); - //computeMatches(classMatchingFile, sourceJar, destJar, mappings); + //computeClassMatches(classMatchingFile, sourceJar, destJar, mappings); //editClasssMatches(classMatchingFile, sourceJar, destJar, mappings); //convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchingFile); - editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchingFile, fieldMatchingFile); + //computeFieldMatches(fieldMatchesFile, destJar, outMappingsFile, classMatchesFile); + editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, fieldMatchesFile); /* TODO // write out the converted mappings @@ -49,17 +61,17 @@ public class ConvertMain { */ } - private static void computeMatches(File classMatchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) + private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) throws IOException { ClassMatches classMatches = MappingsConverter.computeMatches(sourceJar, destJar, mappings); - MatchesWriter.writeClasses(classMatches, classMatchingFile); - System.out.println("Wrote:\n\t" + classMatchingFile.getAbsolutePath()); + MatchesWriter.writeClasses(classMatches, classMatchesFile); + System.out.println("Wrote:\n\t" + classMatchesFile.getAbsolutePath()); } - private static void editClasssMatches(final File classMatchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) + private static void editClasssMatches(final File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) throws IOException { - System.out.println("Reading matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchingFile); + System.out.println("Reading class matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); deobfuscators.source.setMappings(mappings); System.out.println("Starting GUI..."); @@ -67,7 +79,7 @@ public class ConvertMain { @Override public void save(ClassMatches matches) { try { - MatchesWriter.writeClasses(matches, classMatchingFile); + MatchesWriter.writeClasses(matches, classMatchesFile); } catch (IOException ex) { throw new Error(ex); } @@ -75,10 +87,10 @@ public class ConvertMain { }); } - private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchingFile) + private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile) throws IOException { - System.out.println("Reading matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchingFile); + System.out.println("Reading class matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); deobfuscators.source.setMappings(mappings); @@ -90,19 +102,90 @@ public class ConvertMain { System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath()); } - private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchingFile, final File fieldMatchingFile) + private static void computeFieldMatches(File fieldMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile) throws IOException, MappingParseException { - System.out.println("Reading matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchingFile); - FieldMatches fieldMatches; - if (fieldMatchingFile.exists() /* TEMP */ && false) { - // TODO - //fieldMatches = MatchesReader.readFields(fieldMatchingFile); - } else { - fieldMatches = new FieldMatches(); + System.out.println("Reading class matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); + System.out.println("Reading mappings..."); + Mappings destMappings = new MappingsReader().read(new FileReader(destMappingsFile)); + System.out.println("Indexing dest jar..."); + Deobfuscator destDeobfuscator = new Deobfuscator(destJar); + + System.out.println("Writing field matches..."); + + // get the matched and unmatched field mappings + FieldMatches fieldMatches = new FieldMatches(); + + // unmatched source fields are easy + MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); + checker.dropBrokenMappings(destMappings); + for (FieldEntry destObfField : checker.getDroppedFieldMappings().keySet()) { + FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); + fieldMatches.addUnmatchedSourceField(srcObfField); + } + + // get matched fields (anything that's left after the checks/drops is matched( + for (ClassMapping classMapping : destMappings.classes()) { + collectMatchedFields(fieldMatches, classMapping, classMatches); + } + + // get unmatched dest fields + Set unmatchedDestFields = Sets.newHashSet(); + for (FieldEntry destFieldEntry : destDeobfuscator.getJarIndex().getObfFieldEntries()) { + if (!fieldMatches.isDestMatched(destFieldEntry)) { + unmatchedDestFields.add(destFieldEntry); + } + } + fieldMatches.addUnmatchedDestFields(unmatchedDestFields); + + MatchesWriter.writeFields(fieldMatches, fieldMatchesFile); + System.out.println("Wrote:\n\t" + fieldMatchesFile.getAbsolutePath()); + } + + private static void collectMatchedFields(FieldMatches fieldMatches, ClassMapping destClassMapping, ClassMatches classMatches) { + + // get the fields for this class + for (FieldMapping destFieldMapping : destClassMapping.fields()) { + FieldEntry destObfField = EntryFactory.getObfFieldEntry(destClassMapping, destFieldMapping); + FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); + fieldMatches.addMatch(srcObfField, destObfField); } + // recurse + for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { + collectMatchedFields(fieldMatches, destInnerClassMapping, classMatches); + } + } + + private static FieldEntry translate(FieldEntry in, BiMap map) { + return new FieldEntry( + map.get(in.getClassEntry()), + in.getName(), + translate(in.getType(), map) + ); + } + + private static Type translate(Type type, final BiMap map) { + return new Type(type, new ClassNameReplacer() { + @Override + public String replace(String inClassName) { + ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); + if (outClassEntry == null) { + return null; + } + return outClassEntry.getName(); + } + }); + } + + private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File fieldMatchesFile) + throws IOException, MappingParseException { + + System.out.println("Reading matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); + FieldMatches fieldMatches = MatchesReader.readFields(fieldMatchesFile); + // prep deobfuscators Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); deobfuscators.source.setMappings(sourceMappings); @@ -111,16 +194,14 @@ public class ConvertMain { checker.dropBrokenMappings(destMappings); deobfuscators.dest.setMappings(destMappings); - new FieldMatchingGui(classMatches, fieldMatches, checker.getDroppedFieldMappings(), deobfuscators.source, deobfuscators.dest).setSaveListener(new FieldMatchingGui.SaveListener() { + new FieldMatchingGui(classMatches, fieldMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(new FieldMatchingGui.SaveListener() { @Override public void save(FieldMatches matches) { - /* TODO try { - MatchesWriter.writeFields(matches, fieldMatchingFile); + MatchesWriter.writeFields(matches, fieldMatchesFile); } catch (IOException ex) { throw new Error(ex); } - */ } }); } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index e0a8bf52..7ebbd974 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -60,6 +60,8 @@ public class JarIndex { private TranslationIndex m_translationIndex; private Multimap m_interfaces; private Map m_access; + private Multimap m_fields; + private Multimap m_behaviors; private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; private Multimap> m_fieldReferences; @@ -73,6 +75,8 @@ public class JarIndex { m_translationIndex = new TranslationIndex(); m_interfaces = HashMultimap.create(); m_access = Maps.newHashMap(); + m_fields = HashMultimap.create(); + m_behaviors = HashMultimap.create(); m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); m_fieldReferences = HashMultimap.create(); @@ -97,10 +101,14 @@ public class JarIndex { for (CtClass c : JarClassIterator.classes(jar)) { ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); for (CtField field : c.getDeclaredFields()) { - m_access.put(EntryFactory.getFieldEntry(field), Access.get(field)); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); + m_access.put(fieldEntry, Access.get(field)); + m_fields.put(fieldEntry.getClassEntry(), fieldEntry); } for (CtBehavior behavior : c.getDeclaredBehaviors()) { - m_access.put(EntryFactory.getBehaviorEntry(behavior), Access.get(behavior)); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + m_access.put(behaviorEntry, Access.get(behavior)); + m_behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry); } } @@ -520,6 +528,22 @@ public class JarIndex { return m_obfClassEntries; } + public Collection getObfFieldEntries() { + return m_fields.values(); + } + + public Collection getObfFieldEntries(ClassEntry classEntry) { + return m_fields.get(classEntry); + } + + public Collection getObfBehaviorEntries() { + return m_behaviors.values(); + } + + public Collection getObfBehaviorEntries(ClassEntry classEntry) { + return m_behaviors.get(classEntry); + } + public TranslationIndex getTranslationIndex() { return m_translationIndex; } diff --git a/src/cuchaz/enigma/convert/FieldMatches.java b/src/cuchaz/enigma/convert/FieldMatches.java index f78a8f55..6335974b 100644 --- a/src/cuchaz/enigma/convert/FieldMatches.java +++ b/src/cuchaz/enigma/convert/FieldMatches.java @@ -5,7 +5,8 @@ import java.util.Set; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -import com.google.common.collect.Sets; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.FieldEntry; @@ -14,22 +15,70 @@ import cuchaz.enigma.mapping.FieldEntry; public class FieldMatches { private BiMap m_matches; - private Set m_unmatchedSourceFields; + private Multimap m_unmatchedSourceFields; + private Multimap m_unmatchedDestFields; public FieldMatches() { m_matches = HashBiMap.create(); - m_unmatchedSourceFields = Sets.newHashSet(); + m_unmatchedSourceFields = HashMultimap.create(); + m_unmatchedDestFields = HashMultimap.create(); } - public void addUnmatchedSourceFields(Set fieldEntries) { - m_unmatchedSourceFields.addAll(fieldEntries); + public void addMatch(FieldEntry srcField, FieldEntry destField) { + m_matches.put(srcField, destField); } - public Collection getSourceClassesWithUnmatchedFields() { - Set classEntries = Sets.newHashSet(); - for (FieldEntry fieldEntry : m_unmatchedSourceFields) { - classEntries.add(fieldEntry.getClassEntry()); + public void addUnmatchedSourceField(FieldEntry fieldEntry) { + m_unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry); + } + + public void addUnmatchedSourceFields(Iterable fieldEntries) { + for (FieldEntry fieldEntry : fieldEntries) { + addUnmatchedSourceField(fieldEntry); + } + } + + public void addUnmatchedDestField(FieldEntry fieldEntry) { + m_unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry); + } + + public void addUnmatchedDestFields(Iterable fieldEntries) { + for (FieldEntry fieldEntry : fieldEntries) { + addUnmatchedDestField(fieldEntry); } - return classEntries; + } + + public Set getSourceClassesWithUnmatchedFields() { + return m_unmatchedSourceFields.keySet(); + } + + public Collection getUnmatchedSourceFields() { + return m_unmatchedSourceFields.values(); + } + + public Collection getUnmatchedSourceFields(ClassEntry sourceClass) { + return m_unmatchedSourceFields.get(sourceClass); + } + + public Collection getUnmatchedDestFields() { + return m_unmatchedDestFields.values(); + } + + public Collection getUnmatchedDestFields(ClassEntry sourceClass) { + return m_unmatchedDestFields.get(sourceClass); + } + + public BiMap matches() { + return m_matches; + } + + public boolean isDestMatched(FieldEntry destFieldEntry) { + return m_matches.containsValue(destFieldEntry); + } + + public void makeMatch(FieldEntry sourceField, FieldEntry destField) { + m_unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); + m_unmatchedDestFields.remove(destField.getClassEntry(), destField); + m_matches.put(sourceField, destField); } } diff --git a/src/cuchaz/enigma/convert/MatchesReader.java b/src/cuchaz/enigma/convert/MatchesReader.java index b43535cb..1dd042da 100644 --- a/src/cuchaz/enigma/convert/MatchesReader.java +++ b/src/cuchaz/enigma/convert/MatchesReader.java @@ -10,6 +10,8 @@ import java.util.List; import com.beust.jcommander.internal.Lists; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.Type; public class MatchesReader { @@ -42,4 +44,42 @@ public class MatchesReader { } return entries; } + + public static FieldMatches readFields(File file) + throws IOException { + try (BufferedReader in = new BufferedReader(new FileReader(file))) { + FieldMatches matches = new FieldMatches(); + String line = null; + while ((line = in.readLine()) != null) { + readFieldMatch(matches, line); + } + return matches; + } + } + + private static void readFieldMatch(FieldMatches matches, String line) { + String[] parts = line.split(":", 2); + FieldEntry source = readField(parts[0]); + FieldEntry dest = readField(parts[1]); + if (source != null && dest != null) { + matches.addMatch(source, dest); + } else if (source != null) { + matches.addUnmatchedSourceField(source); + } else if (dest != null) { + matches.addUnmatchedDestField(dest); + } + } + + private static FieldEntry readField(String in) { + if (in.length() <= 0) { + return null; + } + String[] parts = in.split(" "); + assert(parts.length == 3); + return new FieldEntry( + new ClassEntry(parts[0]), + parts[1], + new Type(parts[2]) + ); + } } diff --git a/src/cuchaz/enigma/convert/MatchesWriter.java b/src/cuchaz/enigma/convert/MatchesWriter.java index 6658e2a3..6e371bcc 100644 --- a/src/cuchaz/enigma/convert/MatchesWriter.java +++ b/src/cuchaz/enigma/convert/MatchesWriter.java @@ -3,8 +3,10 @@ package cuchaz.enigma.convert; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.util.Map; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; public class MatchesWriter { @@ -38,4 +40,40 @@ public class MatchesWriter { out.write(entry.toString()); } } + + public static void writeFields(FieldMatches fieldMatches, File file) + throws IOException { + try (FileWriter out = new FileWriter(file)) { + for (Map.Entry match : fieldMatches.matches().entrySet()) { + writeFieldMatch(out, match.getKey(), match.getValue()); + } + for (FieldEntry fieldEntry : fieldMatches.getUnmatchedSourceFields()) { + writeFieldMatch(out, fieldEntry, null); + } + for (FieldEntry fieldEntry : fieldMatches.getUnmatchedDestFields()) { + writeFieldMatch(out, null, fieldEntry); + } + } + } + + private static void writeFieldMatch(FileWriter out, FieldEntry source, FieldEntry dest) + throws IOException { + if (source != null) { + writeField(out, source); + } + out.write(":"); + if (dest != null) { + writeField(out, dest); + } + out.write("\n"); + } + + private static void writeField(FileWriter out, FieldEntry fieldEntry) + throws IOException { + out.write(fieldEntry.getClassName()); + out.write(" "); + out.write(fieldEntry.getName()); + out.write(" "); + out.write(fieldEntry.getType().toString()); + } } diff --git a/src/cuchaz/enigma/gui/ClassMatchingGui.java b/src/cuchaz/enigma/gui/ClassMatchingGui.java index b6744515..6943c3ee 100644 --- a/src/cuchaz/enigma/gui/ClassMatchingGui.java +++ b/src/cuchaz/enigma/gui/ClassMatchingGui.java @@ -201,13 +201,10 @@ public class ClassMatchingGui { m_sourceClassLabel = new JLabel(); m_sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT); - m_sourceClassLabel.setPreferredSize(new Dimension(300, 24)); m_destClassLabel = new JLabel(); m_destClassLabel.setHorizontalAlignment(SwingConstants.LEFT); - m_destClassLabel.setPreferredSize(new Dimension(300, 24)); m_matchButton = new JButton(); - m_matchButton.setPreferredSize(new Dimension(140, 24)); m_advanceCheck = new JCheckBox("Advance to next likely match"); m_advanceCheck.addActionListener(new ActionListener() { diff --git a/src/cuchaz/enigma/gui/CodeReader.java b/src/cuchaz/enigma/gui/CodeReader.java index 05feb59e..aa7e2db2 100644 --- a/src/cuchaz/enigma/gui/CodeReader.java +++ b/src/cuchaz/enigma/gui/CodeReader.java @@ -7,12 +7,15 @@ import java.awt.event.ActionListener; import javax.swing.JEditorPane; import javax.swing.SwingUtilities; import javax.swing.Timer; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; import javax.swing.text.BadLocationException; import javax.swing.text.Highlighter.HighlightPainter; import com.strobel.decompiler.languages.java.ast.CompilationUnit; import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ClassEntry; @@ -26,8 +29,13 @@ public class CodeReader extends JEditorPane { private static final Object m_lock = new Object(); - private SelectionHighlightPainter m_highlightPainter; + public static interface SelectionListener { + void onSelect(EntryReference reference); + } + + private SelectionHighlightPainter m_selectionHighlightPainter; private SourceIndex m_sourceIndex; + private SelectionListener m_selectionListener; public CodeReader() { @@ -38,8 +46,28 @@ public class CodeReader extends JEditorPane { DefaultSyntaxKit kit = (DefaultSyntaxKit)getEditorKit(); kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker"); - m_highlightPainter = new SelectionHighlightPainter(); + // hook events + addCaretListener(new CaretListener() { + @Override + public void caretUpdate(CaretEvent event) { + if (m_selectionListener != null && m_sourceIndex != null) { + Token token = m_sourceIndex.getReferenceToken(event.getDot()); + if (token != null) { + m_selectionListener.onSelect(m_sourceIndex.getDeobfReference(token)); + } else { + m_selectionListener.onSelect(null); + } + } + } + }); + + m_selectionHighlightPainter = new SelectionHighlightPainter(); m_sourceIndex = null; + m_selectionListener = null; + } + + public void setSelectionListener(SelectionListener val) { + m_selectionListener = val; } public void setCode(String code) { @@ -49,6 +77,10 @@ public class CodeReader extends JEditorPane { } } + public SourceIndex getSourceIndex() { + return m_sourceIndex; + } + public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) { decompileClass(classEntry, deobfuscator, null); } @@ -110,7 +142,7 @@ public class CodeReader extends JEditorPane { } public void navigateToToken(final Token token) { - navigateToToken(this, token, m_highlightPainter); + navigateToToken(this, token, m_selectionHighlightPainter); } // HACKHACK: someday we can update the main GUI to use this code reader @@ -161,4 +193,22 @@ public class CodeReader extends JEditorPane { }); timer.start(); } + + public void setHighlightedTokens(Iterable tokens, HighlightPainter painter) { + for (Token token : tokens) { + setHighlightedToken(token, painter); + } + } + + public void setHighlightedToken(Token token, HighlightPainter painter) { + try { + getHighlighter().addHighlight(token.start, token.end, painter); + } catch (BadLocationException ex) { + throw new IllegalArgumentException(ex); + } + } + + public void clearHighlights() { + getHighlighter().removeAllHighlights(); + } } diff --git a/src/cuchaz/enigma/gui/FieldMatchingGui.java b/src/cuchaz/enigma/gui/FieldMatchingGui.java index de9ba142..ef374c87 100644 --- a/src/cuchaz/enigma/gui/FieldMatchingGui.java +++ b/src/cuchaz/enigma/gui/FieldMatchingGui.java @@ -4,24 +4,34 @@ import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; -import java.util.Map; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Collection; +import java.util.Set; import javax.swing.BoxLayout; +import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.WindowConstants; +import javax.swing.text.Highlighter.HighlightPainter; + +import com.google.common.collect.Sets; import cuchaz.enigma.Constants; import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; import cuchaz.enigma.convert.ClassMatches; import cuchaz.enigma.convert.FieldMatches; import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.FieldMapping; import de.sciss.syntaxpane.DefaultSyntaxKit; @@ -36,19 +46,28 @@ public class FieldMatchingGui { private ClassSelector m_sourceClasses; private CodeReader m_sourceReader; private CodeReader m_destReader; + private JButton m_matchButton; + private JLabel m_sourceLabel; + private JLabel m_destLabel; + private HighlightPainter m_unmatchedHighlightPainter; + private HighlightPainter m_matchedHighlightPainter; private ClassMatches m_classMatches; private FieldMatches m_fieldMatches; - private Map m_droppedFieldMappings; private Deobfuscator m_sourceDeobfuscator; private Deobfuscator m_destDeobfuscator; private SaveListener m_saveListener; + private ClassEntry m_obfSourceClass; + private ClassEntry m_obfDestClass; + private FieldEntry m_obfSourceField; + private FieldEntry m_obfDestField; + private Set m_obfUnmatchedSourceFields; + private Set m_obfUnmatchedDestFields; - public FieldMatchingGui(ClassMatches classMatches, FieldMatches fieldMatches, Map droppedFieldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + public FieldMatchingGui(ClassMatches classMatches, FieldMatches fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { m_classMatches = classMatches; m_fieldMatches = fieldMatches; - m_droppedFieldMappings = droppedFieldMappings; m_sourceDeobfuscator = sourceDeobfuscator; m_destDeobfuscator = destDeobfuscator; @@ -74,31 +93,57 @@ public class FieldMatchingGui { JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); classesPanel.add(sourceScroller); - // init fields side - JPanel fieldsPanel = new JPanel(); - fieldsPanel.setLayout(new BoxLayout(fieldsPanel, BoxLayout.PAGE_AXIS)); - fieldsPanel.setPreferredSize(new Dimension(200, 0)); - pane.add(fieldsPanel, BorderLayout.WEST); - fieldsPanel.add(new JLabel("Destination Fields")); - // init readers DefaultSyntaxKit.initKit(); m_sourceReader = new CodeReader(); + m_sourceReader.setSelectionListener(new CodeReader.SelectionListener() { + @Override + public void onSelect(EntryReference reference) { + if (reference != null) { + onSelectSource(reference.entry); + } else { + onSelectSource(null); + } + } + }); m_destReader = new CodeReader(); + m_destReader.setSelectionListener(new CodeReader.SelectionListener() { + @Override + public void onSelect(EntryReference reference) { + if (reference != null) { + onSelectDest(reference.entry); + } else { + onSelectDest(null); + } + } + }); // init all the splits - JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, new JScrollPane(m_sourceReader)); + JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_sourceReader), new JScrollPane(m_destReader)); + splitRight.setResizeWeight(0.5); // resize 50:50 + JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight); splitLeft.setResizeWeight(0); // let the right side take all the slack - JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_destReader), fieldsPanel); - splitRight.setResizeWeight(1); // let the left side take all the slack - JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); - splitCenter.setResizeWeight(0.5); // resize 50:50 - pane.add(splitCenter, BorderLayout.CENTER); - splitCenter.resetToPreferredSizes(); + pane.add(splitLeft, BorderLayout.CENTER); + splitLeft.resetToPreferredSizes(); // init bottom panel JPanel bottomPanel = new JPanel(); bottomPanel.setLayout(new FlowLayout()); + pane.add(bottomPanel, BorderLayout.SOUTH); + + m_matchButton = new JButton("Match"); + m_matchButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + match(); + } + }); + + m_sourceLabel = new JLabel(); + bottomPanel.add(m_sourceLabel); + bottomPanel.add(m_matchButton); + m_destLabel = new JLabel(); + bottomPanel.add(m_destLabel); // show the frame pane.doLayout(); @@ -106,26 +151,139 @@ public class FieldMatchingGui { m_frame.setMinimumSize(new Dimension(640, 480)); m_frame.setVisible(true); m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + m_unmatchedHighlightPainter = new ObfuscatedHighlightPainter(); + m_matchedHighlightPainter = new DeobfuscatedHighlightPainter(); // init state m_saveListener = null; - m_fieldMatches.addUnmatchedSourceFields(droppedFieldMappings.keySet()); - m_sourceClasses.setClasses(m_fieldMatches.getSourceClassesWithUnmatchedFields()); + m_obfSourceClass = null; + m_obfDestClass = null; + m_obfSourceField = null; + m_obfDestField = null; + m_obfUnmatchedSourceFields = null; + m_obfUnmatchedDestFields = null; + updateSourceClasses(); + updateMatchButton(); } public void setSaveListener(SaveListener val) { m_saveListener = val; } + private void updateSourceClasses() { + m_sourceClasses.setClasses(m_fieldMatches.getSourceClassesWithUnmatchedFields()); + m_sourceClasses.expandAll(); + } + protected void setSourceClass(ClassEntry obfSourceClass) { - // get the matched dest class - final ClassEntry obfDestClass = m_classMatches.getUniqueMatches().get(obfSourceClass); - if (obfDestClass == null) { - throw new Error("No matching dest class for source class: " + obfSourceClass); + m_obfSourceClass = obfSourceClass; + m_obfDestClass = m_classMatches.getUniqueMatches().get(obfSourceClass); + if (m_obfDestClass == null) { + throw new Error("No matching dest class for source class: " + m_obfSourceClass); } + + updateUnmatchedFields(); + m_sourceReader.decompileClass(m_obfSourceClass, m_sourceDeobfuscator, new Runnable() { + @Override + public void run() { + updateSourceHighlights(); + } + }); + m_destReader.decompileClass(m_obfDestClass, m_destDeobfuscator, new Runnable() { + @Override + public void run() { + updateDestHighlights(); + } + }); + } + + private void updateUnmatchedFields() { + m_obfUnmatchedSourceFields = Sets.newHashSet(m_fieldMatches.getUnmatchedSourceFields(m_obfSourceClass)); + m_obfUnmatchedDestFields = Sets.newHashSet(); + for (FieldEntry destFieldEntry : m_destDeobfuscator.getJarIndex().getObfFieldEntries(m_obfDestClass)) { + if (!m_fieldMatches.isDestMatched(destFieldEntry)) { + m_obfUnmatchedDestFields.add(destFieldEntry); + } + } + } + + protected void updateSourceHighlights() { + highlightFields(m_sourceReader, m_sourceDeobfuscator, m_obfUnmatchedSourceFields, m_fieldMatches.matches().keySet()); + } + + protected void updateDestHighlights() { + highlightFields(m_destReader, m_destDeobfuscator, m_obfUnmatchedDestFields, m_fieldMatches.matches().values()); + } + + private void highlightFields(CodeReader reader, Deobfuscator deobfuscator, Collection obfFieldEntries, Collection obfMatchedFieldEntries) { + reader.clearHighlights(); + SourceIndex index = reader.getSourceIndex(); + for (FieldEntry obfFieldEntry : obfFieldEntries) { + FieldEntry deobfFieldEntry = deobfuscator.deobfuscateEntry(obfFieldEntry); + Token token = index.getDeclarationToken(deobfFieldEntry); + if (token == null) { + System.err.println("WARNING: Can't find declaration token for " + deobfFieldEntry); + } else { + reader.setHighlightedToken( + token, + obfMatchedFieldEntries.contains(obfFieldEntry) ? m_matchedHighlightPainter : m_unmatchedHighlightPainter + ); + } + } + } + + protected void onSelectSource(Entry entry) { + m_sourceLabel.setText(""); + m_obfSourceField = null; + if (entry != null && entry instanceof FieldEntry) { + FieldEntry fieldEntry = (FieldEntry)entry; + FieldEntry obfFieldEntry = m_sourceDeobfuscator.obfuscateEntry(fieldEntry); + if (m_obfUnmatchedSourceFields.contains(obfFieldEntry)) { + m_obfSourceField = obfFieldEntry; + m_sourceLabel.setText(fieldEntry.getName() + " " + fieldEntry.getType().toString()); + } + } + updateMatchButton(); + } - m_sourceReader.decompileClass(obfSourceClass, m_sourceDeobfuscator); - m_destReader.decompileClass(obfDestClass, m_destDeobfuscator); + protected void onSelectDest(Entry entry) { + m_destLabel.setText(""); + m_obfDestField = null; + if (entry != null && entry instanceof FieldEntry) { + FieldEntry fieldEntry = (FieldEntry)entry; + FieldEntry obfFieldEntry = m_destDeobfuscator.obfuscateEntry(fieldEntry); + if (m_obfUnmatchedDestFields.contains(obfFieldEntry)) { + m_obfDestField = obfFieldEntry; + m_destLabel.setText(fieldEntry.getName() + " " + fieldEntry.getType().toString()); + } + } + updateMatchButton(); + } + + private void updateMatchButton() { + m_matchButton.setEnabled(m_obfSourceField != null && m_obfDestField != null); + } + + protected void match() { + + // update the field matches + m_fieldMatches.makeMatch(m_obfSourceField, m_obfDestField); + save(); + + // update the ui + onSelectSource(null); + onSelectDest(null); + updateUnmatchedFields(); + updateSourceHighlights(); + updateDestHighlights(); + updateSourceClasses(); + } + + private void save() { + if (m_saveListener != null) { + m_saveListener.save(m_fieldMatches); + } } } diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index f4d62c8e..333bb09b 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java @@ -26,6 +26,10 @@ public class EntryFactory { return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); } + private static ClassEntry getObfClassEntry(ClassMapping classMapping) { + return new ClassEntry(classMapping.getObfFullName()); + } + public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { return new ClassEntry(classMapping.getDeobfName()); } @@ -50,6 +54,14 @@ public class EntryFactory { ); } + public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { + return new FieldEntry( + getObfClassEntry(classMapping), + fieldMapping.getObfName(), + fieldMapping.getObfType() + ); + } + public static MethodEntry getMethodEntry(CtMethod method) { return new MethodEntry( getClassEntry(method.getDeclaringClass()), @@ -148,4 +160,8 @@ public class EntryFactory { public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); } + + public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) { + return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping); + } } -- cgit v1.2.3 From 430df87ba5d855ca29bc53a5765a2862d2209098 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 10 Mar 2015 00:55:03 -0400 Subject: tweaks and improvements to field matching gui --- src/cuchaz/enigma/ConvertMain.java | 73 +---- src/cuchaz/enigma/Deobfuscator.java | 12 +- src/cuchaz/enigma/analysis/SourceIndex.java | 8 +- .../analysis/SourceIndexBehaviorVisitor.java | 10 +- src/cuchaz/enigma/convert/FieldMatches.java | 81 +++++- src/cuchaz/enigma/convert/MappingsConverter.java | 130 ++++++--- src/cuchaz/enigma/convert/MatchesReader.java | 22 +- src/cuchaz/enigma/convert/MatchesWriter.java | 10 + src/cuchaz/enigma/gui/ClassMatchingGui.java | 24 +- src/cuchaz/enigma/gui/CodeReader.java | 8 +- src/cuchaz/enigma/gui/FieldMatchingGui.java | 317 ++++++++++++++++----- src/cuchaz/enigma/gui/GuiTricks.java | 20 ++ 12 files changed, 495 insertions(+), 220 deletions(-) diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index a5a00e8b..a45fb35c 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -4,12 +4,8 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import java.util.Set; import java.util.jar.JarFile; -import com.google.common.collect.BiMap; -import com.google.common.collect.Sets; - import cuchaz.enigma.convert.ClassMatches; import cuchaz.enigma.convert.FieldMatches; import cuchaz.enigma.convert.MappingsConverter; @@ -17,18 +13,11 @@ import cuchaz.enigma.convert.MatchesReader; import cuchaz.enigma.convert.MatchesWriter; import cuchaz.enigma.gui.ClassMatchingGui; import cuchaz.enigma.gui.FieldMatchingGui; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.ClassMapping; -import cuchaz.enigma.mapping.ClassNameReplacer; -import cuchaz.enigma.mapping.EntryFactory; -import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.FieldMapping; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsChecker; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; -import cuchaz.enigma.mapping.Type; public class ConvertMain { @@ -63,7 +52,7 @@ public class ConvertMain { private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) throws IOException { - ClassMatches classMatches = MappingsConverter.computeMatches(sourceJar, destJar, mappings); + ClassMatches classMatches = MappingsConverter.computeClassMatches(sourceJar, destJar, mappings); MatchesWriter.writeClasses(classMatches, classMatchesFile); System.out.println("Wrote:\n\t" + classMatchesFile.getAbsolutePath()); } @@ -115,70 +104,12 @@ public class ConvertMain { System.out.println("Writing field matches..."); // get the matched and unmatched field mappings - FieldMatches fieldMatches = new FieldMatches(); - - // unmatched source fields are easy - MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); - checker.dropBrokenMappings(destMappings); - for (FieldEntry destObfField : checker.getDroppedFieldMappings().keySet()) { - FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); - fieldMatches.addUnmatchedSourceField(srcObfField); - } - - // get matched fields (anything that's left after the checks/drops is matched( - for (ClassMapping classMapping : destMappings.classes()) { - collectMatchedFields(fieldMatches, classMapping, classMatches); - } - - // get unmatched dest fields - Set unmatchedDestFields = Sets.newHashSet(); - for (FieldEntry destFieldEntry : destDeobfuscator.getJarIndex().getObfFieldEntries()) { - if (!fieldMatches.isDestMatched(destFieldEntry)) { - unmatchedDestFields.add(destFieldEntry); - } - } - fieldMatches.addUnmatchedDestFields(unmatchedDestFields); + FieldMatches fieldMatches = MappingsConverter.computeFieldMatches(destDeobfuscator, destMappings, classMatches); MatchesWriter.writeFields(fieldMatches, fieldMatchesFile); System.out.println("Wrote:\n\t" + fieldMatchesFile.getAbsolutePath()); } - private static void collectMatchedFields(FieldMatches fieldMatches, ClassMapping destClassMapping, ClassMatches classMatches) { - - // get the fields for this class - for (FieldMapping destFieldMapping : destClassMapping.fields()) { - FieldEntry destObfField = EntryFactory.getObfFieldEntry(destClassMapping, destFieldMapping); - FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); - fieldMatches.addMatch(srcObfField, destObfField); - } - - // recurse - for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { - collectMatchedFields(fieldMatches, destInnerClassMapping, classMatches); - } - } - - private static FieldEntry translate(FieldEntry in, BiMap map) { - return new FieldEntry( - map.get(in.getClassEntry()), - in.getName(), - translate(in.getType(), map) - ); - } - - private static Type translate(Type type, final BiMap map) { - return new Type(type, new ClassNameReplacer() { - @Override - public String replace(String inClassName) { - ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); - if (outClassEntry == null) { - return null; - } - return outClassEntry.getName(); - } - }); - } - private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File fieldMatchesFile) throws IOException, MappingParseException { diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 35cfd0b6..f5012bde 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -225,8 +225,18 @@ public class Deobfuscator { } public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { + return getSourceIndex(sourceTree, source, null); + } + + public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, Boolean ignoreBadTokens) { + // build the source index - SourceIndex index = new SourceIndex(source); + SourceIndex index; + if (ignoreBadTokens != null) { + index = new SourceIndex(source, ignoreBadTokens); + } else { + index = new SourceIndex(source); + } sourceTree.acceptVisitor(new SourceIndexVisitor(), index); // DEBUG diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index b3fb751a..8f751ef5 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -32,9 +32,15 @@ public class SourceIndex { private Multimap,Token> m_referenceToTokens; private Map m_declarationToToken; private List m_lineOffsets; + private boolean m_ignoreBadTokens; public SourceIndex(String source) { + this(source, true); + } + + public SourceIndex(String source, boolean ignoreBadTokens) { m_source = source; + m_ignoreBadTokens = ignoreBadTokens; m_tokenToReference = Maps.newTreeMap(); m_referenceToTokens = HashMultimap.create(); m_declarationToToken = Maps.newHashMap(); @@ -83,7 +89,7 @@ public class SourceIndex { // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); // if the token has a $ in it, something's wrong. Ignore this token - if (name.lastIndexOf('$') >= 0) { + if (name.lastIndexOf('$') >= 0 && m_ignoreBadTokens) { // DEBUG System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); return null; diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index a9a055be..eb120b66 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -111,10 +111,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { @Override public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); - MethodDefinition methodDef = (MethodDefinition)def.getMethod(); - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(methodDef); - ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); - index.addDeclaration(node.getNameToken(), argumentEntry); + if (def.getMethod() instanceof MethodDefinition) { + MethodDefinition methodDef = (MethodDefinition)def.getMethod(); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(methodDef); + ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); + index.addDeclaration(node.getNameToken(), argumentEntry); + } return recurse(node, index); } diff --git a/src/cuchaz/enigma/convert/FieldMatches.java b/src/cuchaz/enigma/convert/FieldMatches.java index 6335974b..2973356b 100644 --- a/src/cuchaz/enigma/convert/FieldMatches.java +++ b/src/cuchaz/enigma/convert/FieldMatches.java @@ -7,6 +7,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.FieldEntry; @@ -15,21 +16,29 @@ import cuchaz.enigma.mapping.FieldEntry; public class FieldMatches { private BiMap m_matches; + private Multimap m_matchedSourceFields; private Multimap m_unmatchedSourceFields; private Multimap m_unmatchedDestFields; + private Multimap m_unmatchableSourceFields; public FieldMatches() { m_matches = HashBiMap.create(); + m_matchedSourceFields = HashMultimap.create(); m_unmatchedSourceFields = HashMultimap.create(); m_unmatchedDestFields = HashMultimap.create(); + m_unmatchableSourceFields = HashMultimap.create(); } public void addMatch(FieldEntry srcField, FieldEntry destField) { - m_matches.put(srcField, destField); + boolean wasAdded = m_matches.put(srcField, destField) == null; + assert (wasAdded); + wasAdded = m_matchedSourceFields.put(srcField.getClassEntry(), srcField); + assert (wasAdded); } public void addUnmatchedSourceField(FieldEntry fieldEntry) { - m_unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry); + boolean wasAdded = m_unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry); + assert (wasAdded); } public void addUnmatchedSourceFields(Iterable fieldEntries) { @@ -39,7 +48,8 @@ public class FieldMatches { } public void addUnmatchedDestField(FieldEntry fieldEntry) { - m_unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry); + boolean wasAdded = m_unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry); + assert (wasAdded); } public void addUnmatchedDestFields(Iterable fieldEntries) { @@ -48,9 +58,21 @@ public class FieldMatches { } } + public void addUnmatchableSourceField(FieldEntry sourceField) { + boolean wasAdded = m_unmatchableSourceFields.put(sourceField.getClassEntry(), sourceField); + assert (wasAdded); + } + public Set getSourceClassesWithUnmatchedFields() { return m_unmatchedSourceFields.keySet(); } + + public Collection getSourceClassesWithoutUnmatchedFields() { + Set out = Sets.newHashSet(); + out.addAll(m_matchedSourceFields.keySet()); + out.removeAll(m_unmatchedSourceFields.keySet()); + return out; + } public Collection getUnmatchedSourceFields() { return m_unmatchedSourceFields.values(); @@ -64,21 +86,60 @@ public class FieldMatches { return m_unmatchedDestFields.values(); } - public Collection getUnmatchedDestFields(ClassEntry sourceClass) { - return m_unmatchedDestFields.get(sourceClass); + public Collection getUnmatchedDestFields(ClassEntry destClass) { + return m_unmatchedDestFields.get(destClass); + } + + public Collection getUnmatchableSourceFields() { + return m_unmatchableSourceFields.values(); + } + + public boolean hasSource(FieldEntry fieldEntry) { + return m_matches.containsKey(fieldEntry) || m_unmatchedSourceFields.containsValue(fieldEntry); + } + + public boolean hasDest(FieldEntry fieldEntry) { + return m_matches.containsValue(fieldEntry) || m_unmatchedDestFields.containsValue(fieldEntry); } public BiMap matches() { return m_matches; } + + public boolean isMatchedSourceField(FieldEntry sourceField) { + return m_matches.containsKey(sourceField); + } - public boolean isDestMatched(FieldEntry destFieldEntry) { - return m_matches.containsValue(destFieldEntry); + public boolean isMatchedDestField(FieldEntry destField) { + return m_matches.containsValue(destField); } public void makeMatch(FieldEntry sourceField, FieldEntry destField) { - m_unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); - m_unmatchedDestFields.remove(destField.getClassEntry(), destField); - m_matches.put(sourceField, destField); + boolean wasRemoved = m_unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); + assert (wasRemoved); + wasRemoved = m_unmatchedDestFields.remove(destField.getClassEntry(), destField); + assert (wasRemoved); + addMatch(sourceField, destField); + } + + public boolean isMatched(FieldEntry sourceField, FieldEntry destField) { + FieldEntry match = m_matches.get(sourceField); + return match != null && match.equals(destField); + } + + public void unmakeMatch(FieldEntry sourceField, FieldEntry destField) { + boolean wasRemoved = m_matches.remove(sourceField) != null; + assert (wasRemoved); + wasRemoved = m_matchedSourceFields.remove(sourceField.getClassEntry(), sourceField); + assert (wasRemoved); + addUnmatchedSourceField(sourceField); + addUnmatchedDestField(destField); + } + + public void makeSourceUnmatchable(FieldEntry sourceField) { + assert(!isMatchedSourceField(sourceField)); + boolean wasRemoved = m_unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); + assert (wasRemoved); + addUnmatchableSourceField(sourceField); } } diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index 667ee9de..9ab1baa3 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -17,6 +17,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.jar.JarFile; import com.beust.jcommander.internal.Lists; @@ -24,6 +25,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.JarIndex; @@ -31,13 +33,17 @@ import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; import cuchaz.enigma.mapping.ClassNameReplacer; +import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.FieldMapping; import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsChecker; import cuchaz.enigma.mapping.MethodMapping; +import cuchaz.enigma.mapping.Type; public class MappingsConverter { - public static ClassMatches computeMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { + public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { // index jars System.out.println("Indexing source jar..."); @@ -245,47 +251,101 @@ public class MappingsConverter { mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); } } - - /* TODO: after we get a mapping, check to see that the other entries match - public static void checkMethods() { + + public static FieldMatches computeFieldMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches) { + + FieldMatches fieldMatches = new FieldMatches(); + + // unmatched source fields are easy + MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); + checker.dropBrokenMappings(destMappings); + for (FieldEntry destObfField : checker.getDroppedFieldMappings().keySet()) { + FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); + fieldMatches.addUnmatchedSourceField(srcObfField); + } - // check the method matches - System.out.println("Checking methods..."); - for (ClassMapping classMapping : mappings.classes()) { - ClassEntry classEntry = new ClassEntry(classMapping.getObfFullName()); - for (MethodMapping methodMapping : classMapping.methods()) { + // get matched fields (anything that's left after the checks/drops is matched( + for (ClassMapping classMapping : destMappings.classes()) { + collectMatchedFields(fieldMatches, classMapping, classMatches); + } + + // get unmatched dest fields + for (FieldEntry destFieldEntry : destDeobfuscator.getJarIndex().getObfFieldEntries()) { + if (!fieldMatches.isMatchedDestField(destFieldEntry)) { + fieldMatches.addUnmatchedDestField(destFieldEntry); + } + } + + System.out.println("Automatching " + fieldMatches.getUnmatchedSourceFields().size() + " unmatched source fields..."); + + // go through the unmatched source fields and try to pick out the easy matches + for (ClassEntry obfSourceClass : Lists.newArrayList(fieldMatches.getSourceClassesWithUnmatchedFields())) { + for (FieldEntry obfSourceField : Lists.newArrayList(fieldMatches.getUnmatchedSourceFields(obfSourceClass))) { - // skip constructors - if (methodMapping.getObfName().equals("")) { - continue; - } + // get the possible dest matches + ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); - MethodEntry methodEntry = new MethodEntry( - classEntry, - methodMapping.getObfName(), - methodMapping.getObfSignature() - ); - if (!destIndex.containsObfBehavior(methodEntry)) { - System.err.println("WARNING: method doesn't match: " + methodEntry); - - // TODO: show methods if needed - // show the available methods - System.err.println("\tAvailable dest methods:"); - CtClass c = destLoader.loadClass(classMapping.getObfFullName()); - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); - } - - System.err.println("\tAvailable source methods:"); - c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfFullName())); - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); + // filter by type + Set obfDestFields = Sets.newHashSet(); + for (FieldEntry obfDestField : fieldMatches.getUnmatchedDestFields(obfDestClass)) { + Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse()); + if (translatedDestType.equals(obfSourceField.getType())) { + obfDestFields.add(obfDestField); } } + + if (obfDestFields.size() == 1) { + // make the easy match + FieldEntry obfDestField = obfDestFields.iterator().next(); + fieldMatches.makeMatch(obfSourceField, obfDestField); + } else if (obfDestFields.isEmpty()) { + // no match is possible =( + fieldMatches.makeSourceUnmatchable(obfSourceField); + } } } - System.out.println("Done!"); + System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source fields", + fieldMatches.getUnmatchedSourceFields().size(), + fieldMatches.getUnmatchableSourceFields().size() + )); + + return fieldMatches; + } + + private static void collectMatchedFields(FieldMatches fieldMatches, ClassMapping destClassMapping, ClassMatches classMatches) { + + // get the fields for this class + for (FieldMapping destFieldMapping : destClassMapping.fields()) { + FieldEntry destObfField = EntryFactory.getObfFieldEntry(destClassMapping, destFieldMapping); + FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); + fieldMatches.addMatch(srcObfField, destObfField); + } + + // recurse + for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { + collectMatchedFields(fieldMatches, destInnerClassMapping, classMatches); + } + } + + private static FieldEntry translate(FieldEntry in, BiMap map) { + return new FieldEntry( + map.get(in.getClassEntry()), + in.getName(), + translate(in.getType(), map) + ); + } + + private static Type translate(Type type, final BiMap map) { + return new Type(type, new ClassNameReplacer() { + @Override + public String replace(String inClassName) { + ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); + if (outClassEntry == null) { + return null; + } + return outClassEntry.getName(); + } + }); } - */ } diff --git a/src/cuchaz/enigma/convert/MatchesReader.java b/src/cuchaz/enigma/convert/MatchesReader.java index 1dd042da..921ab1d0 100644 --- a/src/cuchaz/enigma/convert/MatchesReader.java +++ b/src/cuchaz/enigma/convert/MatchesReader.java @@ -58,15 +58,19 @@ public class MatchesReader { } private static void readFieldMatch(FieldMatches matches, String line) { - String[] parts = line.split(":", 2); - FieldEntry source = readField(parts[0]); - FieldEntry dest = readField(parts[1]); - if (source != null && dest != null) { - matches.addMatch(source, dest); - } else if (source != null) { - matches.addUnmatchedSourceField(source); - } else if (dest != null) { - matches.addUnmatchedDestField(dest); + if (line.startsWith("!")) { + matches.addUnmatchableSourceField(readField(line.substring(1))); + } else { + String[] parts = line.split(":", 2); + FieldEntry source = readField(parts[0]); + FieldEntry dest = readField(parts[1]); + if (source != null && dest != null) { + matches.addMatch(source, dest); + } else if (source != null) { + matches.addUnmatchedSourceField(source); + } else if (dest != null) { + matches.addUnmatchedDestField(dest); + } } } diff --git a/src/cuchaz/enigma/convert/MatchesWriter.java b/src/cuchaz/enigma/convert/MatchesWriter.java index 6e371bcc..2118dd08 100644 --- a/src/cuchaz/enigma/convert/MatchesWriter.java +++ b/src/cuchaz/enigma/convert/MatchesWriter.java @@ -53,6 +53,9 @@ public class MatchesWriter { for (FieldEntry fieldEntry : fieldMatches.getUnmatchedDestFields()) { writeFieldMatch(out, null, fieldEntry); } + for (FieldEntry fieldEntry : fieldMatches.getUnmatchableSourceFields()) { + writeUnmatchableField(out, fieldEntry); + } } } @@ -68,6 +71,13 @@ public class MatchesWriter { out.write("\n"); } + private static void writeUnmatchableField(FileWriter out, FieldEntry fieldEntry) + throws IOException { + out.write("!"); + writeField(out, fieldEntry); + out.write("\n"); + } + private static void writeField(FileWriter out, FieldEntry fieldEntry) throws IOException { out.write(fieldEntry.getClassName()); diff --git a/src/cuchaz/enigma/gui/ClassMatchingGui.java b/src/cuchaz/enigma/gui/ClassMatchingGui.java index 6943c3ee..9e210ec4 100644 --- a/src/cuchaz/enigma/gui/ClassMatchingGui.java +++ b/src/cuchaz/enigma/gui/ClassMatchingGui.java @@ -6,7 +6,6 @@ import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; @@ -421,17 +420,17 @@ public class ClassMatchingGui { boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); boolean canMatch = !uniqueMatches.containsKey(obfSource) && ! uniqueMatches.containsValue(obfDest); - deactivateButton(m_matchButton); + GuiTricks.deactivateButton(m_matchButton); if (twoSelected) { if (isMatched) { - activateButton(m_matchButton, "Unmatch", new ActionListener() { + GuiTricks.activateButton(m_matchButton, "Unmatch", new ActionListener() { @Override public void actionPerformed(ActionEvent event) { onUnmatchClick(); } }); } else if (canMatch) { - activateButton(m_matchButton, "Match", new ActionListener() { + GuiTricks.activateButton(m_matchButton, "Match", new ActionListener() { @Override public void actionPerformed(ActionEvent event) { onMatchClick(); @@ -440,23 +439,6 @@ public class ClassMatchingGui { } } } - - private void deactivateButton(JButton button) { - button.setEnabled(false); - button.setText(""); - for (ActionListener listener : Arrays.asList(button.getActionListeners())) { - button.removeActionListener(listener); - } - } - - private void activateButton(JButton button, String text, ActionListener newListener) { - button.setText(text); - button.setEnabled(true); - for (ActionListener listener : Arrays.asList(button.getActionListeners())) { - button.removeActionListener(listener); - } - button.addActionListener(newListener); - } private void onMatchClick() { // precondition: source and dest classes are set correctly diff --git a/src/cuchaz/enigma/gui/CodeReader.java b/src/cuchaz/enigma/gui/CodeReader.java index aa7e2db2..743ef2e4 100644 --- a/src/cuchaz/enigma/gui/CodeReader.java +++ b/src/cuchaz/enigma/gui/CodeReader.java @@ -85,7 +85,11 @@ public class CodeReader extends JEditorPane { decompileClass(classEntry, deobfuscator, null); } - public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Runnable callback) { + public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) { + decompileClass(classEntry, deobfuscator, null, callback); + } + + public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) { if (classEntry == null) { setCode(null); @@ -109,7 +113,7 @@ public class CodeReader extends JEditorPane { CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName()); String source = deobfuscator.getSource(sourceTree); setCode(source); - m_sourceIndex = deobfuscator.getSourceIndex(sourceTree, source); + m_sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens); if (callback != null) { callback.run(); diff --git a/src/cuchaz/enigma/gui/FieldMatchingGui.java b/src/cuchaz/enigma/gui/FieldMatchingGui.java index ef374c87..3f4a378c 100644 --- a/src/cuchaz/enigma/gui/FieldMatchingGui.java +++ b/src/cuchaz/enigma/gui/FieldMatchingGui.java @@ -6,20 +6,26 @@ import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; import java.util.Collection; -import java.util.Set; +import java.util.List; +import java.util.Map; import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.WindowConstants; import javax.swing.text.Highlighter.HighlightPainter; -import com.google.common.collect.Sets; +import com.beust.jcommander.internal.Lists; +import com.beust.jcommander.internal.Maps; import cuchaz.enigma.Constants; import cuchaz.enigma.Deobfuscator; @@ -37,16 +43,49 @@ import de.sciss.syntaxpane.DefaultSyntaxKit; public class FieldMatchingGui { + private static enum SourceType { + Matched { + + @Override + public Collection getObfSourceClasses(FieldMatches matches) { + return matches.getSourceClassesWithoutUnmatchedFields(); + } + }, + Unmatched { + + @Override + public Collection getObfSourceClasses(FieldMatches matches) { + return matches.getSourceClassesWithUnmatchedFields(); + } + }; + + public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { + JRadioButton button = new JRadioButton(name(), this == getDefault()); + button.setActionCommand(name()); + button.addActionListener(listener); + group.add(button); + return button; + } + + public abstract Collection getObfSourceClasses(FieldMatches matches); + + public static SourceType getDefault() { + return values()[0]; + } + } + public static interface SaveListener { public void save(FieldMatches matches); } // controls private JFrame m_frame; + private Map m_sourceTypeButtons; private ClassSelector m_sourceClasses; private CodeReader m_sourceReader; private CodeReader m_destReader; private JButton m_matchButton; + private JButton m_unmatchableButton; private JLabel m_sourceLabel; private JLabel m_destLabel; private HighlightPainter m_unmatchedHighlightPainter; @@ -57,12 +96,11 @@ public class FieldMatchingGui { private Deobfuscator m_sourceDeobfuscator; private Deobfuscator m_destDeobfuscator; private SaveListener m_saveListener; + private SourceType m_sourceType; private ClassEntry m_obfSourceClass; private ClassEntry m_obfDestClass; private FieldEntry m_obfSourceField; private FieldEntry m_obfDestField; - private Set m_obfUnmatchedSourceFields; - private Set m_obfUnmatchedDestFields; public FieldMatchingGui(ClassMatches classMatches, FieldMatches fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { @@ -83,6 +121,24 @@ public class FieldMatchingGui { pane.add(classesPanel, BorderLayout.WEST); classesPanel.add(new JLabel("Classes")); + // init source type radios + JPanel sourceTypePanel = new JPanel(); + classesPanel.add(sourceTypePanel); + sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); + ActionListener sourceTypeListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + setSourceType(SourceType.valueOf(event.getActionCommand())); + } + }; + ButtonGroup sourceTypeButtons = new ButtonGroup(); + m_sourceTypeButtons = Maps.newHashMap(); + for (SourceType sourceType : SourceType.values()) { + JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); + m_sourceTypeButtons.put(sourceType, button); + sourceTypePanel.add(button); + } + m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); m_sourceClasses.setListener(new ClassSelectionListener() { @Override @@ -117,6 +173,20 @@ public class FieldMatchingGui { } } }); + + // add key bindings + KeyAdapter keyListener = new KeyAdapter() { + @Override + public void keyPressed(KeyEvent event) { + switch (event.getKeyCode()) { + case KeyEvent.VK_M: + m_matchButton.doClick(); + break; + } + } + }; + m_sourceReader.addKeyListener(keyListener); + m_destReader.addKeyListener(keyListener); // init all the splits JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_sourceReader), new JScrollPane(m_destReader)); @@ -131,17 +201,13 @@ public class FieldMatchingGui { bottomPanel.setLayout(new FlowLayout()); pane.add(bottomPanel, BorderLayout.SOUTH); - m_matchButton = new JButton("Match"); - m_matchButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - match(); - } - }); + m_matchButton = new JButton(); + m_unmatchableButton = new JButton(); m_sourceLabel = new JLabel(); bottomPanel.add(m_sourceLabel); bottomPanel.add(m_matchButton); + bottomPanel.add(m_unmatchableButton); m_destLabel = new JLabel(); bottomPanel.add(m_destLabel); @@ -161,10 +227,13 @@ public class FieldMatchingGui { m_obfDestClass = null; m_obfSourceField = null; m_obfDestField = null; - m_obfUnmatchedSourceFields = null; - m_obfUnmatchedDestFields = null; + setSourceType(SourceType.getDefault()); + updateButtons(); + } + + protected void setSourceType(SourceType val) { + m_sourceType = val; updateSourceClasses(); - updateMatchButton(); } public void setSaveListener(SaveListener val) { @@ -172,26 +241,41 @@ public class FieldMatchingGui { } private void updateSourceClasses() { - m_sourceClasses.setClasses(m_fieldMatches.getSourceClassesWithUnmatchedFields()); - m_sourceClasses.expandAll(); + + String selectedPackage = m_sourceClasses.getSelectedPackage(); + + List deobfClassEntries = Lists.newArrayList(); + for (ClassEntry entry : m_sourceType.getObfSourceClasses(m_fieldMatches)) { + deobfClassEntries.add(m_sourceDeobfuscator.deobfuscateEntry(entry)); + } + m_sourceClasses.setClasses(deobfClassEntries); + + if (selectedPackage != null) { + m_sourceClasses.expandPackage(selectedPackage); + } + + for (SourceType sourceType : SourceType.values()) { + m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", + sourceType.name(), sourceType.getObfSourceClasses(m_fieldMatches).size() + )); + } } - protected void setSourceClass(ClassEntry obfSourceClass) { + protected void setSourceClass(ClassEntry sourceClass) { - m_obfSourceClass = obfSourceClass; - m_obfDestClass = m_classMatches.getUniqueMatches().get(obfSourceClass); + m_obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass); + m_obfDestClass = m_classMatches.getUniqueMatches().get(m_obfSourceClass); if (m_obfDestClass == null) { throw new Error("No matching dest class for source class: " + m_obfSourceClass); } - updateUnmatchedFields(); - m_sourceReader.decompileClass(m_obfSourceClass, m_sourceDeobfuscator, new Runnable() { + m_sourceReader.decompileClass(m_obfSourceClass, m_sourceDeobfuscator, false, new Runnable() { @Override public void run() { updateSourceHighlights(); } }); - m_destReader.decompileClass(m_obfDestClass, m_destDeobfuscator, new Runnable() { + m_destReader.decompileClass(m_obfDestClass, m_destDeobfuscator, false, new Runnable() { @Override public void run() { updateDestHighlights(); @@ -199,71 +283,145 @@ public class FieldMatchingGui { }); } - private void updateUnmatchedFields() { - m_obfUnmatchedSourceFields = Sets.newHashSet(m_fieldMatches.getUnmatchedSourceFields(m_obfSourceClass)); - m_obfUnmatchedDestFields = Sets.newHashSet(); - for (FieldEntry destFieldEntry : m_destDeobfuscator.getJarIndex().getObfFieldEntries(m_obfDestClass)) { - if (!m_fieldMatches.isDestMatched(destFieldEntry)) { - m_obfUnmatchedDestFields.add(destFieldEntry); - } - } - } - protected void updateSourceHighlights() { - highlightFields(m_sourceReader, m_sourceDeobfuscator, m_obfUnmatchedSourceFields, m_fieldMatches.matches().keySet()); + highlightFields(m_sourceReader, m_sourceDeobfuscator, m_fieldMatches.matches().keySet(), m_fieldMatches.getUnmatchedSourceFields()); } protected void updateDestHighlights() { - highlightFields(m_destReader, m_destDeobfuscator, m_obfUnmatchedDestFields, m_fieldMatches.matches().values()); + highlightFields(m_destReader, m_destDeobfuscator, m_fieldMatches.matches().values(), m_fieldMatches.getUnmatchedDestFields()); } - private void highlightFields(CodeReader reader, Deobfuscator deobfuscator, Collection obfFieldEntries, Collection obfMatchedFieldEntries) { + private void highlightFields(CodeReader reader, Deobfuscator deobfuscator, Collection obfMatchedFields, Collection obfUnmatchedFields) { reader.clearHighlights(); SourceIndex index = reader.getSourceIndex(); - for (FieldEntry obfFieldEntry : obfFieldEntries) { + + // matched fields + for (FieldEntry obfFieldEntry : obfMatchedFields) { FieldEntry deobfFieldEntry = deobfuscator.deobfuscateEntry(obfFieldEntry); Token token = index.getDeclarationToken(deobfFieldEntry); - if (token == null) { - System.err.println("WARNING: Can't find declaration token for " + deobfFieldEntry); - } else { - reader.setHighlightedToken( - token, - obfMatchedFieldEntries.contains(obfFieldEntry) ? m_matchedHighlightPainter : m_unmatchedHighlightPainter - ); + if (token != null) { + reader.setHighlightedToken(token, m_matchedHighlightPainter); + } + } + + // unmatched fields + for (FieldEntry obfFieldEntry : obfUnmatchedFields) { + FieldEntry deobfFieldEntry = deobfuscator.deobfuscateEntry(obfFieldEntry); + Token token = index.getDeclarationToken(deobfFieldEntry); + if (token != null) { + reader.setHighlightedToken(token, m_unmatchedHighlightPainter); } } } - protected void onSelectSource(Entry entry) { - m_sourceLabel.setText(""); - m_obfSourceField = null; - if (entry != null && entry instanceof FieldEntry) { - FieldEntry fieldEntry = (FieldEntry)entry; - FieldEntry obfFieldEntry = m_sourceDeobfuscator.obfuscateEntry(fieldEntry); - if (m_obfUnmatchedSourceFields.contains(obfFieldEntry)) { - m_obfSourceField = obfFieldEntry; - m_sourceLabel.setText(fieldEntry.getName() + " " + fieldEntry.getType().toString()); + private boolean isSelectionMatched() { + return m_obfSourceField != null && m_obfDestField != null + && m_fieldMatches.isMatched(m_obfSourceField, m_obfDestField); + } + + protected void onSelectSource(Entry source) { + + // start with no selection + if (isSelectionMatched()) { + setDest(null); + } + setSource(null); + + // then look for a valid source selection + if (source != null && source instanceof FieldEntry) { + FieldEntry sourceField = (FieldEntry)source; + FieldEntry obfSourceField = m_sourceDeobfuscator.obfuscateEntry(sourceField); + if (m_fieldMatches.hasSource(obfSourceField)) { + setSource(obfSourceField); + + // look for a matched dest too + FieldEntry obfDestField = m_fieldMatches.matches().get(obfSourceField); + if (obfDestField != null) { + setDest(obfDestField); + } } } - updateMatchButton(); + + updateButtons(); } - protected void onSelectDest(Entry entry) { - m_destLabel.setText(""); - m_obfDestField = null; - if (entry != null && entry instanceof FieldEntry) { - FieldEntry fieldEntry = (FieldEntry)entry; - FieldEntry obfFieldEntry = m_destDeobfuscator.obfuscateEntry(fieldEntry); - if (m_obfUnmatchedDestFields.contains(obfFieldEntry)) { - m_obfDestField = obfFieldEntry; - m_destLabel.setText(fieldEntry.getName() + " " + fieldEntry.getType().toString()); + protected void onSelectDest(Entry dest) { + + // start with no selection + if (isSelectionMatched()) { + setSource(null); + } + setDest(null); + + // then look for a valid dest selection + if (dest != null && dest instanceof FieldEntry) { + FieldEntry destField = (FieldEntry)dest; + FieldEntry obfDestField = m_destDeobfuscator.obfuscateEntry(destField); + if (m_fieldMatches.hasDest(obfDestField)) { + setDest(obfDestField); + + // look for a matched source too + FieldEntry obfSourceField = m_fieldMatches.matches().inverse().get(obfDestField); + if (obfSourceField != null) { + setSource(obfSourceField); + } } } - updateMatchButton(); + + updateButtons(); } + + private void setSource(FieldEntry obfField) { + if (obfField == null) { + m_obfSourceField = obfField; + m_sourceLabel.setText(""); + } else { + m_obfSourceField = obfField; + FieldEntry deobfField = m_sourceDeobfuscator.deobfuscateEntry(obfField); + m_sourceLabel.setText(deobfField.getName() + " " + deobfField.getType().toString()); + } + } + + private void setDest(FieldEntry obfField) { + if (obfField == null) { + m_obfDestField = obfField; + m_destLabel.setText(""); + } else { + m_obfDestField = obfField; + FieldEntry deobfField = m_destDeobfuscator.deobfuscateEntry(obfField); + m_destLabel.setText(deobfField.getName() + " " + deobfField.getType().toString()); + } + } + + private void updateButtons() { - private void updateMatchButton() { - m_matchButton.setEnabled(m_obfSourceField != null && m_obfDestField != null); + GuiTricks.deactivateButton(m_matchButton); + GuiTricks.deactivateButton(m_unmatchableButton); + + if (m_obfSourceField != null && m_obfDestField != null) { + if (m_fieldMatches.isMatched(m_obfSourceField, m_obfDestField)) { + GuiTricks.activateButton(m_matchButton, "Unmatch", new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + unmatch(); + } + }); + } else if (!m_fieldMatches.isMatchedSourceField(m_obfSourceField) && !m_fieldMatches.isMatchedDestField(m_obfDestField)) { + GuiTricks.activateButton(m_matchButton, "Match", new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + match(); + } + }); + } + } else if (m_obfSourceField != null) { + GuiTricks.activateButton(m_unmatchableButton, "Set Unmatchable", new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + unmatchable(); + } + }); + } } protected void match() { @@ -275,7 +433,34 @@ public class FieldMatchingGui { // update the ui onSelectSource(null); onSelectDest(null); - updateUnmatchedFields(); + updateSourceHighlights(); + updateDestHighlights(); + updateSourceClasses(); + } + + protected void unmatch() { + + // update the field matches + m_fieldMatches.unmakeMatch(m_obfSourceField, m_obfDestField); + save(); + + // update the ui + onSelectSource(null); + onSelectDest(null); + updateSourceHighlights(); + updateDestHighlights(); + updateSourceClasses(); + } + + protected void unmatchable() { + + // update the field matches + m_fieldMatches.makeSourceUnmatchable(m_obfSourceField); + save(); + + // update the ui + onSelectSource(null); + onSelectDest(null); updateSourceHighlights(); updateDestHighlights(); updateSourceClasses(); diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java index df9e2215..7e539a12 100644 --- a/src/cuchaz/enigma/gui/GuiTricks.java +++ b/src/cuchaz/enigma/gui/GuiTricks.java @@ -11,8 +11,11 @@ package cuchaz.enigma.gui; import java.awt.Font; +import java.awt.event.ActionListener; import java.awt.event.MouseEvent; +import java.util.Arrays; +import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.ToolTipManager; @@ -33,4 +36,21 @@ public class GuiTricks { manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); manager.setInitialDelay(oldDelay); } + + public static void deactivateButton(JButton button) { + button.setEnabled(false); + button.setText(""); + for (ActionListener listener : Arrays.asList(button.getActionListeners())) { + button.removeActionListener(listener); + } + } + + public static void activateButton(JButton button, String text, ActionListener newListener) { + button.setText(text); + button.setEnabled(true); + for (ActionListener listener : Arrays.asList(button.getActionListeners())) { + button.removeActionListener(listener); + } + button.addActionListener(newListener); + } } -- cgit v1.2.3 From 0a7b3da29f1ff53351c940eebd1c40c1bfc91e29 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 10 Mar 2015 00:56:40 -0400 Subject: nothing of consequence --- src/cuchaz/enigma/ConvertMain.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index a45fb35c..e012f197 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -35,11 +35,11 @@ public class ConvertMain { File classMatchesFile = new File(inMappingsFile.getName() + ".class.matches"); File fieldMatchesFile = new File(inMappingsFile.getName() + ".field.matches"); - //computeClassMatches(classMatchingFile, sourceJar, destJar, mappings); - //editClasssMatches(classMatchingFile, sourceJar, destJar, mappings); - //convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchingFile); + //computeClassMatches(classMatchesFile, sourceJar, destJar, mappings); + editClasssMatches(classMatchesFile, sourceJar, destJar, mappings); + //convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile); //computeFieldMatches(fieldMatchesFile, destJar, outMappingsFile, classMatchesFile); - editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, fieldMatchesFile); + //editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, fieldMatchesFile); /* TODO // write out the converted mappings -- cgit v1.2.3 From e33b4003a5c423894e7aef575faff359dd1d33b1 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 11 Mar 2015 11:03:16 -0400 Subject: generalized field matching added method matching --- src/cuchaz/enigma/ConvertMain.java | 82 +++- src/cuchaz/enigma/convert/MappingsConverter.java | 196 ++++++--- src/cuchaz/enigma/convert/MatchesReader.java | 46 ++- src/cuchaz/enigma/convert/MatchesWriter.java | 50 ++- src/cuchaz/enigma/convert/MemberMatches.java | 145 +++++++ src/cuchaz/enigma/gui/FieldMatchingGui.java | 474 ---------------------- src/cuchaz/enigma/gui/MemberMatchingGui.java | 489 +++++++++++++++++++++++ src/cuchaz/enigma/mapping/ClassMapping.java | 4 + src/cuchaz/enigma/mapping/EntryFactory.java | 12 + src/cuchaz/enigma/mapping/FieldMapping.java | 7 +- src/cuchaz/enigma/mapping/MemberMapping.java | 6 + src/cuchaz/enigma/mapping/MethodMapping.java | 11 +- 12 files changed, 957 insertions(+), 565 deletions(-) create mode 100644 src/cuchaz/enigma/convert/MemberMatches.java delete mode 100644 src/cuchaz/enigma/gui/FieldMatchingGui.java create mode 100644 src/cuchaz/enigma/gui/MemberMatchingGui.java create mode 100644 src/cuchaz/enigma/mapping/MemberMapping.java diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index e012f197..c3a2ad5c 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -7,12 +7,14 @@ import java.io.IOException; import java.util.jar.JarFile; import cuchaz.enigma.convert.ClassMatches; -import cuchaz.enigma.convert.FieldMatches; import cuchaz.enigma.convert.MappingsConverter; import cuchaz.enigma.convert.MatchesReader; import cuchaz.enigma.convert.MatchesWriter; +import cuchaz.enigma.convert.MemberMatches; import cuchaz.enigma.gui.ClassMatchingGui; -import cuchaz.enigma.gui.FieldMatchingGui; +import cuchaz.enigma.gui.MemberMatchingGui; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsChecker; @@ -34,13 +36,21 @@ public class ConvertMain { Mappings mappings = new MappingsReader().read(new FileReader(inMappingsFile)); File classMatchesFile = new File(inMappingsFile.getName() + ".class.matches"); File fieldMatchesFile = new File(inMappingsFile.getName() + ".field.matches"); + File methodMatchesFile = new File(inMappingsFile.getName() + ".method.matches"); + // match classes //computeClassMatches(classMatchesFile, sourceJar, destJar, mappings); - editClasssMatches(classMatchesFile, sourceJar, destJar, mappings); + //editClasssMatches(classMatchesFile, sourceJar, destJar, mappings); //convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile); + + // match fields //computeFieldMatches(fieldMatchesFile, destJar, outMappingsFile, classMatchesFile); //editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, fieldMatchesFile); + // match methods/constructors + //computeMethodMatches(methodMatchesFile, destJar, outMappingsFile, classMatchesFile); + editMethodMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, methodMatchesFile); + /* TODO // write out the converted mappings FileWriter writer = new FileWriter(outMappingsFile); @@ -91,7 +101,7 @@ public class ConvertMain { System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath()); } - private static void computeFieldMatches(File fieldMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile) + private static void computeFieldMatches(File memberMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile) throws IOException, MappingParseException { System.out.println("Reading class matches..."); @@ -101,13 +111,13 @@ public class ConvertMain { System.out.println("Indexing dest jar..."); Deobfuscator destDeobfuscator = new Deobfuscator(destJar); - System.out.println("Writing field matches..."); + System.out.println("Writing matches..."); - // get the matched and unmatched field mappings - FieldMatches fieldMatches = MappingsConverter.computeFieldMatches(destDeobfuscator, destMappings, classMatches); + // get the matched and unmatched mappings + MemberMatches fieldMatches = MappingsConverter.computeFieldMatches(destDeobfuscator, destMappings, classMatches); - MatchesWriter.writeFields(fieldMatches, fieldMatchesFile); - System.out.println("Wrote:\n\t" + fieldMatchesFile.getAbsolutePath()); + MatchesWriter.writeMembers(fieldMatches, memberMatchesFile); + System.out.println("Wrote:\n\t" + memberMatchesFile.getAbsolutePath()); } private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File fieldMatchesFile) @@ -115,7 +125,7 @@ public class ConvertMain { System.out.println("Reading matches..."); ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); - FieldMatches fieldMatches = MatchesReader.readFields(fieldMatchesFile); + MemberMatches fieldMatches = MatchesReader.readMembers(fieldMatchesFile); // prep deobfuscators Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); @@ -125,18 +135,64 @@ public class ConvertMain { checker.dropBrokenMappings(destMappings); deobfuscators.dest.setMappings(destMappings); - new FieldMatchingGui(classMatches, fieldMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(new FieldMatchingGui.SaveListener() { + new MemberMatchingGui(classMatches, fieldMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(new MemberMatchingGui.SaveListener() { @Override - public void save(FieldMatches matches) { + public void save(MemberMatches matches) { try { - MatchesWriter.writeFields(matches, fieldMatchesFile); + MatchesWriter.writeMembers(matches, fieldMatchesFile); } catch (IOException ex) { throw new Error(ex); } } }); } + + private static void computeMethodMatches(File methodMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile) + throws IOException, MappingParseException { + + System.out.println("Reading class matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); + System.out.println("Reading mappings..."); + Mappings destMappings = new MappingsReader().read(new FileReader(destMappingsFile)); + System.out.println("Indexing dest jar..."); + Deobfuscator destDeobfuscator = new Deobfuscator(destJar); + + System.out.println("Writing method matches..."); + + // get the matched and unmatched mappings + MemberMatches methodMatches = MappingsConverter.computeBehaviorMatches(destDeobfuscator, destMappings, classMatches); + + MatchesWriter.writeMembers(methodMatches, methodMatchesFile); + System.out.println("Wrote:\n\t" + methodMatchesFile.getAbsolutePath()); + } + private static void editMethodMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File methodMatchesFile) + throws IOException, MappingParseException { + + System.out.println("Reading matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); + MemberMatches methodMatches = MatchesReader.readMembers(methodMatchesFile); + + // prep deobfuscators + Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); + deobfuscators.source.setMappings(sourceMappings); + Mappings destMappings = new MappingsReader().read(new FileReader(destMappingsFile)); + MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); + checker.dropBrokenMappings(destMappings); + deobfuscators.dest.setMappings(destMappings); + + new MemberMatchingGui(classMatches, methodMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(new MemberMatchingGui.SaveListener() { + @Override + public void save(MemberMatches matches) { + try { + MatchesWriter.writeMembers(matches, methodMatchesFile); + } catch (IOException ex) { + throw new Error(ex); + } + } + }); + } + private static class Deobfuscators { public Deobfuscator source; diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index 9ab1baa3..2987ea08 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -11,12 +11,12 @@ package cuchaz.enigma.convert; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.jar.JarFile; @@ -30,15 +30,20 @@ import com.google.common.collect.Sets; import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; +import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; import cuchaz.enigma.mapping.ClassNameReplacer; -import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.FieldMapping; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsChecker; +import cuchaz.enigma.mapping.MemberMapping; +import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.MethodMapping; +import cuchaz.enigma.mapping.Signature; import cuchaz.enigma.mapping.Type; public class MappingsConverter { @@ -124,8 +129,8 @@ public class MappingsConverter { public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { // sort the unique matches by size of inner class chain - Multimap> matchesByDestChainSize = HashMultimap.create(); - for (Entry match : matches.getUniqueMatches().entrySet()) { + Multimap> matchesByDestChainSize = HashMultimap.create(); + for (java.util.Map.Entry match : matches.getUniqueMatches().entrySet()) { int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size(); matchesByDestChainSize.put(chainSize, match); } @@ -135,7 +140,7 @@ public class MappingsConverter { List chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet()); Collections.sort(chainSizes); for (int chainSize : chainSizes) { - for (Entry match : matchesByDestChainSize.get(chainSize)) { + for (java.util.Map.Entry match : matchesByDestChainSize.get(chainSize)) { // get class info ClassEntry obfSourceClassEntry = match.getKey(); @@ -251,89 +256,172 @@ public class MappingsConverter { mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); } } + + public static interface Doer { + Collection getDroppedEntries(MappingsChecker checker); + Collection getObfEntries(JarIndex jarIndex); + Collection> getMappings(ClassMapping destClassMapping); + Set filterEntries(Collection obfEntries, T obfSourceEntry, ClassMatches classMatches); + } + + public static MemberMatches computeFieldMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches) { + return computeMemberMatches(destDeobfuscator, destMappings, classMatches, new Doer() { + + @Override + public Collection getDroppedEntries(MappingsChecker checker) { + return checker.getDroppedFieldMappings().keySet(); + } + + @Override + public Collection getObfEntries(JarIndex jarIndex) { + return jarIndex.getObfFieldEntries(); + } + + @Override + public Collection> getMappings(ClassMapping destClassMapping) { + return (Collection>)destClassMapping.fields(); + } + + @Override + public Set filterEntries(Collection obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) { + Set out = Sets.newHashSet(); + for (FieldEntry obfDestField : obfDestFields) { + Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse()); + if (translatedDestType.equals(obfSourceField.getType())) { + out.add(obfDestField); + } + } + return out; + } + }); + } + + public static MemberMatches computeBehaviorMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches) { + return computeMemberMatches(destDeobfuscator, destMappings, classMatches, new Doer() { + + @Override + public Collection getDroppedEntries(MappingsChecker checker) { + return checker.getDroppedMethodMappings().keySet(); + } + + @Override + public Collection getObfEntries(JarIndex jarIndex) { + return jarIndex.getObfBehaviorEntries(); + } - public static FieldMatches computeFieldMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches) { + @Override + public Collection> getMappings(ClassMapping destClassMapping) { + return (Collection>)destClassMapping.methods(); + } + + @Override + public Set filterEntries(Collection obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) { + Set out = Sets.newHashSet(); + for (BehaviorEntry obfDestField : obfDestFields) { + Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse()); + if (translatedDestSignature == null && obfSourceField.getSignature() == null) { + out.add(obfDestField); + } else if (translatedDestSignature == null || obfSourceField.getSignature() == null) { + // skip it + } else if (translatedDestSignature.equals(obfSourceField.getSignature())) { + out.add(obfDestField); + } + } + return out; + } + }); + } + + public static MemberMatches computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer doer) { - FieldMatches fieldMatches = new FieldMatches(); + MemberMatches memberMatches = new MemberMatches(); // unmatched source fields are easy MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); checker.dropBrokenMappings(destMappings); - for (FieldEntry destObfField : checker.getDroppedFieldMappings().keySet()) { - FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); - fieldMatches.addUnmatchedSourceField(srcObfField); + for (T destObfEntry : doer.getDroppedEntries(checker)) { + T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); + memberMatches.addUnmatchedSourceEntry(srcObfEntry); } // get matched fields (anything that's left after the checks/drops is matched( for (ClassMapping classMapping : destMappings.classes()) { - collectMatchedFields(fieldMatches, classMapping, classMatches); + collectMatchedFields(memberMatches, classMapping, classMatches, doer); } // get unmatched dest fields - for (FieldEntry destFieldEntry : destDeobfuscator.getJarIndex().getObfFieldEntries()) { - if (!fieldMatches.isMatchedDestField(destFieldEntry)) { - fieldMatches.addUnmatchedDestField(destFieldEntry); + for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) { + if (!memberMatches.isMatchedDestEntry(destEntry)) { + memberMatches.addUnmatchedDestEntry(destEntry); } } - System.out.println("Automatching " + fieldMatches.getUnmatchedSourceFields().size() + " unmatched source fields..."); + System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); // go through the unmatched source fields and try to pick out the easy matches - for (ClassEntry obfSourceClass : Lists.newArrayList(fieldMatches.getSourceClassesWithUnmatchedFields())) { - for (FieldEntry obfSourceField : Lists.newArrayList(fieldMatches.getUnmatchedSourceFields(obfSourceClass))) { + for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { + for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { // get the possible dest matches ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); - // filter by type - Set obfDestFields = Sets.newHashSet(); - for (FieldEntry obfDestField : fieldMatches.getUnmatchedDestFields(obfDestClass)) { - Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse()); - if (translatedDestType.equals(obfSourceField.getType())) { - obfDestFields.add(obfDestField); - } - } + // filter by type/signature + Set obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); - if (obfDestFields.size() == 1) { + if (obfDestEntries.size() == 1) { // make the easy match - FieldEntry obfDestField = obfDestFields.iterator().next(); - fieldMatches.makeMatch(obfSourceField, obfDestField); - } else if (obfDestFields.isEmpty()) { + memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); + } else if (obfDestEntries.isEmpty()) { // no match is possible =( - fieldMatches.makeSourceUnmatchable(obfSourceField); + memberMatches.makeSourceUnmatchable(obfSourceEntry); } } } - System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source fields", - fieldMatches.getUnmatchedSourceFields().size(), - fieldMatches.getUnmatchableSourceFields().size() + System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", + memberMatches.getUnmatchedSourceEntries().size(), + memberMatches.getUnmatchableSourceEntries().size() )); - return fieldMatches; + return memberMatches; } - private static void collectMatchedFields(FieldMatches fieldMatches, ClassMapping destClassMapping, ClassMatches classMatches) { + private static void collectMatchedFields(MemberMatches memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer doer) { // get the fields for this class - for (FieldMapping destFieldMapping : destClassMapping.fields()) { - FieldEntry destObfField = EntryFactory.getObfFieldEntry(destClassMapping, destFieldMapping); - FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); - fieldMatches.addMatch(srcObfField, destObfField); + for (MemberMapping destEntryMapping : doer.getMappings(destClassMapping)) { + T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry()); + T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); + memberMatches.addMatch(srcObfField, destObfField); } // recurse for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { - collectMatchedFields(fieldMatches, destInnerClassMapping, classMatches); + collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer); } } - private static FieldEntry translate(FieldEntry in, BiMap map) { - return new FieldEntry( - map.get(in.getClassEntry()), - in.getName(), - translate(in.getType(), map) - ); + @SuppressWarnings("unchecked") + private static T translate(T in, BiMap map) { + if (in instanceof FieldEntry) { + return (T)new FieldEntry( + map.get(in.getClassEntry()), + in.getName(), + translate(((FieldEntry)in).getType(), map) + ); + } else if (in instanceof MethodEntry) { + return (T)new MethodEntry( + map.get(in.getClassEntry()), + in.getName(), + translate(((MethodEntry)in).getSignature(), map) + ); + } else if (in instanceof ConstructorEntry) { + return (T)new ConstructorEntry( + map.get(in.getClassEntry()), + translate(((ConstructorEntry)in).getSignature(), map) + ); + } + throw new Error("Unhandled entry type: " + in.getClass()); } private static Type translate(Type type, final BiMap map) { @@ -348,4 +436,20 @@ public class MappingsConverter { } }); } + + private static Signature translate(Signature signature, final BiMap map) { + if (signature == null) { + return null; + } + return new Signature(signature, new ClassNameReplacer() { + @Override + public String replace(String inClassName) { + ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); + if (outClassEntry == null) { + return null; + } + return outClassEntry.getName(); + } + }); + } } diff --git a/src/cuchaz/enigma/convert/MatchesReader.java b/src/cuchaz/enigma/convert/MatchesReader.java index 921ab1d0..dac2f057 100644 --- a/src/cuchaz/enigma/convert/MatchesReader.java +++ b/src/cuchaz/enigma/convert/MatchesReader.java @@ -10,6 +10,8 @@ import java.util.List; import com.beust.jcommander.internal.Lists; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Type; @@ -45,45 +47,57 @@ public class MatchesReader { return entries; } - public static FieldMatches readFields(File file) + public static MemberMatches readMembers(File file) throws IOException { try (BufferedReader in = new BufferedReader(new FileReader(file))) { - FieldMatches matches = new FieldMatches(); + MemberMatches matches = new MemberMatches(); String line = null; while ((line = in.readLine()) != null) { - readFieldMatch(matches, line); + readMemberMatch(matches, line); } return matches; } } - private static void readFieldMatch(FieldMatches matches, String line) { + private static void readMemberMatch(MemberMatches matches, String line) { if (line.startsWith("!")) { - matches.addUnmatchableSourceField(readField(line.substring(1))); + T source = readEntry(line.substring(1)); + matches.addUnmatchableSourceEntry(source); } else { String[] parts = line.split(":", 2); - FieldEntry source = readField(parts[0]); - FieldEntry dest = readField(parts[1]); + T source = readEntry(parts[0]); + T dest = readEntry(parts[1]); if (source != null && dest != null) { matches.addMatch(source, dest); } else if (source != null) { - matches.addUnmatchedSourceField(source); + matches.addUnmatchedSourceEntry(source); } else if (dest != null) { - matches.addUnmatchedDestField(dest); + matches.addUnmatchedDestEntry(dest); } } } - private static FieldEntry readField(String in) { + @SuppressWarnings("unchecked") + private static T readEntry(String in) { if (in.length() <= 0) { return null; } String[] parts = in.split(" "); - assert(parts.length == 3); - return new FieldEntry( - new ClassEntry(parts[0]), - parts[1], - new Type(parts[2]) - ); + if (parts.length == 3 && parts[2].indexOf('(') < 0) { + return (T)new FieldEntry( + new ClassEntry(parts[0]), + parts[1], + new Type(parts[2]) + ); + } else { + assert(parts.length == 2 || parts.length == 3); + if (parts.length == 2) { + return (T)EntryFactory.getBehaviorEntry(parts[0], parts[1]); + } else if (parts.length == 3) { + return (T)EntryFactory.getBehaviorEntry(parts[0], parts[1], parts[2]); + } else { + throw new Error("Malformed behavior entry: " + in); + } + } } } diff --git a/src/cuchaz/enigma/convert/MatchesWriter.java b/src/cuchaz/enigma/convert/MatchesWriter.java index 2118dd08..9e9ead02 100644 --- a/src/cuchaz/enigma/convert/MatchesWriter.java +++ b/src/cuchaz/enigma/convert/MatchesWriter.java @@ -5,7 +5,9 @@ import java.io.FileWriter; import java.io.IOException; import java.util.Map; +import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; @@ -41,43 +43,52 @@ public class MatchesWriter { } } - public static void writeFields(FieldMatches fieldMatches, File file) + public static void writeMembers(MemberMatches matches, File file) throws IOException { try (FileWriter out = new FileWriter(file)) { - for (Map.Entry match : fieldMatches.matches().entrySet()) { - writeFieldMatch(out, match.getKey(), match.getValue()); + for (Map.Entry match : matches.matches().entrySet()) { + writeMemberMatch(out, match.getKey(), match.getValue()); } - for (FieldEntry fieldEntry : fieldMatches.getUnmatchedSourceFields()) { - writeFieldMatch(out, fieldEntry, null); + for (T entry : matches.getUnmatchedSourceEntries()) { + writeMemberMatch(out, entry, null); } - for (FieldEntry fieldEntry : fieldMatches.getUnmatchedDestFields()) { - writeFieldMatch(out, null, fieldEntry); + for (T entry : matches.getUnmatchedDestEntries()) { + writeMemberMatch(out, null, entry); } - for (FieldEntry fieldEntry : fieldMatches.getUnmatchableSourceFields()) { - writeUnmatchableField(out, fieldEntry); + for (T entry : matches.getUnmatchableSourceEntries()) { + writeUnmatchableEntry(out, entry); } } } - private static void writeFieldMatch(FileWriter out, FieldEntry source, FieldEntry dest) + private static void writeMemberMatch(FileWriter out, T source, T dest) throws IOException { if (source != null) { - writeField(out, source); + writeEntry(out, source); } out.write(":"); if (dest != null) { - writeField(out, dest); + writeEntry(out, dest); } out.write("\n"); } - private static void writeUnmatchableField(FileWriter out, FieldEntry fieldEntry) + private static void writeUnmatchableEntry(FileWriter out, T entry) throws IOException { out.write("!"); - writeField(out, fieldEntry); + writeEntry(out, entry); out.write("\n"); } + private static void writeEntry(FileWriter out, T entry) + throws IOException { + if (entry instanceof FieldEntry) { + writeField(out, (FieldEntry)entry); + } else if (entry instanceof BehaviorEntry) { + writeBehavior(out, (BehaviorEntry)entry); + } + } + private static void writeField(FileWriter out, FieldEntry fieldEntry) throws IOException { out.write(fieldEntry.getClassName()); @@ -86,4 +97,15 @@ public class MatchesWriter { out.write(" "); out.write(fieldEntry.getType().toString()); } + + private static void writeBehavior(FileWriter out, BehaviorEntry behaviorEntry) + throws IOException { + out.write(behaviorEntry.getClassName()); + out.write(" "); + out.write(behaviorEntry.getName()); + out.write(" "); + if (behaviorEntry.getSignature() != null) { + out.write(behaviorEntry.getSignature().toString()); + } + } } diff --git a/src/cuchaz/enigma/convert/MemberMatches.java b/src/cuchaz/enigma/convert/MemberMatches.java new file mode 100644 index 00000000..1078ab75 --- /dev/null +++ b/src/cuchaz/enigma/convert/MemberMatches.java @@ -0,0 +1,145 @@ +package cuchaz.enigma.convert; + +import java.util.Collection; +import java.util.Set; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; + + +public class MemberMatches { + + private BiMap m_matches; + private Multimap m_matchedSourceEntries; + private Multimap m_unmatchedSourceEntries; + private Multimap m_unmatchedDestEntries; + private Multimap m_unmatchableSourceEntries; + + public MemberMatches() { + m_matches = HashBiMap.create(); + m_matchedSourceEntries = HashMultimap.create(); + m_unmatchedSourceEntries = HashMultimap.create(); + m_unmatchedDestEntries = HashMultimap.create(); + m_unmatchableSourceEntries = HashMultimap.create(); + } + + public void addMatch(T srcEntry, T destEntry) { + boolean wasAdded = m_matches.put(srcEntry, destEntry) == null; + assert (wasAdded); + wasAdded = m_matchedSourceEntries.put(srcEntry.getClassEntry(), srcEntry); + assert (wasAdded); + } + + public void addUnmatchedSourceEntry(T sourceEntry) { + boolean wasAdded = m_unmatchedSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); + assert (wasAdded); + } + + public void addUnmatchedSourceEntries(Iterable sourceEntries) { + for (T sourceEntry : sourceEntries) { + addUnmatchedSourceEntry(sourceEntry); + } + } + + public void addUnmatchedDestEntry(T destEntry) { + boolean wasAdded = m_unmatchedDestEntries.put(destEntry.getClassEntry(), destEntry); + assert (wasAdded); + } + + public void addUnmatchedDestEntries(Iterable destEntriesntries) { + for (T entry : destEntriesntries) { + addUnmatchedDestEntry(entry); + } + } + + public void addUnmatchableSourceEntry(T sourceEntry) { + boolean wasAdded = m_unmatchableSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); + assert (wasAdded); + } + + public Set getSourceClassesWithUnmatchedEntries() { + return m_unmatchedSourceEntries.keySet(); + } + + public Collection getSourceClassesWithoutUnmatchedEntries() { + Set out = Sets.newHashSet(); + out.addAll(m_matchedSourceEntries.keySet()); + out.removeAll(m_unmatchedSourceEntries.keySet()); + return out; + } + + public Collection getUnmatchedSourceEntries() { + return m_unmatchedSourceEntries.values(); + } + + public Collection getUnmatchedSourceEntries(ClassEntry sourceClass) { + return m_unmatchedSourceEntries.get(sourceClass); + } + + public Collection getUnmatchedDestEntries() { + return m_unmatchedDestEntries.values(); + } + + public Collection getUnmatchedDestEntries(ClassEntry destClass) { + return m_unmatchedDestEntries.get(destClass); + } + + public Collection getUnmatchableSourceEntries() { + return m_unmatchableSourceEntries.values(); + } + + public boolean hasSource(T sourceEntry) { + return m_matches.containsKey(sourceEntry) || m_unmatchedSourceEntries.containsValue(sourceEntry); + } + + public boolean hasDest(T destEntry) { + return m_matches.containsValue(destEntry) || m_unmatchedDestEntries.containsValue(destEntry); + } + + public BiMap matches() { + return m_matches; + } + + public boolean isMatchedSourceEntry(T sourceEntry) { + return m_matches.containsKey(sourceEntry); + } + + public boolean isMatchedDestEntry(T destEntry) { + return m_matches.containsValue(destEntry); + } + + public void makeMatch(T sourceEntry, T destEntry) { + boolean wasRemoved = m_unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); + assert (wasRemoved); + wasRemoved = m_unmatchedDestEntries.remove(destEntry.getClassEntry(), destEntry); + assert (wasRemoved); + addMatch(sourceEntry, destEntry); + } + + public boolean isMatched(T sourceEntry, T destEntry) { + T match = m_matches.get(sourceEntry); + return match != null && match.equals(destEntry); + } + + public void unmakeMatch(T sourceEntry, T destEntry) { + boolean wasRemoved = m_matches.remove(sourceEntry) != null; + assert (wasRemoved); + wasRemoved = m_matchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); + assert (wasRemoved); + addUnmatchedSourceEntry(sourceEntry); + addUnmatchedDestEntry(destEntry); + } + + public void makeSourceUnmatchable(T sourceEntry) { + assert(!isMatchedSourceEntry(sourceEntry)); + boolean wasRemoved = m_unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); + assert (wasRemoved); + addUnmatchableSourceEntry(sourceEntry); + } +} diff --git a/src/cuchaz/enigma/gui/FieldMatchingGui.java b/src/cuchaz/enigma/gui/FieldMatchingGui.java deleted file mode 100644 index 3f4a378c..00000000 --- a/src/cuchaz/enigma/gui/FieldMatchingGui.java +++ /dev/null @@ -1,474 +0,0 @@ -package cuchaz.enigma.gui; - -import java.awt.BorderLayout; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import javax.swing.BoxLayout; -import javax.swing.ButtonGroup; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JRadioButton; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.WindowConstants; -import javax.swing.text.Highlighter.HighlightPainter; - -import com.beust.jcommander.internal.Lists; -import com.beust.jcommander.internal.Maps; - -import cuchaz.enigma.Constants; -import cuchaz.enigma.Deobfuscator; -import cuchaz.enigma.analysis.EntryReference; -import cuchaz.enigma.analysis.SourceIndex; -import cuchaz.enigma.analysis.Token; -import cuchaz.enigma.convert.ClassMatches; -import cuchaz.enigma.convert.FieldMatches; -import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.Entry; -import cuchaz.enigma.mapping.FieldEntry; -import de.sciss.syntaxpane.DefaultSyntaxKit; - - -public class FieldMatchingGui { - - private static enum SourceType { - Matched { - - @Override - public Collection getObfSourceClasses(FieldMatches matches) { - return matches.getSourceClassesWithoutUnmatchedFields(); - } - }, - Unmatched { - - @Override - public Collection getObfSourceClasses(FieldMatches matches) { - return matches.getSourceClassesWithUnmatchedFields(); - } - }; - - public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { - JRadioButton button = new JRadioButton(name(), this == getDefault()); - button.setActionCommand(name()); - button.addActionListener(listener); - group.add(button); - return button; - } - - public abstract Collection getObfSourceClasses(FieldMatches matches); - - public static SourceType getDefault() { - return values()[0]; - } - } - - public static interface SaveListener { - public void save(FieldMatches matches); - } - - // controls - private JFrame m_frame; - private Map m_sourceTypeButtons; - private ClassSelector m_sourceClasses; - private CodeReader m_sourceReader; - private CodeReader m_destReader; - private JButton m_matchButton; - private JButton m_unmatchableButton; - private JLabel m_sourceLabel; - private JLabel m_destLabel; - private HighlightPainter m_unmatchedHighlightPainter; - private HighlightPainter m_matchedHighlightPainter; - - private ClassMatches m_classMatches; - private FieldMatches m_fieldMatches; - private Deobfuscator m_sourceDeobfuscator; - private Deobfuscator m_destDeobfuscator; - private SaveListener m_saveListener; - private SourceType m_sourceType; - private ClassEntry m_obfSourceClass; - private ClassEntry m_obfDestClass; - private FieldEntry m_obfSourceField; - private FieldEntry m_obfDestField; - - public FieldMatchingGui(ClassMatches classMatches, FieldMatches fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { - - m_classMatches = classMatches; - m_fieldMatches = fieldMatches; - m_sourceDeobfuscator = sourceDeobfuscator; - m_destDeobfuscator = destDeobfuscator; - - // init frame - m_frame = new JFrame(Constants.Name + " - Field Matcher"); - final Container pane = m_frame.getContentPane(); - pane.setLayout(new BorderLayout()); - - // init classes side - JPanel classesPanel = new JPanel(); - classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS)); - classesPanel.setPreferredSize(new Dimension(200, 0)); - pane.add(classesPanel, BorderLayout.WEST); - classesPanel.add(new JLabel("Classes")); - - // init source type radios - JPanel sourceTypePanel = new JPanel(); - classesPanel.add(sourceTypePanel); - sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); - ActionListener sourceTypeListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - setSourceType(SourceType.valueOf(event.getActionCommand())); - } - }; - ButtonGroup sourceTypeButtons = new ButtonGroup(); - m_sourceTypeButtons = Maps.newHashMap(); - for (SourceType sourceType : SourceType.values()) { - JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); - m_sourceTypeButtons.put(sourceType, button); - sourceTypePanel.add(button); - } - - m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); - m_sourceClasses.setListener(new ClassSelectionListener() { - @Override - public void onSelectClass(ClassEntry classEntry) { - setSourceClass(classEntry); - } - }); - JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); - classesPanel.add(sourceScroller); - - // init readers - DefaultSyntaxKit.initKit(); - m_sourceReader = new CodeReader(); - m_sourceReader.setSelectionListener(new CodeReader.SelectionListener() { - @Override - public void onSelect(EntryReference reference) { - if (reference != null) { - onSelectSource(reference.entry); - } else { - onSelectSource(null); - } - } - }); - m_destReader = new CodeReader(); - m_destReader.setSelectionListener(new CodeReader.SelectionListener() { - @Override - public void onSelect(EntryReference reference) { - if (reference != null) { - onSelectDest(reference.entry); - } else { - onSelectDest(null); - } - } - }); - - // add key bindings - KeyAdapter keyListener = new KeyAdapter() { - @Override - public void keyPressed(KeyEvent event) { - switch (event.getKeyCode()) { - case KeyEvent.VK_M: - m_matchButton.doClick(); - break; - } - } - }; - m_sourceReader.addKeyListener(keyListener); - m_destReader.addKeyListener(keyListener); - - // init all the splits - JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_sourceReader), new JScrollPane(m_destReader)); - splitRight.setResizeWeight(0.5); // resize 50:50 - JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight); - splitLeft.setResizeWeight(0); // let the right side take all the slack - pane.add(splitLeft, BorderLayout.CENTER); - splitLeft.resetToPreferredSizes(); - - // init bottom panel - JPanel bottomPanel = new JPanel(); - bottomPanel.setLayout(new FlowLayout()); - pane.add(bottomPanel, BorderLayout.SOUTH); - - m_matchButton = new JButton(); - m_unmatchableButton = new JButton(); - - m_sourceLabel = new JLabel(); - bottomPanel.add(m_sourceLabel); - bottomPanel.add(m_matchButton); - bottomPanel.add(m_unmatchableButton); - m_destLabel = new JLabel(); - bottomPanel.add(m_destLabel); - - // show the frame - pane.doLayout(); - m_frame.setSize(1024, 576); - m_frame.setMinimumSize(new Dimension(640, 480)); - m_frame.setVisible(true); - m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - - m_unmatchedHighlightPainter = new ObfuscatedHighlightPainter(); - m_matchedHighlightPainter = new DeobfuscatedHighlightPainter(); - - // init state - m_saveListener = null; - m_obfSourceClass = null; - m_obfDestClass = null; - m_obfSourceField = null; - m_obfDestField = null; - setSourceType(SourceType.getDefault()); - updateButtons(); - } - - protected void setSourceType(SourceType val) { - m_sourceType = val; - updateSourceClasses(); - } - - public void setSaveListener(SaveListener val) { - m_saveListener = val; - } - - private void updateSourceClasses() { - - String selectedPackage = m_sourceClasses.getSelectedPackage(); - - List deobfClassEntries = Lists.newArrayList(); - for (ClassEntry entry : m_sourceType.getObfSourceClasses(m_fieldMatches)) { - deobfClassEntries.add(m_sourceDeobfuscator.deobfuscateEntry(entry)); - } - m_sourceClasses.setClasses(deobfClassEntries); - - if (selectedPackage != null) { - m_sourceClasses.expandPackage(selectedPackage); - } - - for (SourceType sourceType : SourceType.values()) { - m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", - sourceType.name(), sourceType.getObfSourceClasses(m_fieldMatches).size() - )); - } - } - - protected void setSourceClass(ClassEntry sourceClass) { - - m_obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass); - m_obfDestClass = m_classMatches.getUniqueMatches().get(m_obfSourceClass); - if (m_obfDestClass == null) { - throw new Error("No matching dest class for source class: " + m_obfSourceClass); - } - - m_sourceReader.decompileClass(m_obfSourceClass, m_sourceDeobfuscator, false, new Runnable() { - @Override - public void run() { - updateSourceHighlights(); - } - }); - m_destReader.decompileClass(m_obfDestClass, m_destDeobfuscator, false, new Runnable() { - @Override - public void run() { - updateDestHighlights(); - } - }); - } - - protected void updateSourceHighlights() { - highlightFields(m_sourceReader, m_sourceDeobfuscator, m_fieldMatches.matches().keySet(), m_fieldMatches.getUnmatchedSourceFields()); - } - - protected void updateDestHighlights() { - highlightFields(m_destReader, m_destDeobfuscator, m_fieldMatches.matches().values(), m_fieldMatches.getUnmatchedDestFields()); - } - - private void highlightFields(CodeReader reader, Deobfuscator deobfuscator, Collection obfMatchedFields, Collection obfUnmatchedFields) { - reader.clearHighlights(); - SourceIndex index = reader.getSourceIndex(); - - // matched fields - for (FieldEntry obfFieldEntry : obfMatchedFields) { - FieldEntry deobfFieldEntry = deobfuscator.deobfuscateEntry(obfFieldEntry); - Token token = index.getDeclarationToken(deobfFieldEntry); - if (token != null) { - reader.setHighlightedToken(token, m_matchedHighlightPainter); - } - } - - // unmatched fields - for (FieldEntry obfFieldEntry : obfUnmatchedFields) { - FieldEntry deobfFieldEntry = deobfuscator.deobfuscateEntry(obfFieldEntry); - Token token = index.getDeclarationToken(deobfFieldEntry); - if (token != null) { - reader.setHighlightedToken(token, m_unmatchedHighlightPainter); - } - } - } - - private boolean isSelectionMatched() { - return m_obfSourceField != null && m_obfDestField != null - && m_fieldMatches.isMatched(m_obfSourceField, m_obfDestField); - } - - protected void onSelectSource(Entry source) { - - // start with no selection - if (isSelectionMatched()) { - setDest(null); - } - setSource(null); - - // then look for a valid source selection - if (source != null && source instanceof FieldEntry) { - FieldEntry sourceField = (FieldEntry)source; - FieldEntry obfSourceField = m_sourceDeobfuscator.obfuscateEntry(sourceField); - if (m_fieldMatches.hasSource(obfSourceField)) { - setSource(obfSourceField); - - // look for a matched dest too - FieldEntry obfDestField = m_fieldMatches.matches().get(obfSourceField); - if (obfDestField != null) { - setDest(obfDestField); - } - } - } - - updateButtons(); - } - - protected void onSelectDest(Entry dest) { - - // start with no selection - if (isSelectionMatched()) { - setSource(null); - } - setDest(null); - - // then look for a valid dest selection - if (dest != null && dest instanceof FieldEntry) { - FieldEntry destField = (FieldEntry)dest; - FieldEntry obfDestField = m_destDeobfuscator.obfuscateEntry(destField); - if (m_fieldMatches.hasDest(obfDestField)) { - setDest(obfDestField); - - // look for a matched source too - FieldEntry obfSourceField = m_fieldMatches.matches().inverse().get(obfDestField); - if (obfSourceField != null) { - setSource(obfSourceField); - } - } - } - - updateButtons(); - } - - private void setSource(FieldEntry obfField) { - if (obfField == null) { - m_obfSourceField = obfField; - m_sourceLabel.setText(""); - } else { - m_obfSourceField = obfField; - FieldEntry deobfField = m_sourceDeobfuscator.deobfuscateEntry(obfField); - m_sourceLabel.setText(deobfField.getName() + " " + deobfField.getType().toString()); - } - } - - private void setDest(FieldEntry obfField) { - if (obfField == null) { - m_obfDestField = obfField; - m_destLabel.setText(""); - } else { - m_obfDestField = obfField; - FieldEntry deobfField = m_destDeobfuscator.deobfuscateEntry(obfField); - m_destLabel.setText(deobfField.getName() + " " + deobfField.getType().toString()); - } - } - - private void updateButtons() { - - GuiTricks.deactivateButton(m_matchButton); - GuiTricks.deactivateButton(m_unmatchableButton); - - if (m_obfSourceField != null && m_obfDestField != null) { - if (m_fieldMatches.isMatched(m_obfSourceField, m_obfDestField)) { - GuiTricks.activateButton(m_matchButton, "Unmatch", new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - unmatch(); - } - }); - } else if (!m_fieldMatches.isMatchedSourceField(m_obfSourceField) && !m_fieldMatches.isMatchedDestField(m_obfDestField)) { - GuiTricks.activateButton(m_matchButton, "Match", new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - match(); - } - }); - } - } else if (m_obfSourceField != null) { - GuiTricks.activateButton(m_unmatchableButton, "Set Unmatchable", new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - unmatchable(); - } - }); - } - } - - protected void match() { - - // update the field matches - m_fieldMatches.makeMatch(m_obfSourceField, m_obfDestField); - save(); - - // update the ui - onSelectSource(null); - onSelectDest(null); - updateSourceHighlights(); - updateDestHighlights(); - updateSourceClasses(); - } - - protected void unmatch() { - - // update the field matches - m_fieldMatches.unmakeMatch(m_obfSourceField, m_obfDestField); - save(); - - // update the ui - onSelectSource(null); - onSelectDest(null); - updateSourceHighlights(); - updateDestHighlights(); - updateSourceClasses(); - } - - protected void unmatchable() { - - // update the field matches - m_fieldMatches.makeSourceUnmatchable(m_obfSourceField); - save(); - - // update the ui - onSelectSource(null); - onSelectDest(null); - updateSourceHighlights(); - updateDestHighlights(); - updateSourceClasses(); - } - - private void save() { - if (m_saveListener != null) { - m_saveListener.save(m_fieldMatches); - } - } -} diff --git a/src/cuchaz/enigma/gui/MemberMatchingGui.java b/src/cuchaz/enigma/gui/MemberMatchingGui.java new file mode 100644 index 00000000..52545b33 --- /dev/null +++ b/src/cuchaz/enigma/gui/MemberMatchingGui.java @@ -0,0 +1,489 @@ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.WindowConstants; +import javax.swing.text.Highlighter.HighlightPainter; + +import com.beust.jcommander.internal.Lists; +import com.beust.jcommander.internal.Maps; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.convert.ClassMatches; +import cuchaz.enigma.convert.MemberMatches; +import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import de.sciss.syntaxpane.DefaultSyntaxKit; + + +public class MemberMatchingGui { + + private static enum SourceType { + Matched { + + @Override + public Collection getObfSourceClasses(MemberMatches matches) { + return matches.getSourceClassesWithoutUnmatchedEntries(); + } + }, + Unmatched { + + @Override + public Collection getObfSourceClasses(MemberMatches matches) { + return matches.getSourceClassesWithUnmatchedEntries(); + } + }; + + public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { + JRadioButton button = new JRadioButton(name(), this == getDefault()); + button.setActionCommand(name()); + button.addActionListener(listener); + group.add(button); + return button; + } + + public abstract Collection getObfSourceClasses(MemberMatches matches); + + public static SourceType getDefault() { + return values()[0]; + } + } + + public static interface SaveListener { + public void save(MemberMatches matches); + } + + // controls + private JFrame m_frame; + private Map m_sourceTypeButtons; + private ClassSelector m_sourceClasses; + private CodeReader m_sourceReader; + private CodeReader m_destReader; + private JButton m_matchButton; + private JButton m_unmatchableButton; + private JLabel m_sourceLabel; + private JLabel m_destLabel; + private HighlightPainter m_unmatchedHighlightPainter; + private HighlightPainter m_matchedHighlightPainter; + + private ClassMatches m_classMatches; + private MemberMatches m_memberMatches; + private Deobfuscator m_sourceDeobfuscator; + private Deobfuscator m_destDeobfuscator; + private SaveListener m_saveListener; + private SourceType m_sourceType; + private ClassEntry m_obfSourceClass; + private ClassEntry m_obfDestClass; + private T m_obfSourceEntry; + private T m_obfDestEntry; + + public MemberMatchingGui(ClassMatches classMatches, MemberMatches fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + + m_classMatches = classMatches; + m_memberMatches = fieldMatches; + m_sourceDeobfuscator = sourceDeobfuscator; + m_destDeobfuscator = destDeobfuscator; + + // init frame + m_frame = new JFrame(Constants.Name + " - Member Matcher"); + final Container pane = m_frame.getContentPane(); + pane.setLayout(new BorderLayout()); + + // init classes side + JPanel classesPanel = new JPanel(); + classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS)); + classesPanel.setPreferredSize(new Dimension(200, 0)); + pane.add(classesPanel, BorderLayout.WEST); + classesPanel.add(new JLabel("Classes")); + + // init source type radios + JPanel sourceTypePanel = new JPanel(); + classesPanel.add(sourceTypePanel); + sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); + ActionListener sourceTypeListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + setSourceType(SourceType.valueOf(event.getActionCommand())); + } + }; + ButtonGroup sourceTypeButtons = new ButtonGroup(); + m_sourceTypeButtons = Maps.newHashMap(); + for (SourceType sourceType : SourceType.values()) { + JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); + m_sourceTypeButtons.put(sourceType, button); + sourceTypePanel.add(button); + } + + m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); + m_sourceClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + setSourceClass(classEntry); + } + }); + JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); + classesPanel.add(sourceScroller); + + // init readers + DefaultSyntaxKit.initKit(); + m_sourceReader = new CodeReader(); + m_sourceReader.setSelectionListener(new CodeReader.SelectionListener() { + @Override + public void onSelect(EntryReference reference) { + if (reference != null) { + onSelectSource(reference.entry); + } else { + onSelectSource(null); + } + } + }); + m_destReader = new CodeReader(); + m_destReader.setSelectionListener(new CodeReader.SelectionListener() { + @Override + public void onSelect(EntryReference reference) { + if (reference != null) { + onSelectDest(reference.entry); + } else { + onSelectDest(null); + } + } + }); + + // add key bindings + KeyAdapter keyListener = new KeyAdapter() { + @Override + public void keyPressed(KeyEvent event) { + switch (event.getKeyCode()) { + case KeyEvent.VK_M: + m_matchButton.doClick(); + break; + } + } + }; + m_sourceReader.addKeyListener(keyListener); + m_destReader.addKeyListener(keyListener); + + // init all the splits + JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_sourceReader), new JScrollPane(m_destReader)); + splitRight.setResizeWeight(0.5); // resize 50:50 + JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight); + splitLeft.setResizeWeight(0); // let the right side take all the slack + pane.add(splitLeft, BorderLayout.CENTER); + splitLeft.resetToPreferredSizes(); + + // init bottom panel + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new FlowLayout()); + pane.add(bottomPanel, BorderLayout.SOUTH); + + m_matchButton = new JButton(); + m_unmatchableButton = new JButton(); + + m_sourceLabel = new JLabel(); + bottomPanel.add(m_sourceLabel); + bottomPanel.add(m_matchButton); + bottomPanel.add(m_unmatchableButton); + m_destLabel = new JLabel(); + bottomPanel.add(m_destLabel); + + // show the frame + pane.doLayout(); + m_frame.setSize(1024, 576); + m_frame.setMinimumSize(new Dimension(640, 480)); + m_frame.setVisible(true); + m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + m_unmatchedHighlightPainter = new ObfuscatedHighlightPainter(); + m_matchedHighlightPainter = new DeobfuscatedHighlightPainter(); + + // init state + m_saveListener = null; + m_obfSourceClass = null; + m_obfDestClass = null; + m_obfSourceEntry = null; + m_obfDestEntry = null; + setSourceType(SourceType.getDefault()); + updateButtons(); + } + + protected void setSourceType(SourceType val) { + m_sourceType = val; + updateSourceClasses(); + } + + public void setSaveListener(SaveListener val) { + m_saveListener = val; + } + + private void updateSourceClasses() { + + String selectedPackage = m_sourceClasses.getSelectedPackage(); + + List deobfClassEntries = Lists.newArrayList(); + for (ClassEntry entry : m_sourceType.getObfSourceClasses(m_memberMatches)) { + deobfClassEntries.add(m_sourceDeobfuscator.deobfuscateEntry(entry)); + } + m_sourceClasses.setClasses(deobfClassEntries); + + if (selectedPackage != null) { + m_sourceClasses.expandPackage(selectedPackage); + } + + for (SourceType sourceType : SourceType.values()) { + m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", + sourceType.name(), sourceType.getObfSourceClasses(m_memberMatches).size() + )); + } + } + + protected void setSourceClass(ClassEntry sourceClass) { + + m_obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass); + m_obfDestClass = m_classMatches.getUniqueMatches().get(m_obfSourceClass); + if (m_obfDestClass == null) { + throw new Error("No matching dest class for source class: " + m_obfSourceClass); + } + + m_sourceReader.decompileClass(m_obfSourceClass, m_sourceDeobfuscator, false, new Runnable() { + @Override + public void run() { + updateSourceHighlights(); + } + }); + m_destReader.decompileClass(m_obfDestClass, m_destDeobfuscator, false, new Runnable() { + @Override + public void run() { + updateDestHighlights(); + } + }); + } + + protected void updateSourceHighlights() { + highlightEntries(m_sourceReader, m_sourceDeobfuscator, m_memberMatches.matches().keySet(), m_memberMatches.getUnmatchedSourceEntries()); + } + + protected void updateDestHighlights() { + highlightEntries(m_destReader, m_destDeobfuscator, m_memberMatches.matches().values(), m_memberMatches.getUnmatchedDestEntries()); + } + + private void highlightEntries(CodeReader reader, Deobfuscator deobfuscator, Collection obfMatchedEntries, Collection obfUnmatchedEntries) { + reader.clearHighlights(); + SourceIndex index = reader.getSourceIndex(); + + // matched fields + for (T obfT : obfMatchedEntries) { + T deobfT = deobfuscator.deobfuscateEntry(obfT); + Token token = index.getDeclarationToken(deobfT); + if (token != null) { + reader.setHighlightedToken(token, m_matchedHighlightPainter); + } + } + + // unmatched fields + for (T obfT : obfUnmatchedEntries) { + T deobfT = deobfuscator.deobfuscateEntry(obfT); + Token token = index.getDeclarationToken(deobfT); + if (token != null) { + reader.setHighlightedToken(token, m_unmatchedHighlightPainter); + } + } + } + + private boolean isSelectionMatched() { + return m_obfSourceEntry != null && m_obfDestEntry != null + && m_memberMatches.isMatched(m_obfSourceEntry, m_obfDestEntry); + } + + protected void onSelectSource(Entry source) { + + // start with no selection + if (isSelectionMatched()) { + setDest(null); + } + setSource(null); + + // then look for a valid source selection + if (source != null) { + + // this looks really scary, but it's actually ok + // Deobfuscator.obfuscateEntry can handle all implementations of Entry + // and MemberMatches.hasSource() will only pass entries that actually match T + @SuppressWarnings("unchecked") + T sourceEntry = (T)source; + + T obfSourceEntry = m_sourceDeobfuscator.obfuscateEntry(sourceEntry); + if (m_memberMatches.hasSource(obfSourceEntry)) { + setSource(obfSourceEntry); + + // look for a matched dest too + T obfDestEntry = m_memberMatches.matches().get(obfSourceEntry); + if (obfDestEntry != null) { + setDest(obfDestEntry); + } + } + } + + updateButtons(); + } + + protected void onSelectDest(Entry dest) { + + // start with no selection + if (isSelectionMatched()) { + setSource(null); + } + setDest(null); + + // then look for a valid dest selection + if (dest != null) { + + // this looks really scary, but it's actually ok + // Deobfuscator.obfuscateEntry can handle all implementations of Entry + // and MemberMatches.hasSource() will only pass entries that actually match T + @SuppressWarnings("unchecked") + T destEntry = (T)dest; + + T obfDestEntry = m_destDeobfuscator.obfuscateEntry(destEntry); + if (m_memberMatches.hasDest(obfDestEntry)) { + setDest(obfDestEntry); + + // look for a matched source too + T obfSourceEntry = m_memberMatches.matches().inverse().get(obfDestEntry); + if (obfSourceEntry != null) { + setSource(obfSourceEntry); + } + } + } + + updateButtons(); + } + + private void setSource(T obfEntry) { + if (obfEntry == null) { + m_obfSourceEntry = obfEntry; + m_sourceLabel.setText(""); + } else { + m_obfSourceEntry = obfEntry; + m_sourceLabel.setText(getEntryLabel(obfEntry, m_sourceDeobfuscator)); + } + } + + private void setDest(T obfEntry) { + if (obfEntry == null) { + m_obfDestEntry = obfEntry; + m_destLabel.setText(""); + } else { + m_obfDestEntry = obfEntry; + m_destLabel.setText(getEntryLabel(obfEntry, m_destDeobfuscator)); + } + } + + private String getEntryLabel(T obfEntry, Deobfuscator deobfuscator) { + // deobfuscate, then take off the class name + T deobfEntry = deobfuscator.deobfuscateEntry(obfEntry); + return deobfEntry.toString().substring(deobfEntry.getClassName().length() + 1); + } + + private void updateButtons() { + + GuiTricks.deactivateButton(m_matchButton); + GuiTricks.deactivateButton(m_unmatchableButton); + + if (m_obfSourceEntry != null && m_obfDestEntry != null) { + if (m_memberMatches.isMatched(m_obfSourceEntry, m_obfDestEntry)) { + GuiTricks.activateButton(m_matchButton, "Unmatch", new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + unmatch(); + } + }); + } else if (!m_memberMatches.isMatchedSourceEntry(m_obfSourceEntry) && !m_memberMatches.isMatchedDestEntry(m_obfDestEntry)) { + GuiTricks.activateButton(m_matchButton, "Match", new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + match(); + } + }); + } + } else if (m_obfSourceEntry != null) { + GuiTricks.activateButton(m_unmatchableButton, "Set Unmatchable", new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + unmatchable(); + } + }); + } + } + + protected void match() { + + // update the field matches + m_memberMatches.makeMatch(m_obfSourceEntry, m_obfDestEntry); + save(); + + // update the ui + onSelectSource(null); + onSelectDest(null); + updateSourceHighlights(); + updateDestHighlights(); + updateSourceClasses(); + } + + protected void unmatch() { + + // update the field matches + m_memberMatches.unmakeMatch(m_obfSourceEntry, m_obfDestEntry); + save(); + + // update the ui + onSelectSource(null); + onSelectDest(null); + updateSourceHighlights(); + updateDestHighlights(); + updateSourceClasses(); + } + + protected void unmatchable() { + + // update the field matches + m_memberMatches.makeSourceUnmatchable(m_obfSourceEntry); + save(); + + // update the ui + onSelectSource(null); + onSelectDest(null); + updateSourceHighlights(); + updateDestHighlights(); + updateSourceClasses(); + } + + private void save() { + if (m_saveListener != null) { + m_saveListener.save(m_memberMatches); + } + } +} diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 43605e59..07fed32a 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -434,4 +434,8 @@ public class ClassMapping implements Serializable, Comparable { public static boolean isSimpleClassName(String name) { return name.indexOf('/') < 0 && name.indexOf('$') < 0; } + + public ClassEntry getObfEntry() { + return new ClassEntry(m_obfFullName); + } } diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index 333bb09b..7bc61839 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java @@ -138,6 +138,10 @@ public class EntryFactory { public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) { return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); } + + public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) { + return getBehaviorEntry(new ClassEntry(className), behaviorName); + } public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) { if (behaviorName.equals("")) { @@ -149,6 +153,14 @@ public class EntryFactory { } } + public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) { + if(behaviorName.equals("")) { + return new ConstructorEntry(classEntry); + } else { + throw new IllegalArgumentException("Only class initializers don't have signatures"); + } + } + public static BehaviorEntry getBehaviorEntry(MethodDefinition def) { if (def.isConstructor() || def.isTypeInitializer()) { return getConstructorEntry(def); diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java index 55b0a195..1289351d 100644 --- a/src/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -12,7 +12,7 @@ package cuchaz.enigma.mapping; import java.io.Serializable; -public class FieldMapping implements Serializable, Comparable { +public class FieldMapping implements Serializable, Comparable, MemberMapping { private static final long serialVersionUID = 8610742471440861315L; @@ -72,4 +72,9 @@ public class FieldMapping implements Serializable, Comparable { } return false; } + + @Override + public FieldEntry getObfEntry(ClassEntry classEntry) { + return new FieldEntry(classEntry, m_obfName, new Type(m_obfType)); + } } diff --git a/src/cuchaz/enigma/mapping/MemberMapping.java b/src/cuchaz/enigma/mapping/MemberMapping.java new file mode 100644 index 00000000..d62a7c10 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MemberMapping.java @@ -0,0 +1,6 @@ +package cuchaz.enigma.mapping; + + +public interface MemberMapping { + T getObfEntry(ClassEntry classEntry); +} diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index bf8a94f3..bf6dacca 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -16,7 +16,7 @@ import java.util.Map.Entry; import com.google.common.collect.Maps; -public class MethodMapping implements Serializable, Comparable { +public class MethodMapping implements Serializable, Comparable, MemberMapping { private static final long serialVersionUID = -4409570216084263978L; @@ -170,4 +170,13 @@ public class MethodMapping implements Serializable, Comparable { } return false; } + + @Override + public BehaviorEntry getObfEntry(ClassEntry classEntry) { + if (isConstructor()) { + return new ConstructorEntry(classEntry, m_obfSignature); + } else { + return new MethodEntry(classEntry, m_obfName, m_obfSignature); + } + } } -- cgit v1.2.3 From dc2120999c137aa4763ea2358b8df83f4098d280 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 11 Mar 2015 11:44:24 -0400 Subject: working on writing mappings based on all the matches --- src/cuchaz/enigma/ConvertMain.java | 50 ++++++++++++++++++------ src/cuchaz/enigma/convert/MappingsConverter.java | 50 +++++++++++++++++++++--- src/cuchaz/enigma/mapping/ClassMapping.java | 18 +++++++++ src/cuchaz/enigma/mapping/FieldMapping.java | 4 ++ src/cuchaz/enigma/mapping/MethodMapping.java | 4 ++ 5 files changed, 109 insertions(+), 17 deletions(-) diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index c3a2ad5c..15658d90 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -49,15 +49,10 @@ public class ConvertMain { // match methods/constructors //computeMethodMatches(methodMatchesFile, destJar, outMappingsFile, classMatchesFile); - editMethodMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, methodMatchesFile); + //editMethodMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, methodMatchesFile); - /* TODO - // write out the converted mappings - FileWriter writer = new FileWriter(outMappingsFile); - new MappingsWriter().write(writer, mappings); - writer.close(); - System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); - */ + // write final converted mappings + writeFinalMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); } private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) @@ -93,7 +88,7 @@ public class ConvertMain { Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); deobfuscators.source.setMappings(mappings); - Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.source); + Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); try (FileWriter out = new FileWriter(outMappingsFile)) { new MappingsWriter().write(out, newMappings); @@ -114,7 +109,12 @@ public class ConvertMain { System.out.println("Writing matches..."); // get the matched and unmatched mappings - MemberMatches fieldMatches = MappingsConverter.computeFieldMatches(destDeobfuscator, destMappings, classMatches); + MemberMatches fieldMatches = MappingsConverter.computeMemberMatches( + destDeobfuscator, + destMappings, + classMatches, + MappingsConverter.getFieldDoer() + ); MatchesWriter.writeMembers(fieldMatches, memberMatchesFile); System.out.println("Wrote:\n\t" + memberMatchesFile.getAbsolutePath()); @@ -160,7 +160,12 @@ public class ConvertMain { System.out.println("Writing method matches..."); // get the matched and unmatched mappings - MemberMatches methodMatches = MappingsConverter.computeBehaviorMatches(destDeobfuscator, destMappings, classMatches); + MemberMatches methodMatches = MappingsConverter.computeMemberMatches( + destDeobfuscator, + destMappings, + classMatches, + MappingsConverter.getMethodDoer() + ); MatchesWriter.writeMembers(methodMatches, methodMatchesFile); System.out.println("Wrote:\n\t" + methodMatchesFile.getAbsolutePath()); @@ -192,6 +197,29 @@ public class ConvertMain { } }); } + + private static void writeFinalMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile) + throws IOException { + + System.out.println("Reading matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); + MemberMatches fieldMatches = MatchesReader.readMembers(fieldMatchesFile); + MemberMatches methodMatches = MatchesReader.readMembers(methodMatchesFile); + + Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); + deobfuscators.source.setMappings(mappings); + + // apply matches + Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); + MappingsConverter.applyMemberMatches(newMappings, fieldMatches, MappingsConverter.getFieldDoer()); + MappingsConverter.applyMemberMatches(newMappings, methodMatches, MappingsConverter.getMethodDoer()); + + // write out the converted mappings + try (FileWriter out = new FileWriter(outMappingsFile)) { + new MappingsWriter().write(out, newMappings); + } + System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); + } private static class Deobfuscators { diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index 2987ea08..44bc8b87 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -262,10 +262,11 @@ public class MappingsConverter { Collection getObfEntries(JarIndex jarIndex); Collection> getMappings(ClassMapping destClassMapping); Set filterEntries(Collection obfEntries, T obfSourceEntry, ClassMatches classMatches); + void setMemberObfName(ClassMapping classMapping, MemberMapping memberMapping, String newObfName); } - public static MemberMatches computeFieldMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches) { - return computeMemberMatches(destDeobfuscator, destMappings, classMatches, new Doer() { + public static Doer getFieldDoer() { + return new Doer() { @Override public Collection getDroppedEntries(MappingsChecker checker) { @@ -293,11 +294,17 @@ public class MappingsConverter { } return out; } - }); + + @Override + public void setMemberObfName(ClassMapping classMapping, MemberMapping memberMapping, String newObfName) { + FieldMapping fieldMapping = (FieldMapping)memberMapping; + classMapping.setFieldObfName(fieldMapping.getObfName(), fieldMapping.getObfType(), newObfName); + } + }; } - public static MemberMatches computeBehaviorMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches) { - return computeMemberMatches(destDeobfuscator, destMappings, classMatches, new Doer() { + public static Doer getMethodDoer() { + return new Doer() { @Override public Collection getDroppedEntries(MappingsChecker checker) { @@ -329,7 +336,13 @@ public class MappingsConverter { } return out; } - }); + + @Override + public void setMemberObfName(ClassMapping classMapping, MemberMapping memberMapping, String newObfName) { + MethodMapping methodMapping = (MethodMapping)memberMapping; + classMapping.setMethodObfName(methodMapping.getObfName(), methodMapping.getObfSignature(), newObfName); + } + }; } public static MemberMatches computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer doer) { @@ -452,4 +465,29 @@ public class MappingsConverter { } }); } + + public static void applyMemberMatches(Mappings mappings, MemberMatches memberMatches, Doer doer) { + for (ClassMapping classMapping : mappings.classes()) { + applyMemberMatches(classMapping, memberMatches, doer); + } + } + + private static void applyMemberMatches(ClassMapping classMapping, MemberMatches memberMatches, Doer doer) { + ClassEntry classEntry = classMapping.getObfEntry(); + + // apply to this class + // TODO: need to sort renames so they happen in the right order!! + for (MemberMapping memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { + T obfSourceEntry = memberMapping.getObfEntry(classEntry); + T obfDestEntry = memberMatches.matches().get(obfSourceEntry); + if (obfDestEntry != null) { + doer.setMemberObfName(classMapping, memberMapping, obfDestEntry.getName()); + } + } + + // recurse + for (ClassMapping innerClassMapping : classMapping.innerClasses()) { + applyMemberMatches(innerClassMapping, memberMatches, doer); + } + } } diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 07fed32a..ac70df0d 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -243,6 +243,15 @@ public class ClassMapping implements Serializable, Comparable { } } + public void setFieldObfName(String oldObfName, Type obfType, String newObfName) { + assert(newObfName != null); + FieldMapping fieldMapping = m_fieldsByObf.remove(getFieldKey(oldObfName, obfType)); + assert(fieldMapping != null); + fieldMapping.setObfName(newObfName); + boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(newObfName, obfType), fieldMapping) == null; + assert(obfWasAdded); + } + //// METHODS //////// @@ -319,6 +328,15 @@ public class ClassMapping implements Serializable, Comparable { } } + public void setMethodObfName(String oldObfName, Signature obfSignature, String newObfName) { + assert(newObfName != null); + MethodMapping methodMapping = m_methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); + assert(methodMapping != null); + methodMapping.setObfName(newObfName); + boolean obfWasAdded = m_methodsByObf.put(getMethodKey(newObfName, obfSignature), methodMapping) == null; + assert(obfWasAdded); + } + //// ARGUMENTS //////// public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java index 1289351d..3aa9e698 100644 --- a/src/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -36,6 +36,10 @@ public class FieldMapping implements Serializable, Comparable, Mem return m_obfName; } + public void setObfName(String val) { + m_obfName = NameValidator.validateFieldName(val); + } + public String getDeobfName() { return m_deobfName; } diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index bf6dacca..a67e352f 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -56,6 +56,10 @@ public class MethodMapping implements Serializable, Comparable, M return m_obfName; } + public void setObfName(String val) { + m_obfName = NameValidator.validateMethodName(val); + } + public String getDeobfName() { return m_deobfName; } -- cgit v1.2.3 From 65f551cd25739f1ccfa15d819c6a23060ebf2629 Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 13 Mar 2015 16:46:02 -0400 Subject: complete mappings converion code. Still need to debug though --- src/cuchaz/enigma/ConvertMain.java | 29 ++++++++++++--- src/cuchaz/enigma/convert/MappingsConverter.java | 45 ++++++++++++++++++++++-- src/cuchaz/enigma/gui/MemberMatchingGui.java | 4 +-- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index 15658d90..c5c92bcb 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -46,13 +46,12 @@ public class ConvertMain { // match fields //computeFieldMatches(fieldMatchesFile, destJar, outMappingsFile, classMatchesFile); //editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, fieldMatchesFile); + //convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile); // match methods/constructors //computeMethodMatches(methodMatchesFile, destJar, outMappingsFile, classMatchesFile); //editMethodMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, methodMatchesFile); - - // write final converted mappings - writeFinalMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); + convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); } private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) @@ -146,6 +145,28 @@ public class ConvertMain { } }); } + + private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile) + throws IOException { + + System.out.println("Reading matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); + MemberMatches fieldMatches = MatchesReader.readMembers(fieldMatchesFile); + + Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); + deobfuscators.source.setMappings(mappings); + + // apply matches + Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); + MappingsConverter.applyMemberMatches(newMappings, fieldMatches, MappingsConverter.getFieldDoer()); + + // write out the converted mappings + try (FileWriter out = new FileWriter(outMappingsFile)) { + new MappingsWriter().write(out, newMappings); + } + System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); + } + private static void computeMethodMatches(File methodMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile) throws IOException, MappingParseException { @@ -198,7 +219,7 @@ public class ConvertMain { }); } - private static void writeFinalMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile) + private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile) throws IOException { System.out.println("Reading matches..."); diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index 44bc8b87..59f3b5ba 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -263,6 +263,7 @@ public class MappingsConverter { Collection> getMappings(ClassMapping destClassMapping); Set filterEntries(Collection obfEntries, T obfSourceEntry, ClassMatches classMatches); void setMemberObfName(ClassMapping classMapping, MemberMapping memberMapping, String newObfName); + boolean hasObfMember(ClassMapping classMapping, T obfEntry); } public static Doer getFieldDoer() { @@ -300,6 +301,11 @@ public class MappingsConverter { FieldMapping fieldMapping = (FieldMapping)memberMapping; classMapping.setFieldObfName(fieldMapping.getObfName(), fieldMapping.getObfType(), newObfName); } + + @Override + public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) { + return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null; + } }; } @@ -342,6 +348,11 @@ public class MappingsConverter { MethodMapping methodMapping = (MethodMapping)memberMapping; classMapping.setMethodObfName(methodMapping.getObfName(), methodMapping.getObfSignature(), newObfName); } + + @Override + public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) { + return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null; + } }; } @@ -475,13 +486,41 @@ public class MappingsConverter { private static void applyMemberMatches(ClassMapping classMapping, MemberMatches memberMatches, Doer doer) { ClassEntry classEntry = classMapping.getObfEntry(); - // apply to this class - // TODO: need to sort renames so they happen in the right order!! + // make a map of all the renames we need to make + Map renames = Maps.newHashMap(); for (MemberMapping memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { T obfSourceEntry = memberMapping.getObfEntry(classEntry); T obfDestEntry = memberMatches.matches().get(obfSourceEntry); if (obfDestEntry != null) { - doer.setMemberObfName(classMapping, memberMapping, obfDestEntry.getName()); + if (obfDestEntry.getName().equals(obfSourceEntry.getName())) { + // same name, don't need to change anything + continue; + } + renames.put(obfSourceEntry, obfDestEntry); + } + } + + // apply to this class (should never need more than n passes) + int numRenames = renames.size(); + for (int i=0; i memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { + T obfSourceEntry = memberMapping.getObfEntry(classEntry); + T obfDestEntry = renames.get(obfSourceEntry); + if (obfDestEntry != null) { + // make sure this rename won't cause a collision + if (!doer.hasObfMember(classMapping, obfDestEntry)) { + doer.setMemberObfName(classMapping, memberMapping, obfDestEntry.getName()); + renames.remove(obfSourceEntry); + } + } + } + } + if (!renames.isEmpty()) { + System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d/%d renames left.", + classMapping.getObfFullName(), renames.size(), numRenames + )); + for (Map.Entry entry : renames.entrySet()) { + System.err.println(String.format("\t%s -> %s", entry.getKey(), entry.getValue())); } } diff --git a/src/cuchaz/enigma/gui/MemberMatchingGui.java b/src/cuchaz/enigma/gui/MemberMatchingGui.java index 52545b33..181fb548 100644 --- a/src/cuchaz/enigma/gui/MemberMatchingGui.java +++ b/src/cuchaz/enigma/gui/MemberMatchingGui.java @@ -403,9 +403,9 @@ public class MemberMatchingGui { } private String getEntryLabel(T obfEntry, Deobfuscator deobfuscator) { - // deobfuscate, then take off the class name + // show obfuscated and deobfuscated names, but no types/signatures T deobfEntry = deobfuscator.deobfuscateEntry(obfEntry); - return deobfEntry.toString().substring(deobfEntry.getClassName().length() + 1); + return String.format("%s (%s)", deobfEntry.getName(), obfEntry.getName()); } private void updateButtons() { -- cgit v1.2.3 From 294e97d7e4cda6cadb62918fd822e7d0d11f16a9 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 15 Mar 2015 00:44:35 -0400 Subject: fix bugs in the mappings converter --- src/cuchaz/enigma/ConvertMain.java | 27 ++++- src/cuchaz/enigma/convert/MappingsConverter.java | 125 ++++++++++++++--------- src/cuchaz/enigma/convert/MemberMatches.java | 4 + src/cuchaz/enigma/mapping/ClassMapping.java | 10 +- src/cuchaz/enigma/mapping/FieldMapping.java | 5 + src/cuchaz/enigma/mapping/MemberMapping.java | 1 + src/cuchaz/enigma/mapping/MethodMapping.java | 5 + 7 files changed, 121 insertions(+), 56 deletions(-) diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index c5c92bcb..79caae44 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -14,12 +14,16 @@ import cuchaz.enigma.convert.MemberMatches; import cuchaz.enigma.gui.ClassMatchingGui; import cuchaz.enigma.gui.MemberMatchingGui; import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.FieldMapping; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsChecker; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; +import cuchaz.enigma.mapping.MethodMapping; public class ConvertMain { @@ -158,7 +162,7 @@ public class ConvertMain { // apply matches Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); - MappingsConverter.applyMemberMatches(newMappings, fieldMatches, MappingsConverter.getFieldDoer()); + MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer()); // write out the converted mappings try (FileWriter out = new FileWriter(outMappingsFile)) { @@ -232,8 +236,25 @@ public class ConvertMain { // apply matches Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); - MappingsConverter.applyMemberMatches(newMappings, fieldMatches, MappingsConverter.getFieldDoer()); - MappingsConverter.applyMemberMatches(newMappings, methodMatches, MappingsConverter.getMethodDoer()); + MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer()); + MappingsConverter.applyMemberMatches(newMappings, classMatches, methodMatches, MappingsConverter.getMethodDoer()); + + // check the final mappings + MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); + checker.dropBrokenMappings(newMappings); + + for (java.util.Map.Entry mapping : checker.getDroppedClassMappings().entrySet()) { + System.out.println("WARNING: Broken class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); + } + for (java.util.Map.Entry mapping : checker.getDroppedInnerClassMappings().entrySet()) { + System.out.println("WARNING: Broken inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); + } + for (java.util.Map.Entry mapping : checker.getDroppedFieldMappings().entrySet()) { + System.out.println("WARNING: Broken field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); + } + for (java.util.Map.Entry mapping : checker.getDroppedMethodMappings().entrySet()) { + System.out.println("WARNING: Broken behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); + } // write out the converted mappings try (FileWriter out = new FileWriter(outMappingsFile)) { diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index 59f3b5ba..ddd3a53c 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -183,7 +183,7 @@ public class MappingsConverter { return newMappings; } - private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping mapping, final ClassMatches matches, boolean useSimpleName) { + private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping oldClassMapping, final ClassMatches matches, boolean useSimpleName) { ClassNameReplacer replacer = new ClassNameReplacer() { @Override @@ -196,30 +196,28 @@ public class MappingsConverter { } }; - ClassMapping newMapping; - String deobfName = mapping.getDeobfName(); + ClassMapping newClassMapping; + String deobfName = oldClassMapping.getDeobfName(); if (deobfName != null) { if (useSimpleName) { deobfName = new ClassEntry(deobfName).getSimpleName(); } - newMapping = new ClassMapping(newObfClass.getName(), deobfName); + newClassMapping = new ClassMapping(newObfClass.getName(), deobfName); } else { - newMapping = new ClassMapping(newObfClass.getName()); + newClassMapping = new ClassMapping(newObfClass.getName()); } // copy fields - for (FieldMapping fieldMapping : mapping.fields()) { - // TODO: map field obf names too... - newMapping.addFieldMapping(new FieldMapping(fieldMapping, replacer)); + for (FieldMapping fieldMapping : oldClassMapping.fields()) { + newClassMapping.addFieldMapping(new FieldMapping(fieldMapping, replacer)); } // copy methods - for (MethodMapping methodMapping : mapping.methods()) { - // TODO: map method obf names too... - newMapping.addMethodMapping(new MethodMapping(methodMapping, replacer)); + for (MethodMapping methodMapping : oldClassMapping.methods()) { + newClassMapping.addMethodMapping(new MethodMapping(methodMapping, replacer)); } - return newMapping; + return newClassMapping; } public static void convertMappings(Mappings mappings, BiMap changes) { @@ -262,8 +260,9 @@ public class MappingsConverter { Collection getObfEntries(JarIndex jarIndex); Collection> getMappings(ClassMapping destClassMapping); Set filterEntries(Collection obfEntries, T obfSourceEntry, ClassMatches classMatches); - void setMemberObfName(ClassMapping classMapping, MemberMapping memberMapping, String newObfName); + void setUpdateObfMember(ClassMapping classMapping, MemberMapping memberMapping, T newEntry); boolean hasObfMember(ClassMapping classMapping, T obfEntry); + void removeMemberByObf(ClassMapping classMapping, T obfEntry); } public static Doer getFieldDoer() { @@ -297,15 +296,20 @@ public class MappingsConverter { } @Override - public void setMemberObfName(ClassMapping classMapping, MemberMapping memberMapping, String newObfName) { + public void setUpdateObfMember(ClassMapping classMapping, MemberMapping memberMapping, FieldEntry newField) { FieldMapping fieldMapping = (FieldMapping)memberMapping; - classMapping.setFieldObfName(fieldMapping.getObfName(), fieldMapping.getObfType(), newObfName); + classMapping.setFieldObfNameAndType(fieldMapping.getObfName(), fieldMapping.getObfType(), newField.getName(), newField.getType()); } @Override public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) { return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null; } + + @Override + public void removeMemberByObf(ClassMapping classMapping, FieldEntry obfField) { + classMapping.removeFieldMapping(classMapping.getFieldByObf(obfField.getName(), obfField.getType())); + } }; } @@ -344,15 +348,20 @@ public class MappingsConverter { } @Override - public void setMemberObfName(ClassMapping classMapping, MemberMapping memberMapping, String newObfName) { + public void setUpdateObfMember(ClassMapping classMapping, MemberMapping memberMapping, BehaviorEntry newBehavior) { MethodMapping methodMapping = (MethodMapping)memberMapping; - classMapping.setMethodObfName(methodMapping.getObfName(), methodMapping.getObfSignature(), newObfName); + classMapping.setMethodObfNameAndSignature(methodMapping.getObfName(), methodMapping.getObfSignature(), newBehavior.getName(), newBehavior.getSignature()); } @Override public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) { return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null; } + + @Override + public void removeMemberByObf(ClassMapping classMapping, BehaviorEntry obfBehavior) { + classMapping.removeMethodMapping(classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature())); + } }; } @@ -477,56 +486,74 @@ public class MappingsConverter { }); } - public static void applyMemberMatches(Mappings mappings, MemberMatches memberMatches, Doer doer) { + public static void applyMemberMatches(Mappings mappings, ClassMatches classMatches, MemberMatches memberMatches, Doer doer) { for (ClassMapping classMapping : mappings.classes()) { - applyMemberMatches(classMapping, memberMatches, doer); + applyMemberMatches(classMapping, classMatches, memberMatches, doer); } } - private static void applyMemberMatches(ClassMapping classMapping, MemberMatches memberMatches, Doer doer) { - ClassEntry classEntry = classMapping.getObfEntry(); + private static void applyMemberMatches(ClassMapping classMapping, ClassMatches classMatches, MemberMatches memberMatches, Doer doer) { + + // get the classes + ClassEntry obfDestClass = classMapping.getObfEntry(); // make a map of all the renames we need to make Map renames = Maps.newHashMap(); for (MemberMapping memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { - T obfSourceEntry = memberMapping.getObfEntry(classEntry); - T obfDestEntry = memberMatches.matches().get(obfSourceEntry); - if (obfDestEntry != null) { - if (obfDestEntry.getName().equals(obfSourceEntry.getName())) { - // same name, don't need to change anything - continue; - } - renames.put(obfSourceEntry, obfDestEntry); + T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); + T obfSourceEntry = getSourceEntryFromDestMapping(memberMapping, obfDestClass, classMatches); + + // but drop the unmatchable things + if (memberMatches.isUnmatchableSourceEntry(obfSourceEntry)) { + doer.removeMemberByObf(classMapping, obfOldDestEntry); + continue; + } + + T obfNewDestEntry = memberMatches.matches().get(obfSourceEntry); + if (obfNewDestEntry != null && !obfOldDestEntry.getName().equals(obfNewDestEntry.getName())) { + renames.put(obfOldDestEntry, obfNewDestEntry); } } - // apply to this class (should never need more than n passes) - int numRenames = renames.size(); - for (int i=0; i memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { - T obfSourceEntry = memberMapping.getObfEntry(classEntry); - T obfDestEntry = renames.get(obfSourceEntry); - if (obfDestEntry != null) { - // make sure this rename won't cause a collision - if (!doer.hasObfMember(classMapping, obfDestEntry)) { - doer.setMemberObfName(classMapping, memberMapping, obfDestEntry.getName()); - renames.remove(obfSourceEntry); + if (!renames.isEmpty()) { + + // apply to this class (should never need more than n passes) + int numRenamesAppliedThisRound; + do { + numRenamesAppliedThisRound = 0; + + for (MemberMapping memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { + T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); + T obfNewDestEntry = renames.get(obfOldDestEntry); + if (obfNewDestEntry != null) { + // make sure this rename won't cause a collision + // otherwise, save it for the next round and try again next time + if (!doer.hasObfMember(classMapping, obfNewDestEntry)) { + doer.setUpdateObfMember(classMapping, memberMapping, obfNewDestEntry); + renames.remove(obfOldDestEntry); + numRenamesAppliedThisRound++; + } } } - } - } - if (!renames.isEmpty()) { - System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d/%d renames left.", - classMapping.getObfFullName(), renames.size(), numRenames - )); - for (Map.Entry entry : renames.entrySet()) { - System.err.println(String.format("\t%s -> %s", entry.getKey(), entry.getValue())); + } while(numRenamesAppliedThisRound > 0); + + if (!renames.isEmpty()) { + System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d renames left.", + classMapping.getObfFullName(), renames.size() + )); + for (Map.Entry entry : renames.entrySet()) { + System.err.println(String.format("\t%s -> %s", entry.getKey().getName(), entry.getValue().getName())); + } } } // recurse for (ClassMapping innerClassMapping : classMapping.innerClasses()) { - applyMemberMatches(innerClassMapping, memberMatches, doer); + applyMemberMatches(innerClassMapping, classMatches, memberMatches, doer); } } + + private static T getSourceEntryFromDestMapping(MemberMapping destMemberMapping, ClassEntry obfDestClass, ClassMatches classMatches) { + return translate(destMemberMapping.getObfEntry(obfDestClass), classMatches.getUniqueMatches().inverse()); + } } diff --git a/src/cuchaz/enigma/convert/MemberMatches.java b/src/cuchaz/enigma/convert/MemberMatches.java index 1078ab75..1c162eac 100644 --- a/src/cuchaz/enigma/convert/MemberMatches.java +++ b/src/cuchaz/enigma/convert/MemberMatches.java @@ -113,6 +113,10 @@ public class MemberMatches { public boolean isMatchedDestEntry(T destEntry) { return m_matches.containsValue(destEntry); } + + public boolean isUnmatchableSourceEntry(T sourceEntry) { + return m_unmatchableSourceEntries.containsEntry(sourceEntry.getClassEntry(), sourceEntry); + } public void makeMatch(T sourceEntry, T destEntry) { boolean wasRemoved = m_unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index ac70df0d..38cd3d6d 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -243,12 +243,13 @@ public class ClassMapping implements Serializable, Comparable { } } - public void setFieldObfName(String oldObfName, Type obfType, String newObfName) { + public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { assert(newObfName != null); FieldMapping fieldMapping = m_fieldsByObf.remove(getFieldKey(oldObfName, obfType)); assert(fieldMapping != null); fieldMapping.setObfName(newObfName); - boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(newObfName, obfType), fieldMapping) == null; + fieldMapping.setObfType(newObfType); + boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; assert(obfWasAdded); } @@ -328,12 +329,13 @@ public class ClassMapping implements Serializable, Comparable { } } - public void setMethodObfName(String oldObfName, Signature obfSignature, String newObfName) { + public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { assert(newObfName != null); MethodMapping methodMapping = m_methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); assert(methodMapping != null); methodMapping.setObfName(newObfName); - boolean obfWasAdded = m_methodsByObf.put(getMethodKey(newObfName, obfSignature), methodMapping) == null; + methodMapping.setObfSignature(newObfSignature); + boolean obfWasAdded = m_methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; assert(obfWasAdded); } diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java index 3aa9e698..463d901c 100644 --- a/src/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -32,6 +32,7 @@ public class FieldMapping implements Serializable, Comparable, Mem m_obfType = new Type(other.m_obfType, obfClassNameReplacer); } + @Override public String getObfName() { return m_obfName; } @@ -52,6 +53,10 @@ public class FieldMapping implements Serializable, Comparable, Mem return m_obfType; } + public void setObfType(Type val) { + m_obfType = val; + } + @Override public int compareTo(FieldMapping other) { return (m_obfName + m_obfType).compareTo(other.m_obfName + other.m_obfType); diff --git a/src/cuchaz/enigma/mapping/MemberMapping.java b/src/cuchaz/enigma/mapping/MemberMapping.java index d62a7c10..d8e2ee3a 100644 --- a/src/cuchaz/enigma/mapping/MemberMapping.java +++ b/src/cuchaz/enigma/mapping/MemberMapping.java @@ -3,4 +3,5 @@ package cuchaz.enigma.mapping; public interface MemberMapping { T getObfEntry(ClassEntry classEntry); + String getObfName(); } diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index a67e352f..bef232e3 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -52,6 +52,7 @@ public class MethodMapping implements Serializable, Comparable, M } } + @Override public String getObfName() { return m_obfName; } @@ -72,6 +73,10 @@ public class MethodMapping implements Serializable, Comparable, M return m_obfSignature; } + public void setObfSignature(Signature val) { + m_obfSignature = val; + } + public Iterable arguments() { return m_arguments.values(); } -- cgit v1.2.3 From 2ff03cb72dccafd387776f5b0f91be1e8b056afb Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 15 Mar 2015 09:53:21 -0400 Subject: repackage for 0.9 beta --- build.py | 4 ++-- src/cuchaz/enigma/Constants.java | 2 +- test/cuchaz/enigma/TestSourceIndex.java | 7 +++++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/build.py b/build.py index c2caa299..03212496 100644 --- a/build.py +++ b/build.py @@ -18,8 +18,8 @@ import ssjb import ssjb.ivy -ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.8b") -ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.8b") +ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.9b") +ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.9b") # dependencies ExtraRepos = [ diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index db147781..8910a960 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -12,7 +12,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.8 beta"; + public static final String Version = "0.9 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024 * 1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java index 96a8923b..94bf941d 100644 --- a/test/cuchaz/enigma/TestSourceIndex.java +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -11,6 +11,7 @@ ******************************************************************************/ package cuchaz.enigma; +import java.io.File; import java.util.Set; import java.util.jar.JarFile; @@ -23,11 +24,13 @@ import cuchaz.enigma.mapping.ClassEntry; public class TestSourceIndex { - // TEMP @Test public void indexEverything() throws Exception { - Deobfuscator deobfuscator = new Deobfuscator(new JarFile("input/1.8.jar")); + + File home = new File(System.getProperty("user.home")); + File jarFile = new File(home, "/.minecraft/versions/1.8.3/1.8.3.jar"); + Deobfuscator deobfuscator = new Deobfuscator(new JarFile(jarFile)); // get all classes that aren't inner classes Set classEntries = Sets.newHashSet(); -- cgit v1.2.3 -- cgit v1.2.3 From c133e05b786ff5357931842581571c046f958c74 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 16 Mar 2015 12:29:17 -0400 Subject: fix a zillion issues with inner classes --- proguard.conf | 2 + src/cuchaz/enigma/Deobfuscator.java | 8 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 2 +- src/cuchaz/enigma/analysis/EntryReference.java | 2 +- src/cuchaz/enigma/analysis/JarIndex.java | 2 +- src/cuchaz/enigma/bytecode/ClassRenamer.java | 2 +- src/cuchaz/enigma/bytecode/ClassTranslator.java | 17 +-- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 4 +- src/cuchaz/enigma/convert/ClassIdentity.java | 2 +- src/cuchaz/enigma/convert/MappingsConverter.java | 2 +- src/cuchaz/enigma/gui/CodeReader.java | 2 +- src/cuchaz/enigma/gui/GuiController.java | 2 +- src/cuchaz/enigma/mapping/ClassEntry.java | 31 ++++- src/cuchaz/enigma/mapping/ClassMapping.java | 79 +++++++------ src/cuchaz/enigma/mapping/Mappings.java | 28 +++++ src/cuchaz/enigma/mapping/MappingsRenamer.java | 128 ++++++++++----------- src/cuchaz/enigma/mapping/Translator.java | 73 +++--------- test/cuchaz/enigma/TestDeobfed.java | 54 +++++---- test/cuchaz/enigma/TestInnerClasses.java | 38 +++--- test/cuchaz/enigma/TestSourceIndex.java | 2 +- test/cuchaz/enigma/TestTranslator.java | 29 +++-- .../enigma/inputs/translation/F_ObjectMethods.java | 19 +++ .../enigma/inputs/translation/G_ObjectMethods.java | 19 --- .../enigma/inputs/translation/G_OuterClass.java | 26 +++++ .../enigma/inputs/translation/H_NamelessClass.java | 28 +++++ .../enigma/inputs/translation/H_OuterClass.java | 26 ----- .../enigma/inputs/translation/I_Generics.java | 25 ++++ .../enigma/inputs/translation/M_NamelessClass.java | 28 ----- test/cuchaz/enigma/resources/translation.mappings | 13 ++- 29 files changed, 372 insertions(+), 321 deletions(-) create mode 100644 test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java delete mode 100644 test/cuchaz/enigma/inputs/translation/G_ObjectMethods.java create mode 100644 test/cuchaz/enigma/inputs/translation/G_OuterClass.java create mode 100644 test/cuchaz/enigma/inputs/translation/H_NamelessClass.java delete mode 100644 test/cuchaz/enigma/inputs/translation/H_OuterClass.java create mode 100644 test/cuchaz/enigma/inputs/translation/I_Generics.java delete mode 100644 test/cuchaz/enigma/inputs/translation/M_NamelessClass.java diff --git a/proguard.conf b/proguard.conf index e1f04aee..0d3d60e9 100644 --- a/proguard.conf +++ b/proguard.conf @@ -4,4 +4,6 @@ -allowaccessmodification -dontoptimize -dontshrink +-keepparameternames +-keepattributes -keep class cuchaz.enigma.inputs.Keep \ No newline at end of file diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index f5012bde..5a23ce5b 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -438,7 +438,13 @@ public class Deobfuscator { public boolean hasDeobfuscatedName(Entry obfEntry) { Translator translator = getTranslator(TranslationDirection.Deobfuscating); if (obfEntry instanceof ClassEntry) { - return translator.translate((ClassEntry)obfEntry) != null; + ClassEntry obfClass = (ClassEntry)obfEntry; + ClassEntry translated = translator.translateEntry(obfClass); + if (obfClass.isInnerClass()) { + return !obfClass.getInnermostClassName().equals(translated.getInnermostClassName()); + } else { + return !obfClass.equals(translated); + } } else if (obfEntry instanceof FieldEntry) { return translator.translate((FieldEntry)obfEntry) != null; } else if (obfEntry instanceof MethodEntry) { diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 7b57cfa7..ecd7d642 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -205,7 +205,7 @@ public class TranslatingTypeLoader implements ITypeLoader { } if (obfClassEntry.isInnerClass()) { // try just the inner class name - classNamesToTry.add(obfClassEntry.getInnerClassName()); + classNamesToTry.add(obfClassEntry.getInnermostClassName()); } return classNamesToTry; } diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java index bb611df5..d0a5c6af 100644 --- a/src/cuchaz/enigma/analysis/EntryReference.java +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -74,7 +74,7 @@ public class EntryReference { ClassEntry classEntry = (ClassEntry)getNameableEntry(); if (classEntry.isInnerClass()) { // make sure we only rename the inner class name - return classEntry.getInnerClassName(); + return classEntry.getInnermostClassName(); } } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 7ebbd974..a4a3abb8 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -312,7 +312,7 @@ public class JarIndex { // does this class already have an outer class? if (classEntry.isInnerClass()) { - return classEntry.getOuterClassEntry(); + return classEntry.getOutermostClassEntry(); } InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); if (innerClassesAttribute != null) { diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index a5fea926..e9cdea3c 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -43,7 +43,7 @@ public class ClassRenamer { for (int i = 0; i < attr.tableLength(); i++) { ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(attr.innerClass(i))); if (attr.innerNameIndex(i) != 0) { - attr.setInnerNameIndex(i, constants.addUtf8Info(classEntry.getInnerClassName())); + attr.setInnerNameIndex(i, constants.addUtf8Info(classEntry.getInnermostClassName())); } /* DEBUG diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 4167731a..94ab2c4b 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -26,7 +26,6 @@ import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Signature; import cuchaz.enigma.mapping.Translator; import cuchaz.enigma.mapping.Type; @@ -101,26 +100,30 @@ public class ClassTranslator { } // translate the type - Type translatedType = m_translator.translateType(new Type(field.getFieldInfo().getDescriptor())); + Type translatedType = m_translator.translateType(entry.getType()); field.getFieldInfo().setDescriptor(translatedType.toString()); } // translate all the methods and constructors for (CtBehavior behavior : c.getDeclaredBehaviors()) { + + BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); + if (behavior instanceof CtMethod) { CtMethod method = (CtMethod)behavior; // translate the name - MethodEntry entry = EntryFactory.getMethodEntry(method); String translatedName = m_translator.translate(entry); if (translatedName != null) { method.setName(translatedName); } } - // translate the type - Signature translatedSignature = m_translator.translateSignature(new Signature(behavior.getMethodInfo().getDescriptor())); - behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); + if (entry.getSignature() != null) { + // translate the type + Signature translatedSignature = m_translator.translateSignature(entry.getSignature()); + behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); + } } // translate all the class names referenced in the code @@ -137,7 +140,7 @@ public class ClassTranslator { // translate the source file attribute too ClassEntry deobfClassEntry = map.get(classEntry); if (deobfClassEntry != null) { - String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOuterClassName()) + ".java"; + String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassName()) + ".java"; c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); } } diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index dd21a780..976028d2 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -93,7 +93,7 @@ public class InnerClassWriter { // get the new inner class name ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); - ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); + ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOutermostClassEntry(); // here's what the JVM spec says about the InnerClasses attribute // append(inner, parent, 0 if anonymous else simple name, flags); @@ -105,7 +105,7 @@ public class InnerClassWriter { int innerClassNameIndex = 0; int accessFlags = 0; if (!m_index.isAnonymousClass(obfClassEntry)) { - innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnerClassName()); + innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName()); } attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags); diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index 35667b05..d76cd63e 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -180,7 +180,7 @@ public class ClassIdentity { } } - m_outer = EntryFactory.getClassEntry(c).getOuterClassName(); + m_outer = EntryFactory.getClassEntry(c).getOutermostClassName(); } private void addReference(EntryReference reference) { diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index ddd3a53c..2afa1208 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -169,7 +169,7 @@ public class MappingsConverter { newMappings.addClassMapping(destMapping); } } else { - destMapping = destMapping.getInnerClassByObf(destChainClassEntry.getInnerClassName()); + destMapping = destMapping.getInnerClassByObfSimple(destChainClassEntry.getInnermostClassName()); if (destMapping == null) { destMapping = new ClassMapping(destChainClassEntry.getName()); destMapping.addInnerClassMapping(destMapping); diff --git a/src/cuchaz/enigma/gui/CodeReader.java b/src/cuchaz/enigma/gui/CodeReader.java index 743ef2e4..fb8e0825 100644 --- a/src/cuchaz/enigma/gui/CodeReader.java +++ b/src/cuchaz/enigma/gui/CodeReader.java @@ -106,7 +106,7 @@ public class CodeReader extends JEditorPane { // get the outermost class ClassEntry outermostClassEntry = classEntry; while (outermostClassEntry.isInnerClass()) { - outermostClassEntry = outermostClassEntry.getOuterClassEntry(); + outermostClassEntry = outermostClassEntry.getOutermostClassEntry(); } // decompile it diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 9fa633eb..552ee47c 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -257,7 +257,7 @@ public class GuiController { // get the reference target class EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); - ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOuterClassEntry(); + ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOutermostClassEntry(); if (!m_deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); } diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 69e66bc0..5f3b5e23 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -13,6 +13,8 @@ package cuchaz.enigma.mapping; import java.io.Serializable; import java.util.List; +import com.beust.jcommander.internal.Lists; + public class ClassEntry implements Entry, Serializable { private static final long serialVersionUID = 4235460580973955811L; @@ -29,7 +31,7 @@ public class ClassEntry implements Entry, Serializable { m_name = className; - if (isInnerClass() && getInnerClassName().indexOf('/') >= 0) { + if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) { throw new IllegalArgumentException("Inner class must not have a package: " + className); } } @@ -84,22 +86,39 @@ public class ClassEntry implements Entry, Serializable { return m_name.lastIndexOf('$') >= 0; } - public String getOuterClassName() { + public List getClassChainNames() { + return Lists.newArrayList(m_name.split("\\$")); + } + + public List getClassChain() { + List entries = Lists.newArrayList(); + StringBuilder buf = new StringBuilder(); + for (String name : getClassChainNames()) { + if (buf.length() > 0) { + buf.append("$"); + } + buf.append(name); + entries.add(new ClassEntry(buf.toString())); + } + return entries; + } + + public String getOutermostClassName() { if (isInnerClass()) { return m_name.substring(0, m_name.lastIndexOf('$')); } return m_name; } - public String getInnerClassName() { + public String getInnermostClassName() { if (!isInnerClass()) { throw new Error("This is not an inner class!"); } return m_name.substring(m_name.lastIndexOf('$') + 1); } - public ClassEntry getOuterClassEntry() { - return new ClassEntry(getOuterClassName()); + public ClassEntry getOutermostClassEntry() { + return new ClassEntry(getOutermostClassName()); } public boolean isInDefaultPackage() { @@ -130,7 +149,7 @@ public class ClassEntry implements Entry, Serializable { buf.append(chainEntry.getName()); } else { buf.append("$"); - buf.append(chainEntry.isInnerClass() ? chainEntry.getInnerClassName() : chainEntry.getSimpleName()); + buf.append(chainEntry.isInnerClass() ? chainEntry.getInnermostClassName() : chainEntry.getSimpleName()); } if (chainEntry == this) { diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 38cd3d6d..6e7fd17b 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -23,22 +23,23 @@ public class ClassMapping implements Serializable, Comparable { private String m_obfFullName; private String m_obfSimpleName; private String m_deobfName; - private Map m_innerClassesByObf; + private Map m_innerClassesByObfSimple; private Map m_innerClassesByDeobf; private Map m_fieldsByObf; private Map m_fieldsByDeobf; private Map m_methodsByObf; private Map m_methodsByDeobf; - public ClassMapping(String obfName) { - this(obfName, null); + public ClassMapping(String obfFullName) { + this(obfFullName, null); } - public ClassMapping(String obfName, String deobfName) { - m_obfFullName = obfName; - m_obfSimpleName = new ClassEntry(obfName).getSimpleName(); + public ClassMapping(String obfFullName, String deobfName) { + m_obfFullName = obfFullName; + ClassEntry classEntry = new ClassEntry(obfFullName); + m_obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName(); m_deobfName = NameValidator.validateClassName(deobfName, false); - m_innerClassesByObf = Maps.newHashMap(); + m_innerClassesByObfSimple = Maps.newHashMap(); m_innerClassesByDeobf = Maps.newHashMap(); m_fieldsByObf = Maps.newHashMap(); m_fieldsByDeobf = Maps.newHashMap(); @@ -65,12 +66,12 @@ public class ClassMapping implements Serializable, Comparable { //// INNER CLASSES //////// public Iterable innerClasses() { - assert (m_innerClassesByObf.size() >= m_innerClassesByDeobf.size()); - return m_innerClassesByObf.values(); + assert (m_innerClassesByObfSimple.size() >= m_innerClassesByDeobf.size()); + return m_innerClassesByObfSimple.values(); } public void addInnerClassMapping(ClassMapping classMapping) { - boolean obfWasAdded = m_innerClassesByObf.put(classMapping.getObfSimpleName(), classMapping) == null; + boolean obfWasAdded = m_innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null; assert (obfWasAdded); if (classMapping.getDeobfName() != null) { assert (isSimpleClassName(classMapping.getDeobfName())); @@ -80,7 +81,7 @@ public class ClassMapping implements Serializable, Comparable { } public void removeInnerClassMapping(ClassMapping classMapping) { - boolean obfWasRemoved = m_innerClassesByObf.remove(classMapping.getObfSimpleName()) != null; + boolean obfWasRemoved = m_innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null; assert (obfWasRemoved); if (classMapping.getDeobfName() != null) { boolean deobfWasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; @@ -88,20 +89,19 @@ public class ClassMapping implements Serializable, Comparable { } } - public ClassMapping getOrCreateInnerClass(String obfName) { - assert (isSimpleClassName(obfName)); - ClassMapping classMapping = m_innerClassesByObf.get(obfName); + public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) { + ClassMapping classMapping = m_innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName()); if (classMapping == null) { - classMapping = new ClassMapping(obfName); - boolean wasAdded = m_innerClassesByObf.put(obfName, classMapping) == null; + classMapping = new ClassMapping(obfInnerClass.getName()); + boolean wasAdded = m_innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null; assert (wasAdded); } return classMapping; } - public ClassMapping getInnerClassByObf(String obfName) { - assert (isSimpleClassName(obfName)); - return m_innerClassesByObf.get(obfName); + public ClassMapping getInnerClassByObfSimple(String obfSimpleName) { + assert (isSimpleClassName(obfSimpleName)); + return m_innerClassesByObfSimple.get(obfSimpleName); } public ClassMapping getInnerClassByDeobf(String deobfName) { @@ -109,35 +109,25 @@ public class ClassMapping implements Serializable, Comparable { return m_innerClassesByDeobf.get(deobfName); } - public ClassMapping getInnerClassByDeobfThenObf(String name) { + public ClassMapping getInnerClassByDeobfThenObfSimple(String name) { ClassMapping classMapping = getInnerClassByDeobf(name); if (classMapping == null) { - classMapping = getInnerClassByObf(name); + classMapping = getInnerClassByObfSimple(name); } return classMapping; } - public String getObfInnerClassSimpleName(String deobfName) { - assert (isSimpleClassName(deobfName)); - ClassMapping classMapping = m_innerClassesByDeobf.get(deobfName); - if (classMapping != null) { - return classMapping.getObfSimpleName(); - } - return null; - } - - public String getDeobfInnerClassName(String obfName) { - assert (isSimpleClassName(obfName)); - ClassMapping classMapping = m_innerClassesByObf.get(obfName); + public String getDeobfInnerClassName(String obfSimpleName) { + assert (isSimpleClassName(obfSimpleName)); + ClassMapping classMapping = m_innerClassesByObfSimple.get(obfSimpleName); if (classMapping != null) { return classMapping.getDeobfName(); } return null; } - public void setInnerClassName(String obfName, String deobfName) { - assert (isSimpleClassName(obfName)); - ClassMapping classMapping = getOrCreateInnerClass(obfName); + public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) { + ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass); if (classMapping.getDeobfName() != null) { boolean wasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; assert (wasRemoved); @@ -150,6 +140,15 @@ public class ClassMapping implements Serializable, Comparable { } } + public boolean hasInnerClassByObfSimple(String obfSimpleName) { + return m_innerClassesByObfSimple.containsKey(obfSimpleName); + } + + public boolean hasInnerClassByDeobf(String deobfName) { + return m_innerClassesByDeobf.containsKey(deobfName); + } + + //// FIELDS //////// public Iterable fields() { @@ -382,7 +381,7 @@ public class ClassMapping implements Serializable, Comparable { buf.append("\n"); } buf.append("Inner Classes:\n"); - for (ClassMapping classMapping : m_innerClassesByObf.values()) { + for (ClassMapping classMapping : m_innerClassesByObfSimple.values()) { buf.append("\t"); buf.append(classMapping.getObfSimpleName()); buf.append(" <-> "); @@ -404,11 +403,11 @@ public class ClassMapping implements Serializable, Comparable { public boolean renameObfClass(String oldObfClassName, String newObfClassName) { // rename inner classes - for (ClassMapping innerClassMapping : new ArrayList(m_innerClassesByObf.values())) { + for (ClassMapping innerClassMapping : new ArrayList(m_innerClassesByObfSimple.values())) { if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) { - boolean wasRemoved = m_innerClassesByObf.remove(oldObfClassName) != null; + boolean wasRemoved = m_innerClassesByObfSimple.remove(oldObfClassName) != null; assert (wasRemoved); - boolean wasAdded = m_innerClassesByObf.put(newObfClassName, innerClassMapping) == null; + boolean wasAdded = m_innerClassesByObfSimple.put(newObfClassName, innerClassMapping) == null; assert (wasAdded); } } diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index a85bcbf6..659d23ac 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -13,9 +13,11 @@ package cuchaz.enigma.mapping; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Set; +import com.beust.jcommander.internal.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -89,6 +91,18 @@ public class Mappings implements Serializable { return m_classesByDeobf.get(deobfName); } + public void setClassDeobfName(ClassMapping classMapping, String deobfName) { + if (classMapping.getDeobfName() != null) { + boolean wasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (wasRemoved); + } + classMapping.setDeobfName(deobfName); + if (deobfName != null) { + boolean wasAdded = m_classesByDeobf.put(deobfName, classMapping) == null; + assert (wasAdded); + } + } + public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { switch (direction) { case Deobfuscating: @@ -185,4 +199,18 @@ public class Mappings implements Serializable { } return false; } + + public List getClassMappingChain(ClassEntry obfClass) { + List mappingChain = Lists.newArrayList(); + ClassMapping classMapping = null; + for (ClassEntry obfClassEntry : obfClass.getClassChain()) { + if (mappingChain.isEmpty()) { + classMapping = m_classesByObf.get(obfClassEntry.getName()); + } else if (classMapping != null) { + classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName()); + } + mappingChain.add(classMapping); + } + return mappingChain; + } } diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index 16f700d2..d7766dc8 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -13,10 +13,10 @@ package cuchaz.enigma.mapping; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; +import java.util.List; import java.util.Set; import java.util.zip.GZIPOutputStream; -import cuchaz.enigma.Constants; import cuchaz.enigma.analysis.JarIndex; public class MappingsRenamer { @@ -30,48 +30,43 @@ public class MappingsRenamer { } public void setClassName(ClassEntry obf, String deobfName) { - deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass()); - ClassEntry targetEntry = new ClassEntry(deobfName); - if (m_mappings.containsDeobfClass(deobfName) || m_index.containsObfClass(targetEntry)) { - throw new IllegalNameException(deobfName, "There is already a class with that name"); - } - ClassMapping classMapping = getOrCreateClassMapping(obf); + deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass()); - if (obf.isInnerClass()) { - classMapping.setInnerClassName(obf.getInnerClassName(), deobfName); + List mappingChain = getOrCreateClassMappingChain(obf); + if (mappingChain.size() == 1) { + + if (deobfName != null) { + // make sure we don't rename to an existing obf or deobf class + if (m_mappings.containsDeobfClass(deobfName) || m_index.containsObfClass(new ClassEntry(deobfName))) { + throw new IllegalNameException(deobfName, "There is already a class with that name"); + } + } + + ClassMapping classMapping = mappingChain.get(0); + m_mappings.setClassDeobfName(classMapping, deobfName); + } else { - if (classMapping.getDeobfName() != null) { - boolean wasRemoved = m_mappings.m_classesByDeobf.remove(classMapping.getDeobfName()) != null; - assert (wasRemoved); + + ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); + + if (deobfName != null) { + // make sure we don't rename to an existing obf or deobf inner class + if (outerClassMapping.hasInnerClassByDeobf(deobfName) || outerClassMapping.hasInnerClassByObfSimple(deobfName)) { + throw new IllegalNameException(deobfName, "There is already a class with that name"); + } } - classMapping.setDeobfName(deobfName); - boolean wasAdded = m_mappings.m_classesByDeobf.put(deobfName, classMapping) == null; - assert (wasAdded); + + outerClassMapping.setInnerClassName(obf, deobfName); } } public void removeClassMapping(ClassEntry obf) { - ClassMapping classMapping = getClassMapping(obf); - if (obf.isInnerClass()) { - classMapping.setInnerClassName(obf.getName(), null); - } else { - boolean wasRemoved = m_mappings.m_classesByDeobf.remove(classMapping.getDeobfName()) != null; - assert (wasRemoved); - classMapping.setDeobfName(null); - } + setClassName(obf, null); } public void markClassAsDeobfuscated(ClassEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf); - if (obf.isInnerClass()) { - String innerClassName = Constants.NonePackage + "/" + obf.getInnerClassName(); - classMapping.setInnerClassName(innerClassName, innerClassName); - } else { - classMapping.setDeobfName(obf.getName()); - boolean wasAdded = m_mappings.m_classesByDeobf.put(obf.getName(), classMapping) == null; - assert (wasAdded); - } + setClassName(obf, obf.isInnerClass() ? obf.getInnermostClassName() : obf.getSimpleName()); } public void setFieldName(FieldEntry obf, String deobfName) { @@ -81,17 +76,17 @@ public class MappingsRenamer { throw new IllegalNameException(deobfName, "There is already a field with that name"); } - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); } public void removeFieldMapping(FieldEntry obf) { - ClassMapping classMapping = getClassMappingOrInnerClassMapping(obf.getClassEntry()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); } public void markFieldAsDeobfuscated(FieldEntry obf) { - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); } @@ -121,7 +116,7 @@ public class MappingsRenamer { throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); } - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); } @@ -132,7 +127,7 @@ public class MappingsRenamer { } public void removeMethodMapping(MethodEntry obf) { - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); classMapping.setMethodName(obf.getName(), obf.getSignature(), null); } @@ -143,7 +138,7 @@ public class MappingsRenamer { } public void markMethodAsDeobfuscated(MethodEntry obf) { - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); } @@ -154,17 +149,17 @@ public class MappingsRenamer { throw new IllegalNameException(deobfName, "There is already an argument with that name"); } - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); } public void removeArgumentMapping(ArgumentEntry obf) { - ClassMapping classMapping = getClassMappingOrInnerClassMapping(obf.getClassEntry()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); } public void markArgumentAsDeobfuscated(ArgumentEntry obf) { - ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); } @@ -204,34 +199,31 @@ public class MappingsRenamer { gzipout.finish(); } - private ClassMapping getClassMapping(ClassEntry obfClassEntry) { - return m_mappings.m_classesByObf.get(obfClassEntry.getOuterClassName()); - } - private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { - String obfClassName = obfClassEntry.getOuterClassName(); - ClassMapping classMapping = m_mappings.m_classesByObf.get(obfClassName); - if (classMapping == null) { - classMapping = new ClassMapping(obfClassName); - boolean obfWasAdded = m_mappings.m_classesByObf.put(classMapping.getObfFullName(), classMapping) == null; - assert (obfWasAdded); - } - return classMapping; - } - - private ClassMapping getClassMappingOrInnerClassMapping(ClassEntry obfClassEntry) { - ClassMapping classMapping = getClassMapping(obfClassEntry); - if (obfClassEntry.isInDefaultPackage()) { - classMapping = classMapping.getInnerClassByObf(obfClassEntry.getInnerClassName()); - } - return classMapping; - } - - private ClassMapping getOrCreateClassMappingOrInnerClassMapping(ClassEntry obfClassEntry) { - ClassMapping classMapping = getOrCreateClassMapping(obfClassEntry); - if (obfClassEntry.isInnerClass()) { - classMapping = classMapping.getOrCreateInnerClass(obfClassEntry.getInnerClassName()); + List mappingChain = getOrCreateClassMappingChain(obfClassEntry); + return mappingChain.get(mappingChain.size() - 1); + } + + private List getOrCreateClassMappingChain(ClassEntry obfClassEntry) { + List classChain = obfClassEntry.getClassChain(); + List mappingChain = m_mappings.getClassMappingChain(obfClassEntry); + for (int i=0; i m_classes; private TranslationIndex m_index; + private ClassNameReplacer m_classNameReplacer = new ClassNameReplacer() { + @Override + public String replace(String className) { + return translateEntry(new ClassEntry(className)).getName(); + } + }; + public Translator() { m_direction = null; m_classes = Maps.newHashMap(); @@ -69,48 +76,16 @@ public class Translator { } } - public String translateClass(String className) { - return translate(new ClassEntry(className)); - } - public String translate(ClassEntry in) { - - if (in.isInnerClass()) { - - // translate everything in the class chain, or return null - List mappingsChain = getClassMappingChain(in); - StringBuilder buf = new StringBuilder(); - for (ClassMapping classMapping : mappingsChain) { - if (classMapping == null) { - return null; - } - boolean isFirstClass = buf.length() == 0; - String name = m_direction.choose( - classMapping.getDeobfName(), - isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() - ); - if (name == null) { - return null; - } - if (!isFirstClass) { - buf.append("$"); - } - buf.append(name); - } - return buf.toString(); - - } else { - - // normal classes are easier - ClassMapping classMapping = m_classes.get(in.getName()); - if (classMapping == null) { - return null; - } - return m_direction.choose( - classMapping.getDeobfName(), - classMapping.getObfFullName() - ); + ClassEntry translated = translateEntry(in); + if (translated.equals(in)) { + return null; } + return translated.getName(); + } + + public String translateClass(String className) { + return translate(new ClassEntry(className)); } public ClassEntry translateEntry(ClassEntry in) { @@ -264,21 +239,11 @@ public class Translator { } public Type translateType(Type type) { - return new Type(type, new ClassNameReplacer() { - @Override - public String replace(String className) { - return translateClass(className); - } - }); + return new Type(type, m_classNameReplacer); } public Signature translateSignature(Signature signature) { - return new Signature(signature, new ClassNameReplacer() { - @Override - public String replace(String className) { - return translateClass(className); - } - }); + return new Signature(signature, m_classNameReplacer); } private ClassMapping findClassMapping(ClassEntry in) { @@ -302,8 +267,8 @@ public class Translator { ClassMapping innerClassMapping = null; if (outerClassMapping != null) { innerClassMapping = m_direction.choose( - outerClassMapping.getInnerClassByObf(parts[i]), - outerClassMapping.getInnerClassByDeobfThenObf(parts[i]) + outerClassMapping.getInnerClassByObfSimple(parts[i]), + outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i]) ); } mappingsChain.add(innerClassMapping); diff --git a/test/cuchaz/enigma/TestDeobfed.java b/test/cuchaz/enigma/TestDeobfed.java index 3c2ae51d..ca349d7c 100644 --- a/test/cuchaz/enigma/TestDeobfed.java +++ b/test/cuchaz/enigma/TestDeobfed.java @@ -34,21 +34,24 @@ public class TestDeobfed { newClass("none/b"), newClass("none/c"), newClass("none/d"), - newClass("none/d$e"), + newClass("none/d$1"), + newClass("none/e"), newClass("none/f"), newClass("none/g"), + newClass("none/g$a"), + newClass("none/g$a$a"), + newClass("none/g$b"), + newClass("none/g$b$a"), newClass("none/h"), - newClass("none/h$i"), - newClass("none/h$i$j"), - newClass("none/h$k"), - newClass("none/h$k$l"), - newClass("none/m"), - newClass("none/m$n"), - newClass("none/m$n$o"), - newClass("none/m$p"), - newClass("none/m$p$q"), - newClass("none/m$p$q$r"), - newClass("none/m$p$q$s") + newClass("none/h$a"), + newClass("none/h$a$a"), + newClass("none/h$b"), + newClass("none/h$b$a"), + newClass("none/h$b$a$a"), + newClass("none/h$b$a$b"), + newClass("none/i"), + newClass("none/i$a"), + newClass("none/i$b") )); } @@ -60,20 +63,23 @@ public class TestDeobfed { deobfuscator.getSourceTree("none/b"); deobfuscator.getSourceTree("none/c"); deobfuscator.getSourceTree("none/d"); - deobfuscator.getSourceTree("none/d$e"); + deobfuscator.getSourceTree("none/d$1"); + deobfuscator.getSourceTree("none/e"); deobfuscator.getSourceTree("none/f"); deobfuscator.getSourceTree("none/g"); + deobfuscator.getSourceTree("none/g$a"); + deobfuscator.getSourceTree("none/g$a$a"); + deobfuscator.getSourceTree("none/g$b"); + deobfuscator.getSourceTree("none/g$b$a"); deobfuscator.getSourceTree("none/h"); - deobfuscator.getSourceTree("none/h$i"); - deobfuscator.getSourceTree("none/h$i$j"); - deobfuscator.getSourceTree("none/h$k"); - deobfuscator.getSourceTree("none/h$k$l"); - deobfuscator.getSourceTree("none/m"); - deobfuscator.getSourceTree("none/m$n"); - deobfuscator.getSourceTree("none/m$n$o"); - deobfuscator.getSourceTree("none/m$p"); - deobfuscator.getSourceTree("none/m$p$q"); - deobfuscator.getSourceTree("none/m$p$q$r"); - deobfuscator.getSourceTree("none/m$p$q$s"); + deobfuscator.getSourceTree("none/h$a"); + deobfuscator.getSourceTree("none/h$a$a"); + deobfuscator.getSourceTree("none/h$b"); + deobfuscator.getSourceTree("none/h$b$a"); + deobfuscator.getSourceTree("none/h$b$a$a"); + deobfuscator.getSourceTree("none/h$b$a$b"); + deobfuscator.getSourceTree("none/i"); + deobfuscator.getSourceTree("none/i$a"); + deobfuscator.getSourceTree("none/i$b"); } } diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java index 2eb5accc..014a4613 100644 --- a/test/cuchaz/enigma/TestInnerClasses.java +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -29,19 +29,19 @@ public class TestInnerClasses { private Deobfuscator m_deobfuscator; private static final ClassEntry AnonymousOuter = newClass("none/a"); - private static final ClassEntry AnonymousInner = newClass("none/b"); - private static final ClassEntry SimpleOuter = newClass("none/g"); - private static final ClassEntry SimpleInner = newClass("none/h"); - private static final ClassEntry ConstructorArgsOuter = newClass("none/e"); - private static final ClassEntry ConstructorArgsInner = newClass("none/f"); - private static final ClassEntry AnonymousWithScopeArgsOuter = newClass("none/c"); - private static final ClassEntry AnonymousWithScopeArgsInner = newClass("none/d"); - private static final ClassEntry AnonymousWithOuterAccessOuter = newClass("none/i"); - private static final ClassEntry AnonymousWithOuterAccessInner = newClass("none/j"); - private static final ClassEntry ClassTreeRoot = newClass("none/k"); - private static final ClassEntry ClassTreeLevel1 = newClass("none/l"); - private static final ClassEntry ClassTreeLevel2 = newClass("none/m"); - private static final ClassEntry ClassTreeLevel3 = newClass("none/n"); + private static final ClassEntry AnonymousInner = newClass("none/a$1"); + private static final ClassEntry SimpleOuter = newClass("none/d"); + private static final ClassEntry SimpleInner = newClass("none/d$a"); + private static final ClassEntry ConstructorArgsOuter = newClass("none/c"); + private static final ClassEntry ConstructorArgsInner = newClass("none/c$a"); + private static final ClassEntry AnonymousWithScopeArgsOuter = newClass("none/b"); + private static final ClassEntry AnonymousWithScopeArgsInner = newClass("none/b$1"); + private static final ClassEntry AnonymousWithOuterAccessOuter = newClass("none/e"); + private static final ClassEntry AnonymousWithOuterAccessInner = newClass("none/e$1"); + private static final ClassEntry ClassTreeRoot = newClass("none/f"); + private static final ClassEntry ClassTreeLevel1 = newClass("none/f$a"); + private static final ClassEntry ClassTreeLevel2 = newClass("none/f$a$a"); + private static final ClassEntry ClassTreeLevel3 = newClass("none/f$a$a$a"); public TestInnerClasses() throws Exception { @@ -101,7 +101,7 @@ public class TestInnerClasses { // level 1 ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName() - + "$" + ClassTreeLevel1.getSimpleName() + + "$" + ClassTreeLevel1.getInnermostClassName() ); assertThat(m_index.containsObfClass(fullClassEntry), is(true)); assertThat(m_index.getOuterClass(ClassTreeLevel1), is(ClassTreeRoot)); @@ -109,8 +109,8 @@ public class TestInnerClasses { // level 2 fullClassEntry = new ClassEntry(ClassTreeRoot.getName() - + "$" + ClassTreeLevel1.getSimpleName() - + "$" + ClassTreeLevel2.getSimpleName() + + "$" + ClassTreeLevel1.getInnermostClassName() + + "$" + ClassTreeLevel2.getInnermostClassName() ); assertThat(m_index.containsObfClass(fullClassEntry), is(true)); assertThat(m_index.getOuterClass(ClassTreeLevel2), is(ClassTreeLevel1)); @@ -118,9 +118,9 @@ public class TestInnerClasses { // level 3 fullClassEntry = new ClassEntry(ClassTreeRoot.getName() - + "$" + ClassTreeLevel1.getSimpleName() - + "$" + ClassTreeLevel2.getSimpleName() - + "$" + ClassTreeLevel3.getSimpleName() + + "$" + ClassTreeLevel1.getInnermostClassName() + + "$" + ClassTreeLevel2.getInnermostClassName() + + "$" + ClassTreeLevel3.getInnermostClassName() ); assertThat(m_index.containsObfClass(fullClassEntry), is(true)); assertThat(m_index.getOuterClass(ClassTreeLevel3), is(ClassTreeLevel2)); diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java index 94bf941d..13971249 100644 --- a/test/cuchaz/enigma/TestSourceIndex.java +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -24,7 +24,7 @@ import cuchaz.enigma.mapping.ClassEntry; public class TestSourceIndex { - @Test + // TEMP @Test public void indexEverything() throws Exception { diff --git a/test/cuchaz/enigma/TestTranslator.java b/test/cuchaz/enigma/TestTranslator.java index 02526050..45c69bb2 100644 --- a/test/cuchaz/enigma/TestTranslator.java +++ b/test/cuchaz/enigma/TestTranslator.java @@ -101,26 +101,31 @@ public class TestTranslator { public void innerClasses() { // classes - assertMapping(newClass("none/h"), newClass("deobf/H_OuterClass")); - assertMapping(newClass("none/h$i"), newClass("deobf/H_OuterClass$I_InnerClass")); - assertMapping(newClass("none/h$i$j"), newClass("deobf/H_OuterClass$I_InnerClass$J_InnerInnerClass")); - assertMapping(newClass("none/h$k"), newClass("deobf/H_OuterClass$k")); - assertMapping(newClass("none/h$k$l"), newClass("deobf/H_OuterClass$k$L_NamedInnerClass")); + assertMapping(newClass("none/g"), newClass("deobf/G_OuterClass")); + assertMapping(newClass("none/g$a"), newClass("deobf/G_OuterClass$A_InnerClass")); + assertMapping(newClass("none/g$a$a"), newClass("deobf/G_OuterClass$A_InnerClass$A_InnerInnerClass")); + assertMapping(newClass("none/g$b"), newClass("deobf/G_OuterClass$b")); + assertMapping(newClass("none/g$b$a"), newClass("deobf/G_OuterClass$b$A_NamedInnerClass")); // fields - assertMapping(newField("none/h$i", "a", "I"), newField("deobf/H_OuterClass$I_InnerClass", "f1", "I")); - assertMapping(newField("none/h$i", "a", "Ljava/lang/String;"), newField("deobf/H_OuterClass$I_InnerClass", "f2", "Ljava/lang/String;")); - assertMapping(newField("none/h$i$j", "a", "I"), newField("deobf/H_OuterClass$I_InnerClass$J_InnerInnerClass", "f3", "I")); - assertMapping(newField("none/h$k$l", "a", "I"), newField("deobf/H_OuterClass$k$L_NamedInnerClass", "f4", "I")); + assertMapping(newField("none/g$a", "a", "I"), newField("deobf/G_OuterClass$A_InnerClass", "f1", "I")); + assertMapping(newField("none/g$a", "a", "Ljava/lang/String;"), newField("deobf/G_OuterClass$A_InnerClass", "f2", "Ljava/lang/String;")); + assertMapping(newField("none/g$a$a", "a", "I"), newField("deobf/G_OuterClass$A_InnerClass$A_InnerInnerClass", "f3", "I")); + assertMapping(newField("none/g$b$a", "a", "I"), newField("deobf/G_OuterClass$b$A_NamedInnerClass", "f4", "I")); // methods - assertMapping(newMethod("none/h$i", "a", "()V"), newMethod("deobf/H_OuterClass$I_InnerClass", "m1", "()V")); - assertMapping(newMethod("none/h$i$j", "a", "()V"), newMethod("deobf/H_OuterClass$I_InnerClass$J_InnerInnerClass", "m2", "()V")); + assertMapping(newMethod("none/g$a", "a", "()V"), newMethod("deobf/G_OuterClass$A_InnerClass", "m1", "()V")); + assertMapping(newMethod("none/g$a$a", "a", "()V"), newMethod("deobf/G_OuterClass$A_InnerClass$A_InnerInnerClass", "m2", "()V")); } @Test public void namelessClass() { - assertMapping(newClass("none/m"), newClass("none/m")); + assertMapping(newClass("none/h"), newClass("none/h")); + } + + @Test + public void testGenerics() { + // TODO } private void assertMapping(Entry obf, Entry deobf) { diff --git a/test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java b/test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java new file mode 100644 index 00000000..4e091797 --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java @@ -0,0 +1,19 @@ +package cuchaz.enigma.inputs.translation; + +public class F_ObjectMethods { + + public void callEmAll() + throws Throwable { + clone(); + equals(this); + finalize(); + getClass(); + hashCode(); + notify(); + notifyAll(); + toString(); + wait(); + wait(0); + wait(0, 0); + } +} diff --git a/test/cuchaz/enigma/inputs/translation/G_ObjectMethods.java b/test/cuchaz/enigma/inputs/translation/G_ObjectMethods.java deleted file mode 100644 index ebad5a38..00000000 --- a/test/cuchaz/enigma/inputs/translation/G_ObjectMethods.java +++ /dev/null @@ -1,19 +0,0 @@ -package cuchaz.enigma.inputs.translation; - -public class G_ObjectMethods { - - public void callEmAll() - throws Throwable { - clone(); - equals(this); - finalize(); - getClass(); - hashCode(); - notify(); - notifyAll(); - toString(); - wait(); - wait(0); - wait(0, 0); - } -} diff --git a/test/cuchaz/enigma/inputs/translation/G_OuterClass.java b/test/cuchaz/enigma/inputs/translation/G_OuterClass.java new file mode 100644 index 00000000..0856afed --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/G_OuterClass.java @@ -0,0 +1,26 @@ +package cuchaz.enigma.inputs.translation; + + +public class G_OuterClass { + + public class A_InnerClass { + + public int f1; + public String f2; + + public void m1() {} + + public class A_InnerInnerClass { + + public int f3; + + public void m2() {} + } + } + + public class B_NamelessClass { + public class A_NamedInnerClass { + public int f4; + } + } +} diff --git a/test/cuchaz/enigma/inputs/translation/H_NamelessClass.java b/test/cuchaz/enigma/inputs/translation/H_NamelessClass.java new file mode 100644 index 00000000..5802d789 --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/H_NamelessClass.java @@ -0,0 +1,28 @@ +package cuchaz.enigma.inputs.translation; + + +public class H_NamelessClass { + + public class A_InnerClass { + + public int f1; + public String f2; + + public void m1() {} + + public class A_InnerInnerClass { + + public int f3; + + public void m2() {} + } + } + + public class B_NamelessClass { + public class A_NamedInnerClass { + public int f4; + public class A_AnotherInnerClass {} + public class B_YetAnotherInnerClass {} + } + } +} diff --git a/test/cuchaz/enigma/inputs/translation/H_OuterClass.java b/test/cuchaz/enigma/inputs/translation/H_OuterClass.java deleted file mode 100644 index 995c65d2..00000000 --- a/test/cuchaz/enigma/inputs/translation/H_OuterClass.java +++ /dev/null @@ -1,26 +0,0 @@ -package cuchaz.enigma.inputs.translation; - - -public class H_OuterClass { - - public class I_InnerClass { - - public int f1; - public String f2; - - public void m1() {} - - public class J_InnerInnerClass { - - public int f3; - - public void m2() {} - } - } - - public class K_NamelessClass { - public class L_NamedInnerClass { - public int f4; - } - } -} diff --git a/test/cuchaz/enigma/inputs/translation/I_Generics.java b/test/cuchaz/enigma/inputs/translation/I_Generics.java new file mode 100644 index 00000000..191931a8 --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/I_Generics.java @@ -0,0 +1,25 @@ +package cuchaz.enigma.inputs.translation; + +import java.util.List; +import java.util.Map; + + +public class I_Generics { + + public class A_Type { + } + + public List f1; + public List f2; + public Map f3; + + public class B_Generic { + public T f4; + public T m1() { + return null; + } + } + + public B_Generic f5; + public B_Generic f6; +} diff --git a/test/cuchaz/enigma/inputs/translation/M_NamelessClass.java b/test/cuchaz/enigma/inputs/translation/M_NamelessClass.java deleted file mode 100644 index 5d8acbc7..00000000 --- a/test/cuchaz/enigma/inputs/translation/M_NamelessClass.java +++ /dev/null @@ -1,28 +0,0 @@ -package cuchaz.enigma.inputs.translation; - - -public class M_NamelessClass { - - public class N_InnerClass { - - public int f1; - public String f2; - - public void m1() {} - - public class O_InnerInnerClass { - - public int f3; - - public void m2() {} - } - } - - public class P_NamelessClass { - public class Q_NamedInnerClass { - public int f4; - public class R_AnotherInnerClass {} - public class S_YetAnotherInnerClass {} - } - } -} diff --git a/test/cuchaz/enigma/resources/translation.mappings b/test/cuchaz/enigma/resources/translation.mappings index 5dffaf1d..55bd7e5c 100644 --- a/test/cuchaz/enigma/resources/translation.mappings +++ b/test/cuchaz/enigma/resources/translation.mappings @@ -17,15 +17,16 @@ CLASS none/c deobf/C_SubClass FIELD c f4 I METHOD a m1 ()I METHOD c m3 ()I -CLASS none/h deobf/H_OuterClass - CLASS none/i I_InnerClass - CLASS none/j J_InnerInnerClass +CLASS none/g deobf/G_OuterClass + CLASS none/g$a A_InnerClass + CLASS none/g$a$a A_InnerInnerClass FIELD a f3 I METHOD a m2 ()V FIELD a f1 I FIELD a f2 Ljava/lang/String; METHOD a m1 ()V - CLASS none/k - CLASS none/l L_NamedInnerClass + CLASS none/g$b + CLASS none/g$b$a A_NamedInnerClass FIELD a f4 I -CLASS none/m \ No newline at end of file +CLASS none/h +CLASS none/i I_Generics -- cgit v1.2.3 From 563c5e08e3d61bfd39402a94e78bbaaf75623b04 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 16 Mar 2015 12:52:09 -0400 Subject: fix more inner class issues --- src/cuchaz/enigma/analysis/JarIndex.java | 2 +- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 2 +- src/cuchaz/enigma/convert/ClassIdentity.java | 2 +- src/cuchaz/enigma/gui/CodeReader.java | 8 +------- src/cuchaz/enigma/mapping/ClassEntry.java | 21 ++++++++++++++++----- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index a4a3abb8..7ebbd974 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -312,7 +312,7 @@ public class JarIndex { // does this class already have an outer class? if (classEntry.isInnerClass()) { - return classEntry.getOutermostClassEntry(); + return classEntry.getOuterClassEntry(); } InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); if (innerClassesAttribute != null) { diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 976028d2..bb643158 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -93,7 +93,7 @@ public class InnerClassWriter { // get the new inner class name ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); - ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOutermostClassEntry(); + ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); // here's what the JVM spec says about the InnerClasses attribute // append(inner, parent, 0 if anonymous else simple name, flags); diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index d76cd63e..35667b05 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -180,7 +180,7 @@ public class ClassIdentity { } } - m_outer = EntryFactory.getClassEntry(c).getOutermostClassName(); + m_outer = EntryFactory.getClassEntry(c).getOuterClassName(); } private void addReference(EntryReference reference) { diff --git a/src/cuchaz/enigma/gui/CodeReader.java b/src/cuchaz/enigma/gui/CodeReader.java index fb8e0825..d8fb394b 100644 --- a/src/cuchaz/enigma/gui/CodeReader.java +++ b/src/cuchaz/enigma/gui/CodeReader.java @@ -103,14 +103,8 @@ public class CodeReader extends JEditorPane { @Override public void run() { - // get the outermost class - ClassEntry outermostClassEntry = classEntry; - while (outermostClassEntry.isInnerClass()) { - outermostClassEntry = outermostClassEntry.getOutermostClassEntry(); - } - // decompile it - CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName()); + CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClassName()); String source = deobfuscator.getSource(sourceTree); setCode(source); m_sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens); diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 5f3b5e23..e6400b8c 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -105,20 +105,31 @@ public class ClassEntry implements Entry, Serializable { public String getOutermostClassName() { if (isInnerClass()) { - return m_name.substring(0, m_name.lastIndexOf('$')); + return m_name.substring(0, m_name.indexOf('$')); } return m_name; } - public String getInnermostClassName() { + public ClassEntry getOutermostClassEntry() { + return new ClassEntry(getOutermostClassName()); + } + + public String getOuterClassName() { if (!isInnerClass()) { throw new Error("This is not an inner class!"); } - return m_name.substring(m_name.lastIndexOf('$') + 1); + return m_name.substring(0, m_name.lastIndexOf('$')); } - public ClassEntry getOutermostClassEntry() { - return new ClassEntry(getOutermostClassName()); + public ClassEntry getOuterClassEntry() { + return new ClassEntry(getOuterClassName()); + } + + public String getInnermostClassName() { + if (!isInnerClass()) { + throw new Error("This is not an inner class!"); + } + return m_name.substring(m_name.lastIndexOf('$') + 1); } public boolean isInDefaultPackage() { -- cgit v1.2.3 From fc5c0cfc933c1a62333caba33e156a443353f5da Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 16 Mar 2015 18:37:19 -0400 Subject: update to new javassist version to (hopefully) get bug fixes --- build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.py b/build.py index 03212496..c3f061a5 100644 --- a/build.py +++ b/build.py @@ -27,7 +27,7 @@ ExtraRepos = [ ] LibDeps = [ ssjb.ivy.Dep("com.google.guava:guava:17.0"), - ssjb.ivy.Dep("org.javassist:javassist:3.18.1-GA"), + ssjb.ivy.Dep("org.javassist:javassist:3.19.0-GA"), ssjb.ivy.Dep("org.bitbucket.mstrobel:procyon-decompiler:0.5.28-enigma") ] StandaloneDeps = LibDeps + [ -- cgit v1.2.3 From 5e3743a0aca3529eacf9be400c8b8d7547f66e7f Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 16 Mar 2015 19:22:22 -0400 Subject: started adding minimal support for generics fixed mark-as-deobfuscated issue --- src/cuchaz/enigma/Deobfuscator.java | 9 +- src/cuchaz/enigma/analysis/JarIndex.java | 31 ++-- .../enigma/analysis/SourceIndexClassVisitor.java | 7 +- src/cuchaz/enigma/bytecode/ClassRenamer.java | 164 ++++++++++++++------- src/cuchaz/enigma/bytecode/ClassTranslator.java | 42 ++---- src/cuchaz/enigma/mapping/EntryFactory.java | 17 ++- src/cuchaz/enigma/mapping/MappingsChecker.java | 2 +- src/cuchaz/enigma/mapping/MappingsRenamer.java | 10 +- src/cuchaz/enigma/mapping/ParameterizedType.java | 54 +++++++ src/cuchaz/enigma/mapping/Signature.java | 10 -- src/cuchaz/enigma/mapping/Type.java | 78 ++++++++-- test/cuchaz/enigma/TestSourceIndex.java | 2 +- test/cuchaz/enigma/TestTranslator.java | 17 ++- test/cuchaz/enigma/TestType.java | 163 +++++++++++++++++++- test/cuchaz/enigma/resources/translation.mappings | 11 +- 15 files changed, 478 insertions(+), 139 deletions(-) create mode 100644 src/cuchaz/enigma/mapping/ParameterizedType.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 5a23ce5b..b63f1639 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -439,12 +439,9 @@ public class Deobfuscator { Translator translator = getTranslator(TranslationDirection.Deobfuscating); if (obfEntry instanceof ClassEntry) { ClassEntry obfClass = (ClassEntry)obfEntry; - ClassEntry translated = translator.translateEntry(obfClass); - if (obfClass.isInnerClass()) { - return !obfClass.getInnermostClassName().equals(translated.getInnermostClassName()); - } else { - return !obfClass.equals(translated); - } + List mappingChain = m_mappings.getClassMappingChain(obfClass); + ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1); + return classMapping != null && classMapping.getDeobfName() != null; } else if (obfEntry instanceof FieldEntry) { return translator.translate((FieldEntry)obfEntry) != null; } else if (obfEntry instanceof MethodEntry) { diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 7ebbd974..e255468a 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -28,6 +28,7 @@ import javassist.CtMethod; import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import javassist.bytecode.Descriptor; +import javassist.bytecode.EnclosingMethodAttribute; import javassist.bytecode.FieldInfo; import javassist.bytecode.InnerClassesAttribute; import javassist.expr.ConstructorCall; @@ -314,15 +315,6 @@ public class JarIndex { if (classEntry.isInnerClass()) { return classEntry.getOuterClassEntry(); } - InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (innerClassesAttribute != null) { - for (int i=0; i 0) { + return EntryFactory.getBehaviorEntry( + Descriptor.toJvmName(enclosingMethodAttribute.className()), + enclosingMethodAttribute.methodName(), + enclosingMethodAttribute.methodDescriptor() + ); + } else { + // an attribute but no method? assume not anonymous + return null; + } + } + + // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous + InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (innerClassesAttribute != null) { + return null; + } + ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); // anonymous classes: diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index f4f49568..f4202b5b 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -30,7 +30,6 @@ import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.Type; public class SourceIndexClassVisitor extends SourceIndexVisitor { @@ -93,8 +92,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getErasedSignature())); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(def); assert (node.getVariables().size() == 1); VariableInitializer variable = node.getVariables().firstOrNullObject(); index.addDeclaration(variable.getNameToken(), fieldEntry); @@ -106,8 +104,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { // treat enum declarations as field declarations FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getErasedSignature())); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(def); index.addDeclaration(node.getNameToken(), fieldEntry); return recurse(node, index); diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index e9cdea3c..8bc084d3 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -23,60 +23,100 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassNameReplacer; +import cuchaz.enigma.mapping.ParameterizedType; +import cuchaz.enigma.mapping.Translator; +import cuchaz.enigma.mapping.Type; public class ClassRenamer { - public static void renameClasses(CtClass c, Map map) { - - // build the map used by javassist - ClassMap nameMap = new ClassMap(); - for (Map.Entry entry : map.entrySet()) { - nameMap.put(entry.getKey().getName(), entry.getValue().getName()); - } - - c.replaceClassName(nameMap); - - // replace simple names in the InnerClasses attribute too - ConstPool constants = c.getClassFile().getConstPool(); - InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (attr != null) { - for (int i = 0; i < attr.tableLength(); i++) { - ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(attr.innerClass(i))); - if (attr.innerNameIndex(i) != 0) { - attr.setInnerNameIndex(i, constants.addUtf8Info(classEntry.getInnermostClassName())); + public static void renameClasses(CtClass c, final Translator translator) { + renameClasses(c, new ClassNameReplacer() { + @Override + public String replace(String className) { + ClassEntry entry = translator.translateEntry(new ClassEntry(className)); + if (entry != null) { + return entry.getName(); } - - /* DEBUG - System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); - */ + return null; } - } + }); } - public static Set getAllClassEntries(final CtClass c) { + public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) { + renameClasses(c, new ClassNameReplacer() { + @Override + public String replace(String className) { + ClassEntry entry = new ClassEntry(className); + if (entry.isInDefaultPackage()) { + return newPackageName + "/" + entry.getName(); + } + return null; + } + }); + } + + public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) { + renameClasses(c, new ClassNameReplacer() { + @Override + public String replace(String className) { + ClassEntry entry = new ClassEntry(className); + if (entry.getPackageName().equals(oldPackageName)) { + return entry.getSimpleName(); + } + return null; + } + }); + } + + public static void renameClasses(CtClass c, ClassNameReplacer replacer) { + Map map = Maps.newHashMap(); + for (ParameterizedType type : ClassRenamer.getAllClassTypes(c)) { + ParameterizedType renamedType = new ParameterizedType(type, replacer); + if (!type.equals(renamedType)) { + map.put(type, renamedType); + } + } + renameTypes(c, map); + } + + public static Set getAllClassTypes(final CtClass c) { - // get the classes that javassist knows about - final Set entries = Sets.newHashSet(); + // TODO: might have to scan SignatureAttributes directly because javassist is buggy + + // get the class types that javassist knows about + final Set types = Sets.newHashSet(); ClassMap map = new ClassMap() { @Override public Object get(Object obj) { if (obj instanceof String) { String str = (String)obj; - // javassist throws a lot of weird things at this map - // I either have to implement my on class scanner, or just try to filter out the weirdness - // I'm opting to filter out the weirdness for now + // sometimes javasist gives us dot-separated classes... whadda hell? + str = str.replace('.', '/'); - // skip anything with generic arguments - if (str.indexOf('<') >= 0 || str.indexOf('>') >= 0 || str.indexOf(';') >= 0) { + // skip weird types + boolean hasNestedParams = str.indexOf('<') >= 0 && str.indexOf('<', str.indexOf('<')+1) >= 0; + boolean hasWeirdChars = str.indexOf('*') >= 0 || str.indexOf('-') >= 0 || str.indexOf('+') >= 0; + if (hasNestedParams || hasWeirdChars) { + // TEMP + System.out.println("Skipped translating: " + str); return null; } - // convert path/to/class.inner to path/to/class$inner - str = str.replace('.', '$'); + ParameterizedType type = new ParameterizedType(new Type("L" + str + ";")); + assert(type.isClass()); + // TEMP + try { + type.getClassEntry(); + } catch (Throwable t) { + // bad type + // TEMP + System.out.println("Skipped translating: " + str); + return null; + } - // remember everything else - entries.add(new ClassEntry(str)); + types.add(type); } return null; } @@ -85,26 +125,46 @@ public class ClassRenamer { }; c.replaceClassName(map); - return entries; + return types; } - - public static void moveAllClassesOutOfDefaultPackage(CtClass c, String newPackageName) { - Map map = Maps.newHashMap(); - for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { - if (classEntry.isInDefaultPackage()) { - map.put(classEntry, new ClassEntry(newPackageName + "/" + classEntry.getName())); - } + + public static void renameTypes(CtClass c, Map map) { + + // convert the type map to a javassist class map + ClassMap nameMap = new ClassMap(); + for (Map.Entry entry : map.entrySet()) { + String source = entry.getKey().toString(); + String dest = entry.getValue().toString(); + + // don't forget to chop off the L ... ; + // javassist doesn't want it there + source = source.substring(1, source.length() - 1); + dest = dest.substring(1, dest.length() - 1); + + nameMap.put(source, dest); } - ClassRenamer.renameClasses(c, map); - } - - public static void moveAllClassesIntoDefaultPackage(CtClass c, String oldPackageName) { - Map map = Maps.newHashMap(); - for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { - if (classEntry.getPackageName().equals(oldPackageName)) { - map.put(classEntry, new ClassEntry(classEntry.getSimpleName())); + + // replace!! + c.replaceClassName(nameMap); + + // replace simple names in the InnerClasses attribute too + ConstPool constants = c.getClassFile().getConstPool(); + InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (attr != null) { + for (int i = 0; i < attr.tableLength(); i++) { + + // get the inner class full name (which has already been translated) + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(attr.innerClass(i))); + + if (attr.innerNameIndex(i) != 0) { + // update the inner name + attr.setInnerNameIndex(i, constants.addUtf8Info(classEntry.getInnermostClassName())); + } + + /* DEBUG + System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); + */ } } - ClassRenamer.renameClasses(c, map); } } diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 94ab2c4b..7952577b 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -10,8 +10,6 @@ ******************************************************************************/ package cuchaz.enigma.bytecode; -import java.util.Map; - import javassist.CtBehavior; import javassist.CtClass; import javassist.CtField; @@ -19,9 +17,6 @@ import javassist.CtMethod; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.SourceFileAttribute; - -import com.google.common.collect.Maps; - import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.EntryFactory; @@ -50,20 +45,15 @@ public class ClassTranslator { case ConstPool.CONST_Fieldref: { - // translate the name - FieldEntry entry = new FieldEntry( - new ClassEntry(Descriptor.toJvmName(constants.getFieldrefClassName(i))), + // translate the name and type + FieldEntry entry = EntryFactory.getFieldEntry( + Descriptor.toJvmName(constants.getFieldrefClassName(i)), constants.getFieldrefName(i), - new Type(constants.getFieldrefType(i)) + constants.getFieldrefType(i) ); FieldEntry translatedEntry = m_translator.translateEntry(entry); - - // translate the type - Type type = new Type(constants.getFieldrefType(i)); - Type translatedType = m_translator.translateType(type); - - if (!entry.equals(translatedEntry) || !type.equals(translatedType)) { - editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedType.toString()); + if (!entry.equals(translatedEntry)) { + editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); } } break; @@ -71,15 +61,14 @@ public class ClassTranslator { case ConstPool.CONST_Methodref: case ConstPool.CONST_InterfaceMethodref: { - // translate the name and type + // translate the name and type (ie signature) BehaviorEntry entry = EntryFactory.getBehaviorEntry( Descriptor.toJvmName(editor.getMemberrefClassname(i)), editor.getMemberrefName(i), editor.getMemberrefType(i) ); BehaviorEntry translatedEntry = m_translator.translateEntry(entry); - - if (!entry.getName().equals(translatedEntry.getName()) || !entry.getSignature().equals(translatedEntry.getSignature())) { + if (!entry.equals(translatedEntry)) { editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); } } @@ -120,25 +109,18 @@ public class ClassTranslator { } if (entry.getSignature() != null) { - // translate the type + // translate the signature Signature translatedSignature = m_translator.translateSignature(entry.getSignature()); behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); } } // translate all the class names referenced in the code - // the above code only changed method/field/reference names and types, but not the class names themselves - Map map = Maps.newHashMap(); - for (ClassEntry obfClassEntry : ClassRenamer.getAllClassEntries(c)) { - ClassEntry deobfClassEntry = m_translator.translateEntry(obfClassEntry); - if (!obfClassEntry.equals(deobfClassEntry)) { - map.put(obfClassEntry, deobfClassEntry); - } - } - ClassRenamer.renameClasses(c, map); + // the above code only changed method/field/reference names and types, but not the rest of the class references + ClassRenamer.renameClasses(c, m_translator); // translate the source file attribute too - ClassEntry deobfClassEntry = map.get(classEntry); + ClassEntry deobfClassEntry = m_translator.translateEntry(classEntry); if (deobfClassEntry != null) { String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassName()) + ".java"; c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index 7bc61839..4898e6de 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java @@ -11,6 +11,7 @@ import javassist.expr.FieldAccess; import javassist.expr.MethodCall; import javassist.expr.NewExpr; +import com.strobel.assembler.metadata.FieldDefinition; import com.strobel.assembler.metadata.MethodDefinition; import cuchaz.enigma.analysis.JarIndex; @@ -54,6 +55,18 @@ public class EntryFactory { ); } + public static FieldEntry getFieldEntry(FieldDefinition def) { + return new FieldEntry( + new ClassEntry(def.getDeclaringType().getInternalName()), + def.getName(), + new Type(def.getErasedSignature()) + ); + } + + public static FieldEntry getFieldEntry(String className, String name, String type) { + return new FieldEntry(new ClassEntry(className), name, new Type(type)); + } + public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { return new FieldEntry( getObfClassEntry(classMapping), @@ -82,7 +95,7 @@ public class EntryFactory { return new MethodEntry( new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), - new Signature(def.getSignature()) + new Signature(def.getErasedSignature()) ); } @@ -121,7 +134,7 @@ public class EntryFactory { } else { return new ConstructorEntry( new ClassEntry(def.getDeclaringType().getInternalName()), - new Signature(def.getSignature()) + new Signature(def.getErasedSignature()) ); } } diff --git a/src/cuchaz/enigma/mapping/MappingsChecker.java b/src/cuchaz/enigma/mapping/MappingsChecker.java index c5ff7a7e..57ea90ce 100644 --- a/src/cuchaz/enigma/mapping/MappingsChecker.java +++ b/src/cuchaz/enigma/mapping/MappingsChecker.java @@ -66,7 +66,7 @@ public class MappingsChecker { // check the fields for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { - FieldEntry obfFieldEntry = new FieldEntry(classEntry, fieldMapping.getObfName(), fieldMapping.getObfType()); + FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping); if (!m_index.containsObfField(obfFieldEntry)) { classMapping.removeFieldMapping(fieldMapping); m_droppedFieldMappings.put(obfFieldEntry, fieldMapping); diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index d7766dc8..ad6c8785 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -66,7 +66,15 @@ public class MappingsRenamer { } public void markClassAsDeobfuscated(ClassEntry obf) { - setClassName(obf, obf.isInnerClass() ? obf.getInnermostClassName() : obf.getSimpleName()); + String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName(); + List mappingChain = getOrCreateClassMappingChain(obf); + if (mappingChain.size() == 1) { + ClassMapping classMapping = mappingChain.get(0); + m_mappings.setClassDeobfName(classMapping, deobfName); + } else { + ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); + outerClassMapping.setInnerClassName(obf, deobfName); + } } public void setFieldName(FieldEntry obf, String deobfName) { diff --git a/src/cuchaz/enigma/mapping/ParameterizedType.java b/src/cuchaz/enigma/mapping/ParameterizedType.java new file mode 100644 index 00000000..af24ef44 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ParameterizedType.java @@ -0,0 +1,54 @@ +package cuchaz.enigma.mapping; + +import cuchaz.enigma.Util; + + + +public class ParameterizedType extends Type { + + private static final long serialVersionUID = 1758975507937309011L; + + public ParameterizedType(Type other) { + super(other); + for (int i=0; i;"); + return buf.toString(); + } else { + return m_name; + } + } + + @Override + public boolean equals(Object other) { + if (other instanceof ParameterizedType) { + return equals((ParameterizedType)other); + } + return false; + } + + public boolean equals(ParameterizedType other) { + return m_name.equals(other.m_name) && m_parameters.equals(other.m_parameters); + } + + public int hashCode() { + return Util.combineHashesOrdered(m_name.hashCode(), m_parameters.hashCode()); + } + +} diff --git a/src/cuchaz/enigma/mapping/Signature.java b/src/cuchaz/enigma/mapping/Signature.java index ea83e40e..f4850ac3 100644 --- a/src/cuchaz/enigma/mapping/Signature.java +++ b/src/cuchaz/enigma/mapping/Signature.java @@ -79,16 +79,6 @@ public class Signature implements Serializable { return types; } - public Iterable classes() { - List out = Lists.newArrayList(); - for (Type type : types()) { - if (type.isClass()) { - out.add(type.getClassEntry()); - } - } - return out; - } - @Override public boolean equals(Object other) { if (other instanceof Signature) { diff --git a/src/cuchaz/enigma/mapping/Type.java b/src/cuchaz/enigma/mapping/Type.java index 72118b04..d5300839 100644 --- a/src/cuchaz/enigma/mapping/Type.java +++ b/src/cuchaz/enigma/mapping/Type.java @@ -1,8 +1,10 @@ package cuchaz.enigma.mapping; import java.io.Serializable; +import java.util.List; import java.util.Map; +import com.beust.jcommander.internal.Lists; import com.google.common.collect.Maps; public class Type implements Serializable { @@ -84,33 +86,66 @@ public class Type implements Serializable { throw new IllegalArgumentException("don't know how to parse: " + in); } - private String m_name; + protected String m_name; + protected List m_parameters; public Type(String name) { - m_name = name; + m_name = null; + m_parameters = Lists.newArrayList(); + + int start = name.indexOf('<'); + int stop = name.lastIndexOf('>'); + if (start > 0 && stop > start) { + + // deal with generic parameters + m_name = name.substring(0, start) + name.substring(stop + 1); + + String parameters = name.substring(start + 1, stop); + int i=0; + while (i parameters() { + return m_parameters; + } + @Override public boolean equals(Object other) { if (other instanceof Type) { @@ -214,7 +264,7 @@ public class Type implements Serializable { private static String readClass(String in) { // read all the characters in the buffer until we hit a ';' - // remember to treat parameters correctly + // include the parameters too StringBuilder buf = new StringBuilder(); int depth = 0; for (int i=0; i;").isClass(), is(true)); + assertThat(new Type("LFoo;").isClass(), is(true)); + assertThat(new Type("LFoo;>;").isClass(), is(true)); assertThat(new Type("[I").isClass(), is(false)); + assertThat(new Type("TFoo;").isClass(), is(false)); } @Test public void getClassEntry() { assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); + assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); + assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); + assertThat(new Type("LFoo;>;").getClassEntry(), is(newClass("Foo"))); } @Test @@ -89,6 +98,7 @@ public class TestType { assertThat(new Type("D").isArray(), is(false)); assertThat(new Type("LFoo;").isArray(), is(false)); assertThat(new Type("[I").isArray(), is(true)); + assertThat(new Type("TFoo;").isArray(), is(false)); } @Test @@ -106,6 +116,30 @@ public class TestType { assertThat(new Type("[Ljava/lang/String;").getArrayType(), is(new Type("Ljava/lang/String;"))); } + @Test + public void isTemplate() { + assertThat(new Type("V").isTemplate(), is(false)); + assertThat(new Type("Z").isTemplate(), is(false)); + assertThat(new Type("B").isTemplate(), is(false)); + assertThat(new Type("C").isTemplate(), is(false)); + assertThat(new Type("I").isTemplate(), is(false)); + assertThat(new Type("J").isTemplate(), is(false)); + assertThat(new Type("F").isTemplate(), is(false)); + assertThat(new Type("D").isTemplate(), is(false)); + assertThat(new Type("LFoo;").isTemplate(), is(false)); + assertThat(new Type("LFoo;").isTemplate(), is(false)); + assertThat(new Type("LFoo;").isTemplate(), is(false)); + assertThat(new Type("LFoo;>;").isTemplate(), is(false)); + assertThat(new Type("[I").isTemplate(), is(false)); + assertThat(new Type("TFoo;").isTemplate(), is(true)); + } + + @Test + public void getTemplate() { + assertThat(new Type("TT;").getTemplate(), is("T")); + assertThat(new Type("TFoo;").getTemplate(), is("Foo")); + } + @Test public void hasClass() { assertThat(new Type("LFoo;").hasClass(), is(true)); @@ -117,6 +151,37 @@ public class TestType { assertThat(new Type("[I").hasClass(), is(false)); assertThat(new Type("[[[I").hasClass(), is(false)); assertThat(new Type("Z").hasClass(), is(false)); + assertThat(new Type("TFoo;").hasClass(), is(false)); + } + + @Test + public void parameters() { + assertThat(new Type("LFoo;").parameters(), contains( + new Type("I") + )); + assertThat(new Type("LFoo;").parameters(), contains( + new Type("I"), + new Type("I"), + new Type("I"), + new Type("I") + )); + assertThat(new Type("LFoo;").parameters(), contains( + new Type("LBar;") + )); + assertThat(new Type("LFoo;").parameters(), contains( + new Type("LBar;"), + new Type("LCow;"), + new Type("LCheese;") + )); + + assertThat(new Type("LFoo;>;").parameters(), contains( + new Type("LBar;") + )); + + assertThat(new Type("LFoo;>;").parameters().iterator().next().parameters(), contains( + new Type("LCow;"), + new Type("LCheese;") + )); } @Test @@ -171,7 +236,18 @@ public class TestType { assertThat(Type.parseFirst("LFoo;[LFoo;"), is(answer)); } } - + + @Test + public void parseTemplate() { + final String answer = "TFoo;"; + assertThat(Type.parseFirst("TFoo;"), is(answer)); + assertThat(Type.parseFirst("TFoo;I"), is(answer)); + assertThat(Type.parseFirst("TFoo;JZ"), is(answer)); + assertThat(Type.parseFirst("TFoo;[I"), is(answer)); + assertThat(Type.parseFirst("TFoo;LFoo;"), is(answer)); + assertThat(Type.parseFirst("TFoo;[LFoo;"), is(answer)); + } + @Test public void parseArray() { { @@ -215,15 +291,90 @@ public class TestType { assertThat(new Type("[[[I"), is(new Type("[[[I"))); assertThat(new Type("[LFoo;"), is(new Type("[LFoo;"))); assertThat(new Type("LFoo;"), is(new Type("LFoo;"))); + assertThat(new Type("LFoo;"), is(new Type("LFoo;"))); + assertThat(new Type("TFoo;"), is(new Type("TFoo;"))); assertThat(new Type("V"), is(not(new Type("I")))); assertThat(new Type("I"), is(not(new Type("J")))); assertThat(new Type("I"), is(not(new Type("LBar;")))); assertThat(new Type("I"), is(not(new Type("[I")))); assertThat(new Type("LFoo;"), is(not(new Type("LBar;")))); - assertThat(new Type("LFoo;"), is(not(new Type("LFoo;")))); assertThat(new Type("[I"), is(not(new Type("[Z")))); assertThat(new Type("[[[I"), is(not(new Type("[I")))); assertThat(new Type("[LFoo;"), is(not(new Type("[LBar;")))); + assertThat(new Type("TFoo;"), is(not(new Type("TBar;")))); + } + + @Test + public void testToString() { + assertThat(new Type("V").toString(), is("V")); + assertThat(new Type("Z").toString(), is("Z")); + assertThat(new Type("B").toString(), is("B")); + assertThat(new Type("C").toString(), is("C")); + assertThat(new Type("I").toString(), is("I")); + assertThat(new Type("J").toString(), is("J")); + assertThat(new Type("F").toString(), is("F")); + assertThat(new Type("D").toString(), is("D")); + assertThat(new Type("LFoo;").toString(), is("LFoo;")); + assertThat(new Type("[I").toString(), is("[I")); + assertThat(new Type("[[[I").toString(), is("[[[I")); + assertThat(new Type("[LFoo;").toString(), is("[LFoo;")); + assertThat(new Type("LFoo;").toString(), is("LFoo;")); + assertThat(new Type("LFoo;").toString(), is("LFoo;")); + assertThat(new Type("LFoo;>;").toString(), is("LFoo;")); + assertThat(new Type("TFoo;").toString(), is("TFoo;")); + } + + private ParameterizedType ptype(String name) { + return new ParameterizedType(new Type(name)); + } + + @Test + public void equalsWithParameters() { + assertThat(ptype("V"), is(ptype("V"))); + assertThat(ptype("Z"), is(ptype("Z"))); + assertThat(ptype("B"), is(ptype("B"))); + assertThat(ptype("C"), is(ptype("C"))); + assertThat(ptype("I"), is(ptype("I"))); + assertThat(ptype("J"), is(ptype("J"))); + assertThat(ptype("F"), is(ptype("F"))); + assertThat(ptype("D"), is(ptype("D"))); + assertThat(ptype("LFoo;"), is(ptype("LFoo;"))); + assertThat(ptype("[I"), is(ptype("[I"))); + assertThat(ptype("[[[I"), is(ptype("[[[I"))); + assertThat(ptype("[LFoo;"), is(ptype("[LFoo;"))); + assertThat(ptype("LFoo;"), is(ptype("LFoo;"))); + assertThat(ptype("TFoo;"), is(ptype("TFoo;"))); + + assertThat(ptype("V"), is(not(ptype("I")))); + assertThat(ptype("I"), is(not(ptype("J")))); + assertThat(ptype("I"), is(not(ptype("LBar;")))); + assertThat(ptype("I"), is(not(ptype("[I")))); + assertThat(ptype("LFoo;"), is(not(ptype("LBar;")))); + assertThat(ptype("[I"), is(not(ptype("[Z")))); + assertThat(ptype("[[[I"), is(not(ptype("[I")))); + assertThat(ptype("[LFoo;"), is(not(ptype("[LBar;")))); + assertThat(ptype("LFoo;"), is(not(ptype("LFoo;")))); + assertThat(ptype("TFoo;"), is(not(ptype("TBar;")))); + } + + @Test + public void testToStringWithParams() { + assertThat(ptype("V").toString(), is("V")); + assertThat(ptype("Z").toString(), is("Z")); + assertThat(ptype("B").toString(), is("B")); + assertThat(ptype("C").toString(), is("C")); + assertThat(ptype("I").toString(), is("I")); + assertThat(ptype("J").toString(), is("J")); + assertThat(ptype("F").toString(), is("F")); + assertThat(ptype("D").toString(), is("D")); + assertThat(ptype("LFoo;").toString(), is("LFoo;")); + assertThat(ptype("[I").toString(), is("[I")); + assertThat(ptype("[[[I").toString(), is("[[[I")); + assertThat(ptype("[LFoo;").toString(), is("[LFoo;")); + assertThat(ptype("LFoo;").toString(), is("LFoo;")); + assertThat(ptype("LFoo;").toString(), is("LFoo;")); + assertThat(ptype("LFoo;>;").toString(), is("LFoo;>;")); + assertThat(ptype("TFoo;").toString(), is("TFoo;")); } } diff --git a/test/cuchaz/enigma/resources/translation.mappings b/test/cuchaz/enigma/resources/translation.mappings index 55bd7e5c..db78c19d 100644 --- a/test/cuchaz/enigma/resources/translation.mappings +++ b/test/cuchaz/enigma/resources/translation.mappings @@ -29,4 +29,13 @@ CLASS none/g deobf/G_OuterClass CLASS none/g$b$a A_NamedInnerClass FIELD a f4 I CLASS none/h -CLASS none/i I_Generics +CLASS none/i deobf/I_Generics + CLASS none/i$a A_Type + CLASS none/i$b B_Generic + FIELD a f4 Ljava/lang/Object; + METHOD a m1 ()Ljava/lang/Object; + FIELD a f1 Ljava/util/List; + FIELD b f2 Ljava/util/List; + FIELD a f3 Ljava/util/Map; + FIELD a f5 Lnone/i$b; + FIELD b f6 Lnone/i$b; -- cgit v1.2.3 From 03f9a58ced909336f6477d3b69a960d777cce1fa Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 17 Mar 2015 00:05:10 -0400 Subject: slightly better support for generics still needs work --- src/cuchaz/enigma/bytecode/ClassRenamer.java | 309 ++++++++++++++++++++------- 1 file changed, 230 insertions(+), 79 deletions(-) diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index 8bc084d3..d88daa5b 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -10,18 +10,28 @@ ******************************************************************************/ package cuchaz.enigma.bytecode; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.Set; -import javassist.ClassMap; import javassist.CtClass; +import javassist.bytecode.AttributeInfo; +import javassist.bytecode.BadBytecode; +import javassist.bytecode.ByteArray; +import javassist.bytecode.ClassFile; +import javassist.bytecode.CodeAttribute; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; +import javassist.bytecode.FieldInfo; import javassist.bytecode.InnerClassesAttribute; - -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - +import javassist.bytecode.LocalVariableTypeAttribute; +import javassist.bytecode.MethodInfo; +import javassist.bytecode.SignatureAttribute; +import javassist.bytecode.SignatureAttribute.ClassSignature; +import javassist.bytecode.SignatureAttribute.MethodSignature; +import javassist.bytecode.SignatureAttribute.ObjectType; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassNameReplacer; import cuchaz.enigma.mapping.ParameterizedType; @@ -30,6 +40,107 @@ import cuchaz.enigma.mapping.Type; public class ClassRenamer { + private static enum SignatureType { + Class { + + @Override + public void rename(SignatureAttribute attribute, ReplacerClassMap map) { + renameClassSignatureAttribute(attribute, map); + } + }, + Field { + + @Override + public void rename(SignatureAttribute attribute, ReplacerClassMap map) { + renameFieldSignatureAttribute(attribute, map); + } + }, + Method { + + @Override + public void rename(SignatureAttribute attribute, ReplacerClassMap map) { + renameMethodSignatureAttribute(attribute, map); + } + }; + + public abstract void rename(SignatureAttribute attribute, ReplacerClassMap map); + } + + private static class ReplacerClassMap extends HashMap { + + private static final long serialVersionUID = 317915213205066168L; + + private ClassNameReplacer m_replacer; + + public ReplacerClassMap(ClassNameReplacer replacer) { + m_replacer = replacer; + } + + @Override + public String get(Object obj) { + if (obj instanceof String) { + return get((String)obj); + } else if (obj instanceof ObjectType) { + return get((ObjectType)obj); + } + return null; + } + + public String get(String typeName) { + + // javassist doesn't give us the class framing, add it + typeName = "L" + typeName + ";"; + + String out = getFramed(typeName); + if (out == null) { + return null; + } + + // javassist doesn't want the class framing, so remove it + out = out.substring(1, out.length() - 1); + + return out; + } + + public String getFramed(String typeName) { + ParameterizedType type = new ParameterizedType(new Type(typeName)); + ParameterizedType renamedType = new ParameterizedType(type, m_replacer); + if (!type.equals(renamedType)) { + return renamedType.toString(); + } + return null; + } + + public String get(ObjectType type) { + + // we can deal with the ones that start with a class + String signature = type.encode(); + if (signature.startsWith("L") || signature.startsWith("[")) { + + // TEMP: skip special characters for now + if (signature.indexOf('*') >= 0 || signature.indexOf('+') >= 0 || signature.indexOf('-') >= 0) { + System.out.println("Skipping translating: " + signature); + return null; + } + + // replace inner class / with $ + int pos = signature.indexOf("$"); + if (pos >= 0) { + signature = signature.substring(0, pos + 1) + signature.substring(pos, signature.length()).replace('/', '$'); + } + + return getFramed(signature); + } else if (signature.startsWith("T")) { + // don't need to care about template names + return null; + } else { + // TEMP + System.out.println("Skipping translating: " + signature); + return null; + } + } + } + public static void renameClasses(CtClass c, final Translator translator) { renameClasses(c, new ClassNameReplacer() { @Override @@ -69,86 +180,42 @@ public class ClassRenamer { }); } + @SuppressWarnings("unchecked") public static void renameClasses(CtClass c, ClassNameReplacer replacer) { - Map map = Maps.newHashMap(); - for (ParameterizedType type : ClassRenamer.getAllClassTypes(c)) { - ParameterizedType renamedType = new ParameterizedType(type, replacer); - if (!type.equals(renamedType)) { - map.put(type, renamedType); - } - } - renameTypes(c, map); - } - - public static Set getAllClassTypes(final CtClass c) { - // TODO: might have to scan SignatureAttributes directly because javassist is buggy + // sadly, we can't use CtClass.renameClass() because SignatureAttribute.renameClass() is extremely buggy =( - // get the class types that javassist knows about - final Set types = Sets.newHashSet(); - ClassMap map = new ClassMap() { - @Override - public Object get(Object obj) { - if (obj instanceof String) { - String str = (String)obj; - - // sometimes javasist gives us dot-separated classes... whadda hell? - str = str.replace('.', '/'); - - // skip weird types - boolean hasNestedParams = str.indexOf('<') >= 0 && str.indexOf('<', str.indexOf('<')+1) >= 0; - boolean hasWeirdChars = str.indexOf('*') >= 0 || str.indexOf('-') >= 0 || str.indexOf('+') >= 0; - if (hasNestedParams || hasWeirdChars) { - // TEMP - System.out.println("Skipped translating: " + str); - return null; - } - - ParameterizedType type = new ParameterizedType(new Type("L" + str + ";")); - assert(type.isClass()); - // TEMP - try { - type.getClassEntry(); - } catch (Throwable t) { - // bad type - // TEMP - System.out.println("Skipped translating: " + str); - return null; - } - - types.add(type); - } - return null; - } - - private static final long serialVersionUID = -202160293602070641L; - }; - c.replaceClassName(map); + ReplacerClassMap map = new ReplacerClassMap(replacer); + ClassFile classFile = c.getClassFile(); - return types; - } - - public static void renameTypes(CtClass c, Map map) { + // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo) + ConstPool constPool = c.getClassFile().getConstPool(); + constPool.renameClass(map); - // convert the type map to a javassist class map - ClassMap nameMap = new ClassMap(); - for (Map.Entry entry : map.entrySet()) { - String source = entry.getKey().toString(); - String dest = entry.getValue().toString(); - - // don't forget to chop off the L ... ; - // javassist doesn't want it there - source = source.substring(1, source.length() - 1); - dest = dest.substring(1, dest.length() - 1); - - nameMap.put(source, dest); + // rename class attributes + renameAttributes(classFile.getAttributes(), map, SignatureType.Class); + + // rename methods + for (MethodInfo methodInfo : (List)classFile.getMethods()) { + methodInfo.setDescriptor(Descriptor.rename(methodInfo.getDescriptor(), map)); + renameAttributes(methodInfo.getAttributes(), map, SignatureType.Method); + } + + // rename fields + for (FieldInfo fieldInfo : (List)classFile.getFields()) { + fieldInfo.setDescriptor(Descriptor.rename(fieldInfo.getDescriptor(), map)); + renameAttributes(fieldInfo.getAttributes(), map, SignatureType.Field); + } + + // rename the class name itself last + // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass() + // we only want to replace exactly this class name + String newName = replacer.replace(Descriptor.toJvmName(c.getName())); + if (newName != null) { + c.setName(Descriptor.toJavaName(newName)); } - // replace!! - c.replaceClassName(nameMap); - // replace simple names in the InnerClasses attribute too - ConstPool constants = c.getClassFile().getConstPool(); InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); if (attr != null) { for (int i = 0; i < attr.tableLength(); i++) { @@ -158,7 +225,7 @@ public class ClassRenamer { if (attr.innerNameIndex(i) != 0) { // update the inner name - attr.setInnerNameIndex(i, constants.addUtf8Info(classEntry.getInnermostClassName())); + attr.setInnerNameIndex(i, constPool.addUtf8Info(classEntry.getInnermostClassName())); } /* DEBUG @@ -167,4 +234,88 @@ public class ClassRenamer { } } } + + @SuppressWarnings("unchecked") + private static void renameAttributes(List attributes, ReplacerClassMap map, SignatureType type) { + try { + + // make the rename class method accessible + Method renameClassMethod = AttributeInfo.class.getDeclaredMethod("renameClass", Map.class); + renameClassMethod.setAccessible(true); + + for (AttributeInfo attribute : attributes) { + if (attribute instanceof SignatureAttribute) { + // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell + SignatureAttribute signatureAttribute = (SignatureAttribute)attribute; + type.rename(signatureAttribute, map); + } else if (attribute instanceof CodeAttribute) { + // code attributes have signature attributes too (indirectly) + CodeAttribute codeAttribute = (CodeAttribute)attribute; + renameAttributes(codeAttribute.getAttributes(), map, type); + } else if (attribute instanceof LocalVariableTypeAttribute) { + // lvt attributes have signature attributes too + LocalVariableTypeAttribute localVariableAttribute = (LocalVariableTypeAttribute)attribute; + renameLocalVariableTypeAttribute(localVariableAttribute, map); + } else { + renameClassMethod.invoke(attribute, map); + } + } + + } catch(NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + throw new Error("Unable to call javassist methods by reflection!", ex); + } + } + + private static void renameClassSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) { + try { + ClassSignature classSignature = SignatureAttribute.toClassSignature(attribute.getSignature()); + // TODO: do class signatures + } catch (BadBytecode ex) { + throw new Error("Unable to parse class signature: " + attribute.getSignature(), ex); + } + } + + private static void renameFieldSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) { + try { + ObjectType fieldSignature = SignatureAttribute.toFieldSignature(attribute.getSignature()); + String newSignature = map.get(fieldSignature); + if (newSignature != null) { + attribute.setSignature(newSignature); + } + } catch (BadBytecode ex) { + throw new Error("Unable to parse field signature: " + attribute.getSignature(), ex); + } + } + + private static void renameMethodSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) { + try { + MethodSignature methodSignature = SignatureAttribute.toMethodSignature(attribute.getSignature()); + // TODO: do method signatures + } catch (BadBytecode ex) { + throw new Error("Unable to parse method signature: " + attribute.getSignature(), ex); + } + } + + private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) { + // adapted from LocalVariableAttribute.renameClass() + ConstPool cp = attribute.getConstPool(); + int n = attribute.tableLength(); + byte[] info = attribute.get(); + for (int i = 0; i < n; ++i) { + int pos = i * 10 + 2; + int index = ByteArray.readU16bit(info, pos + 6); + if (index != 0) { + String desc = cp.getUtf8Info(index); + try { + ObjectType fieldSignature = SignatureAttribute.toFieldSignature(desc); + String newDesc = map.get(fieldSignature); + if (newDesc != null) { + ByteArray.write16bit(cp.addUtf8Info(newDesc), info, pos + 6); + } + } catch (BadBytecode ex) { + throw new Error("Unable to parse field signature: " + desc, ex); + } + } + } + } } -- cgit v1.2.3 From ecfda21f3db9e62e3acf074e9842e92ace4cb3ab Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 17 Mar 2015 20:01:55 -0400 Subject: parsing generic signatures is tricky. don't write custom code to do it. switching to library instead --- src/cuchaz/enigma/bytecode/ClassRenamer.java | 9 +- src/cuchaz/enigma/mapping/ParameterizedType.java | 54 -------- src/cuchaz/enigma/mapping/Type.java | 61 +-------- test/cuchaz/enigma/TestType.java | 167 ++--------------------- 4 files changed, 22 insertions(+), 269 deletions(-) delete mode 100644 src/cuchaz/enigma/mapping/ParameterizedType.java diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index d88daa5b..d7acd309 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -34,7 +34,6 @@ import javassist.bytecode.SignatureAttribute.MethodSignature; import javassist.bytecode.SignatureAttribute.ObjectType; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassNameReplacer; -import cuchaz.enigma.mapping.ParameterizedType; import cuchaz.enigma.mapping.Translator; import cuchaz.enigma.mapping.Type; @@ -103,8 +102,8 @@ public class ClassRenamer { } public String getFramed(String typeName) { - ParameterizedType type = new ParameterizedType(new Type(typeName)); - ParameterizedType renamedType = new ParameterizedType(type, m_replacer); + Type type = new Type(typeName); + Type renamedType = new Type(type, m_replacer); if (!type.equals(renamedType)) { return renamedType.toString(); } @@ -115,6 +114,7 @@ public class ClassRenamer { // we can deal with the ones that start with a class String signature = type.encode(); + /* if (signature.startsWith("L") || signature.startsWith("[")) { // TEMP: skip special characters for now @@ -134,10 +134,11 @@ public class ClassRenamer { // don't need to care about template names return null; } else { + */ // TEMP System.out.println("Skipping translating: " + signature); return null; - } + //} } } diff --git a/src/cuchaz/enigma/mapping/ParameterizedType.java b/src/cuchaz/enigma/mapping/ParameterizedType.java deleted file mode 100644 index af24ef44..00000000 --- a/src/cuchaz/enigma/mapping/ParameterizedType.java +++ /dev/null @@ -1,54 +0,0 @@ -package cuchaz.enigma.mapping; - -import cuchaz.enigma.Util; - - - -public class ParameterizedType extends Type { - - private static final long serialVersionUID = 1758975507937309011L; - - public ParameterizedType(Type other) { - super(other); - for (int i=0; i;"); - return buf.toString(); - } else { - return m_name; - } - } - - @Override - public boolean equals(Object other) { - if (other instanceof ParameterizedType) { - return equals((ParameterizedType)other); - } - return false; - } - - public boolean equals(ParameterizedType other) { - return m_name.equals(other.m_name) && m_parameters.equals(other.m_parameters); - } - - public int hashCode() { - return Util.combineHashesOrdered(m_name.hashCode(), m_parameters.hashCode()); - } - -} diff --git a/src/cuchaz/enigma/mapping/Type.java b/src/cuchaz/enigma/mapping/Type.java index d5300839..3f441e22 100644 --- a/src/cuchaz/enigma/mapping/Type.java +++ b/src/cuchaz/enigma/mapping/Type.java @@ -1,10 +1,8 @@ package cuchaz.enigma.mapping; import java.io.Serializable; -import java.util.List; import java.util.Map; -import com.beust.jcommander.internal.Lists; import com.google.common.collect.Maps; public class Type implements Serializable { @@ -85,43 +83,22 @@ public class Type implements Serializable { throw new IllegalArgumentException("don't know how to parse: " + in); } - + protected String m_name; - protected List m_parameters; public Type(String name) { - m_name = null; - m_parameters = Lists.newArrayList(); - int start = name.indexOf('<'); - int stop = name.lastIndexOf('>'); - if (start > 0 && stop > start) { - - // deal with generic parameters - m_name = name.substring(0, start) + name.substring(stop + 1); - - String parameters = name.substring(start + 1, stop); - int i=0; - while (i= 0 || name.indexOf('>') >= 0) { + throw new IllegalArgumentException("don't use with generic types or templates: " + name); } + + m_name = name; } public Type(Type other) { m_name = other.m_name; - m_parameters = Lists.newArrayList(); - for (Type parameter : other.m_parameters) { - m_parameters.add(new Type(parameter)); - } } public Type(ClassEntry classEntry) { @@ -141,11 +118,6 @@ public class Type implements Serializable { m_name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";"; } } - - m_parameters = Lists.newArrayList(); - for (Type parameter : other.m_parameters) { - m_parameters.add(new Type(parameter, replacer)); - } } @Override @@ -191,17 +163,6 @@ public class Type implements Serializable { } } - public boolean isTemplate() { - return m_name.charAt(0) == 'T' && m_name.charAt(m_name.length() - 1) == ';'; - } - - public String getTemplate() { - if (!isTemplate()) { - throw new IllegalStateException("not an template"); - } - return m_name.substring(1, m_name.length() - 1); - } - public boolean isArray() { return m_name.charAt(0) == '['; } @@ -232,14 +193,6 @@ public class Type implements Serializable { return isClass() || (isArray() && getArrayType().hasClass()); } - public boolean hasParameters() { - return !m_parameters.isEmpty(); - } - - public Iterable parameters() { - return m_parameters; - } - @Override public boolean equals(Object other) { if (other instanceof Type) { diff --git a/test/cuchaz/enigma/TestType.java b/test/cuchaz/enigma/TestType.java index 544a10c1..71aaaee2 100644 --- a/test/cuchaz/enigma/TestType.java +++ b/test/cuchaz/enigma/TestType.java @@ -6,7 +6,6 @@ import static org.hamcrest.Matchers.*; import org.junit.Test; -import cuchaz.enigma.mapping.ParameterizedType; import cuchaz.enigma.mapping.Type; @@ -24,7 +23,6 @@ public class TestType { assertThat(new Type("D").isVoid(), is(false)); assertThat(new Type("LFoo;").isVoid(), is(false)); assertThat(new Type("[I").isVoid(), is(false)); - assertThat(new Type("TFoo;").isVoid(), is(false)); } @Test @@ -39,7 +37,6 @@ public class TestType { assertThat(new Type("D").isPrimitive(), is(true)); assertThat(new Type("LFoo;").isPrimitive(), is(false)); assertThat(new Type("[I").isPrimitive(), is(false)); - assertThat(new Type("TFoo;").isPrimitive(), is(false)); } @Test @@ -64,26 +61,19 @@ public class TestType { assertThat(new Type("F").isClass(), is(false)); assertThat(new Type("D").isClass(), is(false)); assertThat(new Type("LFoo;").isClass(), is(true)); - assertThat(new Type("LFoo;").isClass(), is(true)); - assertThat(new Type("LFoo;").isClass(), is(true)); - assertThat(new Type("LFoo;>;").isClass(), is(true)); assertThat(new Type("[I").isClass(), is(false)); - assertThat(new Type("TFoo;").isClass(), is(false)); } @Test public void getClassEntry() { assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); - assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); - assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); - assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); - assertThat(new Type("LFoo;>;").getClassEntry(), is(newClass("Foo"))); + assertThat(new Type("Ljava/lang/String;").getClassEntry(), is(newClass("java/lang/String"))); } @Test public void getArrayClassEntry() { assertThat(new Type("[LFoo;").getClassEntry(), is(newClass("Foo"))); - assertThat(new Type("[[[LFoo;").getClassEntry(), is(newClass("Foo"))); + assertThat(new Type("[[[Ljava/lang/String;").getClassEntry(), is(newClass("java/lang/String"))); } @Test @@ -98,7 +88,6 @@ public class TestType { assertThat(new Type("D").isArray(), is(false)); assertThat(new Type("LFoo;").isArray(), is(false)); assertThat(new Type("[I").isArray(), is(true)); - assertThat(new Type("TFoo;").isArray(), is(false)); } @Test @@ -116,34 +105,10 @@ public class TestType { assertThat(new Type("[Ljava/lang/String;").getArrayType(), is(new Type("Ljava/lang/String;"))); } - @Test - public void isTemplate() { - assertThat(new Type("V").isTemplate(), is(false)); - assertThat(new Type("Z").isTemplate(), is(false)); - assertThat(new Type("B").isTemplate(), is(false)); - assertThat(new Type("C").isTemplate(), is(false)); - assertThat(new Type("I").isTemplate(), is(false)); - assertThat(new Type("J").isTemplate(), is(false)); - assertThat(new Type("F").isTemplate(), is(false)); - assertThat(new Type("D").isTemplate(), is(false)); - assertThat(new Type("LFoo;").isTemplate(), is(false)); - assertThat(new Type("LFoo;").isTemplate(), is(false)); - assertThat(new Type("LFoo;").isTemplate(), is(false)); - assertThat(new Type("LFoo;>;").isTemplate(), is(false)); - assertThat(new Type("[I").isTemplate(), is(false)); - assertThat(new Type("TFoo;").isTemplate(), is(true)); - } - - @Test - public void getTemplate() { - assertThat(new Type("TT;").getTemplate(), is("T")); - assertThat(new Type("TFoo;").getTemplate(), is("Foo")); - } - @Test public void hasClass() { assertThat(new Type("LFoo;").hasClass(), is(true)); - assertThat(new Type("LCow;").hasClass(), is(true)); + assertThat(new Type("Ljava/lang/String;").hasClass(), is(true)); assertThat(new Type("[LBar;").hasClass(), is(true)); assertThat(new Type("[[[LCat;").hasClass(), is(true)); @@ -151,37 +116,6 @@ public class TestType { assertThat(new Type("[I").hasClass(), is(false)); assertThat(new Type("[[[I").hasClass(), is(false)); assertThat(new Type("Z").hasClass(), is(false)); - assertThat(new Type("TFoo;").hasClass(), is(false)); - } - - @Test - public void parameters() { - assertThat(new Type("LFoo;").parameters(), contains( - new Type("I") - )); - assertThat(new Type("LFoo;").parameters(), contains( - new Type("I"), - new Type("I"), - new Type("I"), - new Type("I") - )); - assertThat(new Type("LFoo;").parameters(), contains( - new Type("LBar;") - )); - assertThat(new Type("LFoo;").parameters(), contains( - new Type("LBar;"), - new Type("LCow;"), - new Type("LCheese;") - )); - - assertThat(new Type("LFoo;>;").parameters(), contains( - new Type("LBar;") - )); - - assertThat(new Type("LFoo;>;").parameters().iterator().next().parameters(), contains( - new Type("LCow;"), - new Type("LCheese;") - )); } @Test @@ -218,36 +152,16 @@ public class TestType { assertThat(Type.parseFirst("LFoo;[LFoo;"), is(answer)); } { - final String answer = "LFoo;"; - assertThat(Type.parseFirst("LFoo;"), is(answer)); - assertThat(Type.parseFirst("LFoo;I"), is(answer)); - assertThat(Type.parseFirst("LFoo;JZ"), is(answer)); - assertThat(Type.parseFirst("LFoo;[I"), is(answer)); - assertThat(Type.parseFirst("LFoo;LFoo;"), is(answer)); - assertThat(Type.parseFirst("LFoo;[LFoo;"), is(answer)); - } - { - final String answer = "LFoo;"; - assertThat(Type.parseFirst("LFoo;"), is(answer)); - assertThat(Type.parseFirst("LFoo;I"), is(answer)); - assertThat(Type.parseFirst("LFoo;JZ"), is(answer)); - assertThat(Type.parseFirst("LFoo;[I"), is(answer)); - assertThat(Type.parseFirst("LFoo;LFoo;"), is(answer)); - assertThat(Type.parseFirst("LFoo;[LFoo;"), is(answer)); + final String answer = "Ljava/lang/String;"; + assertThat(Type.parseFirst("Ljava/lang/String;"), is(answer)); + assertThat(Type.parseFirst("Ljava/lang/String;I"), is(answer)); + assertThat(Type.parseFirst("Ljava/lang/String;JZ"), is(answer)); + assertThat(Type.parseFirst("Ljava/lang/String;[I"), is(answer)); + assertThat(Type.parseFirst("Ljava/lang/String;LFoo;"), is(answer)); + assertThat(Type.parseFirst("Ljava/lang/String;[LFoo;"), is(answer)); } } - @Test - public void parseTemplate() { - final String answer = "TFoo;"; - assertThat(Type.parseFirst("TFoo;"), is(answer)); - assertThat(Type.parseFirst("TFoo;I"), is(answer)); - assertThat(Type.parseFirst("TFoo;JZ"), is(answer)); - assertThat(Type.parseFirst("TFoo;[I"), is(answer)); - assertThat(Type.parseFirst("TFoo;LFoo;"), is(answer)); - assertThat(Type.parseFirst("TFoo;[LFoo;"), is(answer)); - } - @Test public void parseArray() { { @@ -290,9 +204,6 @@ public class TestType { assertThat(new Type("[I"), is(new Type("[I"))); assertThat(new Type("[[[I"), is(new Type("[[[I"))); assertThat(new Type("[LFoo;"), is(new Type("[LFoo;"))); - assertThat(new Type("LFoo;"), is(new Type("LFoo;"))); - assertThat(new Type("LFoo;"), is(new Type("LFoo;"))); - assertThat(new Type("TFoo;"), is(new Type("TFoo;"))); assertThat(new Type("V"), is(not(new Type("I")))); assertThat(new Type("I"), is(not(new Type("J")))); @@ -302,7 +213,6 @@ public class TestType { assertThat(new Type("[I"), is(not(new Type("[Z")))); assertThat(new Type("[[[I"), is(not(new Type("[I")))); assertThat(new Type("[LFoo;"), is(not(new Type("[LBar;")))); - assertThat(new Type("TFoo;"), is(not(new Type("TBar;")))); } @Test @@ -319,62 +229,5 @@ public class TestType { assertThat(new Type("[I").toString(), is("[I")); assertThat(new Type("[[[I").toString(), is("[[[I")); assertThat(new Type("[LFoo;").toString(), is("[LFoo;")); - assertThat(new Type("LFoo;").toString(), is("LFoo;")); - assertThat(new Type("LFoo;").toString(), is("LFoo;")); - assertThat(new Type("LFoo;>;").toString(), is("LFoo;")); - assertThat(new Type("TFoo;").toString(), is("TFoo;")); - } - - private ParameterizedType ptype(String name) { - return new ParameterizedType(new Type(name)); - } - - @Test - public void equalsWithParameters() { - assertThat(ptype("V"), is(ptype("V"))); - assertThat(ptype("Z"), is(ptype("Z"))); - assertThat(ptype("B"), is(ptype("B"))); - assertThat(ptype("C"), is(ptype("C"))); - assertThat(ptype("I"), is(ptype("I"))); - assertThat(ptype("J"), is(ptype("J"))); - assertThat(ptype("F"), is(ptype("F"))); - assertThat(ptype("D"), is(ptype("D"))); - assertThat(ptype("LFoo;"), is(ptype("LFoo;"))); - assertThat(ptype("[I"), is(ptype("[I"))); - assertThat(ptype("[[[I"), is(ptype("[[[I"))); - assertThat(ptype("[LFoo;"), is(ptype("[LFoo;"))); - assertThat(ptype("LFoo;"), is(ptype("LFoo;"))); - assertThat(ptype("TFoo;"), is(ptype("TFoo;"))); - - assertThat(ptype("V"), is(not(ptype("I")))); - assertThat(ptype("I"), is(not(ptype("J")))); - assertThat(ptype("I"), is(not(ptype("LBar;")))); - assertThat(ptype("I"), is(not(ptype("[I")))); - assertThat(ptype("LFoo;"), is(not(ptype("LBar;")))); - assertThat(ptype("[I"), is(not(ptype("[Z")))); - assertThat(ptype("[[[I"), is(not(ptype("[I")))); - assertThat(ptype("[LFoo;"), is(not(ptype("[LBar;")))); - assertThat(ptype("LFoo;"), is(not(ptype("LFoo;")))); - assertThat(ptype("TFoo;"), is(not(ptype("TBar;")))); - } - - @Test - public void testToStringWithParams() { - assertThat(ptype("V").toString(), is("V")); - assertThat(ptype("Z").toString(), is("Z")); - assertThat(ptype("B").toString(), is("B")); - assertThat(ptype("C").toString(), is("C")); - assertThat(ptype("I").toString(), is("I")); - assertThat(ptype("J").toString(), is("J")); - assertThat(ptype("F").toString(), is("F")); - assertThat(ptype("D").toString(), is("D")); - assertThat(ptype("LFoo;").toString(), is("LFoo;")); - assertThat(ptype("[I").toString(), is("[I")); - assertThat(ptype("[[[I").toString(), is("[[[I")); - assertThat(ptype("[LFoo;").toString(), is("[LFoo;")); - assertThat(ptype("LFoo;").toString(), is("LFoo;")); - assertThat(ptype("LFoo;").toString(), is("LFoo;")); - assertThat(ptype("LFoo;>;").toString(), is("LFoo;>;")); - assertThat(ptype("TFoo;").toString(), is("TFoo;")); } } -- cgit v1.2.3 From 0c15b6485cfeff42a438e8387d209055ad7d7704 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 18 Mar 2015 00:06:33 -0400 Subject: added full rename support for classes buried in generic signatures oi, what a pain in the ass... --- src/cuchaz/enigma/bytecode/ClassRenamer.java | 378 ++++++++++++++++++++------- 1 file changed, 280 insertions(+), 98 deletions(-) diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index d7acd309..8d25e722 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -12,6 +12,7 @@ package cuchaz.enigma.bytecode; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,13 +30,19 @@ import javassist.bytecode.InnerClassesAttribute; import javassist.bytecode.LocalVariableTypeAttribute; import javassist.bytecode.MethodInfo; import javassist.bytecode.SignatureAttribute; +import javassist.bytecode.SignatureAttribute.ArrayType; +import javassist.bytecode.SignatureAttribute.BaseType; import javassist.bytecode.SignatureAttribute.ClassSignature; +import javassist.bytecode.SignatureAttribute.ClassType; import javassist.bytecode.SignatureAttribute.MethodSignature; +import javassist.bytecode.SignatureAttribute.NestedClassType; import javassist.bytecode.SignatureAttribute.ObjectType; +import javassist.bytecode.SignatureAttribute.Type; +import javassist.bytecode.SignatureAttribute.TypeArgument; +import javassist.bytecode.SignatureAttribute.TypeVariable; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassNameReplacer; import cuchaz.enigma.mapping.Translator; -import cuchaz.enigma.mapping.Type; public class ClassRenamer { @@ -43,26 +50,26 @@ public class ClassRenamer { Class { @Override - public void rename(SignatureAttribute attribute, ReplacerClassMap map) { - renameClassSignatureAttribute(attribute, map); + public String rename(String signature, ReplacerClassMap map) { + return renameClassSignature(signature, map); } }, Field { @Override - public void rename(SignatureAttribute attribute, ReplacerClassMap map) { - renameFieldSignatureAttribute(attribute, map); + public String rename(String signature, ReplacerClassMap map) { + return renameFieldSignature(signature, map); } }, Method { @Override - public void rename(SignatureAttribute attribute, ReplacerClassMap map) { - renameMethodSignatureAttribute(attribute, map); + public String rename(String signature, ReplacerClassMap map) { + return renameMethodSignature(signature, map); } }; - public abstract void rename(SignatureAttribute attribute, ReplacerClassMap map); + public abstract String rename(String signature, ReplacerClassMap map); } private static class ReplacerClassMap extends HashMap { @@ -79,66 +86,12 @@ public class ClassRenamer { public String get(Object obj) { if (obj instanceof String) { return get((String)obj); - } else if (obj instanceof ObjectType) { - return get((ObjectType)obj); } return null; } - public String get(String typeName) { - - // javassist doesn't give us the class framing, add it - typeName = "L" + typeName + ";"; - - String out = getFramed(typeName); - if (out == null) { - return null; - } - - // javassist doesn't want the class framing, so remove it - out = out.substring(1, out.length() - 1); - - return out; - } - - public String getFramed(String typeName) { - Type type = new Type(typeName); - Type renamedType = new Type(type, m_replacer); - if (!type.equals(renamedType)) { - return renamedType.toString(); - } - return null; - } - - public String get(ObjectType type) { - - // we can deal with the ones that start with a class - String signature = type.encode(); - /* - if (signature.startsWith("L") || signature.startsWith("[")) { - - // TEMP: skip special characters for now - if (signature.indexOf('*') >= 0 || signature.indexOf('+') >= 0 || signature.indexOf('-') >= 0) { - System.out.println("Skipping translating: " + signature); - return null; - } - - // replace inner class / with $ - int pos = signature.indexOf("$"); - if (pos >= 0) { - signature = signature.substring(0, pos + 1) + signature.substring(pos, signature.length()).replace('/', '$'); - } - - return getFramed(signature); - } else if (signature.startsWith("T")) { - // don't need to care about template names - return null; - } else { - */ - // TEMP - System.out.println("Skipping translating: " + signature); - return null; - //} + public String get(String className) { + return m_replacer.replace(className); } } @@ -248,7 +201,10 @@ public class ClassRenamer { if (attribute instanceof SignatureAttribute) { // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell SignatureAttribute signatureAttribute = (SignatureAttribute)attribute; - type.rename(signatureAttribute, map); + String newSignature = type.rename(signatureAttribute.getSignature(), map); + if (newSignature != null) { + signatureAttribute.setSignature(newSignature); + } } else if (attribute instanceof CodeAttribute) { // code attributes have signature attributes too (indirectly) CodeAttribute codeAttribute = (CodeAttribute)attribute; @@ -267,56 +223,282 @@ public class ClassRenamer { } } - private static void renameClassSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) { + private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) { + + // adapted from LocalVariableAttribute.renameClass() + ConstPool cp = attribute.getConstPool(); + int n = attribute.tableLength(); + byte[] info = attribute.get(); + for (int i = 0; i < n; ++i) { + int pos = i * 10 + 2; + int index = ByteArray.readU16bit(info, pos + 6); + if (index != 0) { + String signature = cp.getUtf8Info(index); + String newSignature = renameLocalVariableSignature(signature, map); + if (newSignature != null) { + ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6); + } + } + } + } + + private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) { + + // for some reason, signatures with . in them don't count as field signatures + // looks like anonymous classes delimit with . in stead of $ + // convert the . to $, but keep track of how many we replace + // we need to put them back after we translate + int start = signature.lastIndexOf('$') + 1; + int numConverted = 0; + StringBuilder buf = new StringBuilder(signature); + for (int i=buf.length()-1; i>=start; i--) { + char c = buf.charAt(i); + if (c == '.') { + buf.setCharAt(i, '$'); + numConverted++; + } + } + signature = buf.toString(); + + // translate + String newSignature = renameFieldSignature(signature, map); + if (newSignature != null) { + + // put the delimiters back + buf = new StringBuilder(newSignature); + for (int i=buf.length()-1; i>=0 && numConverted > 0; i--) { + char c = buf.charAt(i); + if (c == '$') { + buf.setCharAt(i, '.'); + numConverted--; + } + } + assert(numConverted == 0); + newSignature = buf.toString(); + + return newSignature; + } + + return null; + } + + private static String renameClassSignature(String signature, ReplacerClassMap map) { try { - ClassSignature classSignature = SignatureAttribute.toClassSignature(attribute.getSignature()); - // TODO: do class signatures + return getSignature(renameType(SignatureAttribute.toClassSignature(signature), map)); } catch (BadBytecode ex) { - throw new Error("Unable to parse class signature: " + attribute.getSignature(), ex); + throw new Error("Can't parse field signature: " + signature); } } - private static void renameFieldSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) { + private static String renameFieldSignature(String signature, ReplacerClassMap map) { try { - ObjectType fieldSignature = SignatureAttribute.toFieldSignature(attribute.getSignature()); - String newSignature = map.get(fieldSignature); - if (newSignature != null) { - attribute.setSignature(newSignature); - } + return getSignature(renameType(SignatureAttribute.toFieldSignature(signature), map)); } catch (BadBytecode ex) { - throw new Error("Unable to parse field signature: " + attribute.getSignature(), ex); + throw new Error("Can't parse class signature: " + signature); } } - private static void renameMethodSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) { + private static String renameMethodSignature(String signature, ReplacerClassMap map) { try { - MethodSignature methodSignature = SignatureAttribute.toMethodSignature(attribute.getSignature()); - // TODO: do method signatures + return getSignature(renameType(SignatureAttribute.toMethodSignature(signature), map)); } catch (BadBytecode ex) { - throw new Error("Unable to parse method signature: " + attribute.getSignature(), ex); + throw new Error("Can't parse method signature: " + signature); } } - private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) { - // adapted from LocalVariableAttribute.renameClass() - ConstPool cp = attribute.getConstPool(); - int n = attribute.tableLength(); - byte[] info = attribute.get(); - for (int i = 0; i < n; ++i) { - int pos = i * 10 + 2; - int index = ByteArray.readU16bit(info, pos + 6); - if (index != 0) { - String desc = cp.getUtf8Info(index); - try { - ObjectType fieldSignature = SignatureAttribute.toFieldSignature(desc); - String newDesc = map.get(fieldSignature); - if (newDesc != null) { - ByteArray.write16bit(cp.addUtf8Info(newDesc), info, pos + 6); - } - } catch (BadBytecode ex) { - throw new Error("Unable to parse field signature: " + desc, ex); + private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) { + + // NOTE: don't have to translate type parameters + + // translate superclass + ClassType superclassType = type.getSuperClass(); + if (superclassType != ClassType.OBJECT) { + ClassType newSuperclassType = renameType(superclassType, map); + if (newSuperclassType != null) { + superclassType = newSuperclassType; + } + } + + // translate interfaces + ClassType[] interfaceTypes = type.getInterfaces(); + if (interfaceTypes != null) { + interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); + for (int i=0; i;LMoo;)LBar;"); - assertThat(sig.getArgumentTypes(), contains( - new Type("LFoo;"), - new Type("LMoo;") - )); - assertThat(sig.getReturnType(), is(new Type("LBar;"))); - } } @Test -- cgit v1.2.3 From ebbaaba1369ca25b7a92d6e26900bd4e63f262a0 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 18 Mar 2015 22:13:45 -0400 Subject: repackage for v0.10 beta --- build.py | 4 ++-- src/cuchaz/enigma/Constants.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.py b/build.py index c3f061a5..919d99db 100644 --- a/build.py +++ b/build.py @@ -18,8 +18,8 @@ import ssjb import ssjb.ivy -ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.9b") -ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.9b") +ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.10b") +ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.10b") # dependencies ExtraRepos = [ diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 8910a960..5c1fd6be 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -12,7 +12,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.9 beta"; + public static final String Version = "0.10 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024 * 1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte -- cgit v1.2.3 -- cgit v1.2.3 From bfb3fad7e65ce5ebfa349c0ad3c2da49d9b1f4cc Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 18 Mar 2015 23:38:11 -0400 Subject: translate class references hidden inside type parameters --- src/cuchaz/enigma/bytecode/ClassRenamer.java | 102 +++++++++++++++++++-------- 1 file changed, 71 insertions(+), 31 deletions(-) diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index 8d25e722..e708b9d4 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -39,6 +39,7 @@ import javassist.bytecode.SignatureAttribute.NestedClassType; import javassist.bytecode.SignatureAttribute.ObjectType; import javassist.bytecode.SignatureAttribute.Type; import javassist.bytecode.SignatureAttribute.TypeArgument; +import javassist.bytecode.SignatureAttribute.TypeParameter; import javassist.bytecode.SignatureAttribute.TypeVariable; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassNameReplacer; @@ -164,9 +165,9 @@ public class ClassRenamer { // rename the class name itself last // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass() // we only want to replace exactly this class name - String newName = replacer.replace(Descriptor.toJvmName(c.getName())); + String newName = renameClassName(c.getName(), map); if (newName != null) { - c.setName(Descriptor.toJavaName(newName)); + c.setName(newName); } // replace simple names in the InnerClasses attribute too @@ -284,7 +285,11 @@ public class ClassRenamer { private static String renameClassSignature(String signature, ReplacerClassMap map) { try { - return getSignature(renameType(SignatureAttribute.toClassSignature(signature), map)); + ClassSignature type = renameType(SignatureAttribute.toClassSignature(signature), map); + if (type != null) { + return type.encode(); + } + return null; } catch (BadBytecode ex) { throw new Error("Can't parse field signature: " + signature); } @@ -292,7 +297,11 @@ public class ClassRenamer { private static String renameFieldSignature(String signature, ReplacerClassMap map) { try { - return getSignature(renameType(SignatureAttribute.toFieldSignature(signature), map)); + ObjectType type = renameType(SignatureAttribute.toFieldSignature(signature), map); + if (type != null) { + return type.encode(); + } + return null; } catch (BadBytecode ex) { throw new Error("Can't parse class signature: " + signature); } @@ -300,7 +309,11 @@ public class ClassRenamer { private static String renameMethodSignature(String signature, ReplacerClassMap map) { try { - return getSignature(renameType(SignatureAttribute.toMethodSignature(signature), map)); + MethodSignature type = renameType(SignatureAttribute.toMethodSignature(signature), map); + if (type != null) { + return type.encode(); + } + return null; } catch (BadBytecode ex) { throw new Error("Can't parse method signature: " + signature); } @@ -308,9 +321,17 @@ public class ClassRenamer { private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) { - // NOTE: don't have to translate type parameters + TypeParameter[] typeParamTypes = type.getParameters(); + if (typeParamTypes != null) { + typeParamTypes = Arrays.copyOf(typeParamTypes, typeParamTypes.length); + for (int i=0; i names) { + // add the names to the class const pool ConstPool constPool = info.getConstPool(); List parameterNameIndices = new ArrayList(); @@ -44,7 +45,7 @@ public class MethodParametersAttribute extends AttributeInfo { private static byte[] writeStruct(List parameterNameIndices) { // JVM 8 Spec says the struct looks like this: - // http://cr.openjdk.java.net/~mr/se/8/java-se-8-fr-spec-01/java-se-8-jvms-fr-diffs.pdf + // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24 // uint8 num_params // for each param: // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry -- cgit v1.2.3 From f8e16c88898300254b0bc48220521b1a087f0400 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 19 Mar 2015 02:21:08 -0400 Subject: package for v0.10.1 beta --- build.py | 4 ++-- src/cuchaz/enigma/Constants.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.py b/build.py index 919d99db..8d6d447c 100644 --- a/build.py +++ b/build.py @@ -18,8 +18,8 @@ import ssjb import ssjb.ivy -ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.10b") -ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.10b") +ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.10.1b") +ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.10.1b") # dependencies ExtraRepos = [ diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 5c1fd6be..5f3a78d0 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -12,7 +12,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.10 beta"; + public static final String Version = "0.10.1 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024 * 1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte -- cgit v1.2.3 -- cgit v1.2.3 From cef6b12b64db962d02a8e19f164c0b37bb250e10 Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 20 Mar 2015 12:17:07 -0400 Subject: remove debug spam --- src/cuchaz/enigma/bytecode/MethodParameterWriter.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java index 87c9196d..17e4a884 100644 --- a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -64,9 +64,6 @@ public class MethodParameterWriter { } // save the mappings to the class - for (String name : names) { - System.out.println("\t" + name); - } MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); } } -- cgit v1.2.3 From 20c484871c568ae0dac8ead1cf4380c33a67becf Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 20 Mar 2015 12:33:31 -0400 Subject: repackage as v0.10.2 beta --- build.py | 5 +++-- src/cuchaz/enigma/Constants.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build.py b/build.py index 8d6d447c..10c52994 100644 --- a/build.py +++ b/build.py @@ -5,6 +5,7 @@ import sys # settings PathSsjb = "../ssjb" Author = "Cuchaz" +Version = "0.10.2b" DirBin = "bin" DirLib = "lib" @@ -18,8 +19,8 @@ import ssjb import ssjb.ivy -ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:0.10.1b") -ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:0.10.1b") +ArtifactStandalone = ssjb.ivy.Dep("cuchaz:enigma:%s" % Version) +ArtifactLib = ssjb.ivy.Dep("cuchaz:enigma-lib:%s" % Version) # dependencies ExtraRepos = [ diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index 5f3a78d0..ba976b4f 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -12,7 +12,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.10.1 beta"; + public static final String Version = "0.10.2 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024 * 1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte -- cgit v1.2.3 From b7c91d19660ef4a36b0ad3edae81575fb57c39fe Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 20 Mar 2015 12:40:07 -0400 Subject: get rid of stupid fucking com.beust.jcommander imports crashing all my downstream apps Eclipse loves to import that particular collections implementation for some reason and I don't want to use it!! --- src/cuchaz/enigma/TranslatingTypeLoader.java | 2 +- src/cuchaz/enigma/analysis/RelatedMethodChecker.java | 2 +- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 5 ++--- src/cuchaz/enigma/convert/ClassIdentifier.java | 5 ++--- src/cuchaz/enigma/convert/MappingsConverter.java | 2 +- src/cuchaz/enigma/convert/MatchesReader.java | 2 +- src/cuchaz/enigma/gui/ClassMatchingGui.java | 4 ++-- src/cuchaz/enigma/gui/MemberMatchingGui.java | 4 ++-- src/cuchaz/enigma/mapping/ClassEntry.java | 2 +- src/cuchaz/enigma/mapping/Mappings.java | 2 +- src/cuchaz/enigma/mapping/MappingsChecker.java | 2 +- src/cuchaz/enigma/mapping/Signature.java | 2 +- src/cuchaz/enigma/mapping/Translator.java | 2 +- 13 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index dce4b859..184ba263 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -25,7 +25,7 @@ import javassist.CtClass; import javassist.NotFoundException; import javassist.bytecode.Descriptor; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.strobel.assembler.metadata.Buffer; import com.strobel.assembler.metadata.ClasspathTypeLoader; diff --git a/src/cuchaz/enigma/analysis/RelatedMethodChecker.java b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java index 5bd67a0a..e231b807 100644 --- a/src/cuchaz/enigma/analysis/RelatedMethodChecker.java +++ b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java @@ -3,7 +3,7 @@ package cuchaz.enigma.analysis; import java.util.Map; import java.util.Set; -import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import cuchaz.enigma.mapping.BehaviorEntry; diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index bb643158..0c11b71b 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -13,13 +13,12 @@ package cuchaz.enigma.bytecode; import java.util.Collection; import java.util.List; +import com.google.common.collect.Lists; + import javassist.CtClass; import javassist.bytecode.ConstPool; import javassist.bytecode.EnclosingMethodAttribute; import javassist.bytecode.InnerClassesAttribute; - -import com.beust.jcommander.internal.Lists; - import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; diff --git a/src/cuchaz/enigma/convert/ClassIdentifier.java b/src/cuchaz/enigma/convert/ClassIdentifier.java index da799cd0..2e6217b3 100644 --- a/src/cuchaz/enigma/convert/ClassIdentifier.java +++ b/src/cuchaz/enigma/convert/ClassIdentifier.java @@ -3,10 +3,9 @@ package cuchaz.enigma.convert; import java.util.Map; import java.util.jar.JarFile; -import javassist.CtClass; - -import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Maps; +import javassist.CtClass; import cuchaz.enigma.TranslatingTypeLoader; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index 2afa1208..b3ac1cf4 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -20,9 +20,9 @@ import java.util.Map; import java.util.Set; import java.util.jar.JarFile; -import com.beust.jcommander.internal.Lists; import com.google.common.collect.BiMap; import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; diff --git a/src/cuchaz/enigma/convert/MatchesReader.java b/src/cuchaz/enigma/convert/MatchesReader.java index dac2f057..7c823054 100644 --- a/src/cuchaz/enigma/convert/MatchesReader.java +++ b/src/cuchaz/enigma/convert/MatchesReader.java @@ -7,7 +7,7 @@ import java.io.IOException; import java.util.Collection; import java.util.List; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; diff --git a/src/cuchaz/enigma/gui/ClassMatchingGui.java b/src/cuchaz/enigma/gui/ClassMatchingGui.java index 9e210ec4..6f8feac3 100644 --- a/src/cuchaz/enigma/gui/ClassMatchingGui.java +++ b/src/cuchaz/enigma/gui/ClassMatchingGui.java @@ -23,9 +23,9 @@ import javax.swing.JSplitPane; import javax.swing.SwingConstants; import javax.swing.WindowConstants; -import com.beust.jcommander.internal.Lists; -import com.beust.jcommander.internal.Maps; import com.google.common.collect.BiMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import cuchaz.enigma.Constants; import cuchaz.enigma.Deobfuscator; diff --git a/src/cuchaz/enigma/gui/MemberMatchingGui.java b/src/cuchaz/enigma/gui/MemberMatchingGui.java index 181fb548..65e6dd9e 100644 --- a/src/cuchaz/enigma/gui/MemberMatchingGui.java +++ b/src/cuchaz/enigma/gui/MemberMatchingGui.java @@ -24,8 +24,8 @@ import javax.swing.JSplitPane; import javax.swing.WindowConstants; import javax.swing.text.Highlighter.HighlightPainter; -import com.beust.jcommander.internal.Lists; -import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import cuchaz.enigma.Constants; import cuchaz.enigma.Deobfuscator; diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index e6400b8c..4d977b1b 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -13,7 +13,7 @@ package cuchaz.enigma.mapping; import java.io.Serializable; import java.util.List; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; public class ClassEntry implements Entry, Serializable { diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 659d23ac..d802139d 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -17,7 +17,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; diff --git a/src/cuchaz/enigma/mapping/MappingsChecker.java b/src/cuchaz/enigma/mapping/MappingsChecker.java index 57ea90ce..b9feee4e 100644 --- a/src/cuchaz/enigma/mapping/MappingsChecker.java +++ b/src/cuchaz/enigma/mapping/MappingsChecker.java @@ -2,8 +2,8 @@ package cuchaz.enigma.mapping; import java.util.Map; -import com.beust.jcommander.internal.Maps; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.analysis.RelatedMethodChecker; diff --git a/src/cuchaz/enigma/mapping/Signature.java b/src/cuchaz/enigma/mapping/Signature.java index f4850ac3..d1e89b20 100644 --- a/src/cuchaz/enigma/mapping/Signature.java +++ b/src/cuchaz/enigma/mapping/Signature.java @@ -3,7 +3,7 @@ package cuchaz.enigma.mapping; import java.io.Serializable; import java.util.List; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import cuchaz.enigma.Util; diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index fb187b90..3ceeea11 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -13,7 +13,7 @@ package cuchaz.enigma.mapping; import java.util.List; import java.util.Map; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import cuchaz.enigma.analysis.TranslationIndex; -- cgit v1.2.3 From 03a4d2f0d47dfdce209de58f0530b17649c5b19b Mon Sep 17 00:00:00 2001 From: jeff Date: Fri, 20 Mar 2015 12:42:04 -0400 Subject: make sure LVTT names explicitly match the LVT names --- .../enigma/bytecode/LocalVariableRenamer.java | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/cuchaz/enigma/bytecode/LocalVariableRenamer.java b/src/cuchaz/enigma/bytecode/LocalVariableRenamer.java index 1179560a..215afc5a 100644 --- a/src/cuchaz/enigma/bytecode/LocalVariableRenamer.java +++ b/src/cuchaz/enigma/bytecode/LocalVariableRenamer.java @@ -35,12 +35,12 @@ public class LocalVariableRenamer { LocalVariableAttribute table = (LocalVariableAttribute)codeAttribute.getAttribute(LocalVariableAttribute.tag); if (table != null) { - renameTable(behaviorEntry, constants, table); + renameLVT(behaviorEntry, constants, table); } LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute)codeAttribute.getAttribute(LocalVariableAttribute.typeTag); if (typeTable != null) { - renameTable(behaviorEntry, constants, typeTable); + renameLVTT(typeTable, table); } } } @@ -55,7 +55,7 @@ public class LocalVariableRenamer { } } - private void renameTable(BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table) { + private void renameLVT(BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table) { // skip empty tables if (table.tableLength() <= 0) { @@ -90,8 +90,24 @@ public class LocalVariableRenamer { } } + private void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) { + // rename args to the same names as in the LVT + for (int i=0; i")) { return new ConstructorEntry(classEntry, behaviorSignature); -- cgit v1.2.3 From 73b17a9e3079a5a40de3dd8f9849417a3414feea Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 22 Mar 2015 14:25:34 -0400 Subject: fix NPE in the gui with static initializers --- src/cuchaz/enigma/gui/Gui.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index ea05d255..27b06f97 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -839,7 +839,9 @@ public class Gui { private void showConstructorEntry(ConstructorEntry entry) { addNameValue(m_infoPanel, "Constructor", entry.getClassEntry().getName()); - addNameValue(m_infoPanel, "Signature", entry.getSignature().toString()); + if (!entry.isStatic()) { + addNameValue(m_infoPanel, "Signature", entry.getSignature().toString()); + } } private void showArgumentEntry(ArgumentEntry entry) { -- cgit v1.2.3 -- cgit v1.2.3 From 106cb7a2a2e980d596dcfaa5d331ae8b933b6f48 Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Mon, 23 Mar 2015 16:26:11 -0400 Subject: skip writing InnerClasses attribute if there's already one there just translate the existing one --- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 0c11b71b..9cdfd685 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -16,6 +16,7 @@ import java.util.List; import com.google.common.collect.Lists; import javassist.CtClass; +import javassist.bytecode.AccessFlag; import javassist.bytecode.ConstPool; import javassist.bytecode.EnclosingMethodAttribute; import javassist.bytecode.InnerClassesAttribute; @@ -34,6 +35,13 @@ public class InnerClassWriter { public void write(CtClass c) { + // don't change anything if there's already an attribute there + InnerClassesAttribute oldAttr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (oldAttr != null) { + // bail! + return; + } + ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); List obfClassChain = m_index.getObfClassChain(obfClassEntry); @@ -102,7 +110,8 @@ public class InnerClassWriter { int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); int innerClassNameIndex = 0; - int accessFlags = 0; + int accessFlags = AccessFlag.PUBLIC; + // TODO: need to figure out if we can put static or not if (!m_index.isAnonymousClass(obfClassEntry)) { innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName()); } -- cgit v1.2.3 From 924d6aa0df17bc4e2396ecaabe92367e8fabdcc4 Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Mon, 23 Mar 2015 16:26:30 -0400 Subject: repackage for 0.10.3 beta --- build.py | 2 +- src/cuchaz/enigma/Constants.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.py b/build.py index 10c52994..050bb2a1 100644 --- a/build.py +++ b/build.py @@ -5,7 +5,7 @@ import sys # settings PathSsjb = "../ssjb" Author = "Cuchaz" -Version = "0.10.2b" +Version = "0.10.3b" DirBin = "bin" DirLib = "lib" diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index ba976b4f..5088bc20 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -12,7 +12,7 @@ package cuchaz.enigma; public class Constants { public static final String Name = "Enigma"; - public static final String Version = "0.10.2 beta"; + public static final String Version = "0.10.3 beta"; public static final String Url = "http://www.cuchazinteractive.com/enigma"; public static final int MiB = 1024 * 1024; // 1 mebibyte public static final int KiB = 1024; // 1 kebibyte -- cgit v1.2.3 From 902b1b519f8ae89910f6f0d9f077738711349324 Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Mon, 23 Mar 2015 16:26:44 -0400 Subject: add protectifier! Muwahahaha!!! --- src/cuchaz/enigma/CommandMain.java | 13 ++++++- src/cuchaz/enigma/Deobfuscator.java | 32 ++++++++++++++++++ src/cuchaz/enigma/bytecode/ClassProtectifier.java | 41 +++++++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/cuchaz/enigma/bytecode/ClassProtectifier.java diff --git a/src/cuchaz/enigma/CommandMain.java b/src/cuchaz/enigma/CommandMain.java index 0253a92f..74c01913 100644 --- a/src/cuchaz/enigma/CommandMain.java +++ b/src/cuchaz/enigma/CommandMain.java @@ -54,8 +54,10 @@ public class CommandMain { String command = getArg(args, 0, "command", true); if (command.equalsIgnoreCase("deobfuscate")) { deobfuscate(args); - } else if(command.equalsIgnoreCase("decompile")) { + } else if (command.equalsIgnoreCase("decompile")) { decompile(args); + } else if (command.equalsIgnoreCase("protectify")) { + protectify(args); } else { throw new IllegalArgumentException("Command not recognized: " + command); } @@ -72,6 +74,7 @@ public class CommandMain { System.out.println("\twhere is one of:"); System.out.println("\t\tdeobfuscate []"); System.out.println("\t\tdecompile []"); + System.out.println("\t\tprotectify "); } private static void decompile(String[] args) @@ -92,6 +95,14 @@ public class CommandMain { deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); } + private static void protectify(String[] args) + throws Exception { + File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); + File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); + Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn)); + deobfuscator.protectifyJar(fileJarOut, new ConsoleProgressListener()); + } + private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) throws Exception { System.out.println("Reading jar..."); diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index b63f1639..82ce6a2b 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -45,6 +45,7 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.bytecode.ClassProtectifier; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; @@ -357,6 +358,37 @@ public class Deobfuscator { } } + public void protectifyJar(File out, ProgressListener progress) { + try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { + if (progress != null) { + progress.init(JarClassIterator.getClassEntries(m_jar).size(), "Protectifying classes..."); + } + + int i = 0; + for (CtClass c : JarClassIterator.classes(m_jar)) { + if (progress != null) { + progress.onProgress(i++, c.getName()); + } + + try { + c = ClassProtectifier.protectify(c); + outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); + outJar.write(c.toBytecode()); + outJar.closeEntry(); + } catch (Throwable t) { + throw new Error("Unable to protectify class " + c.getName(), t); + } + } + if (progress != null) { + progress.onProgress(i, "Done!"); + } + + outJar.close(); + } catch (IOException ex) { + throw new Error("Unable to write to Jar file!"); + } + } + public T obfuscateEntry(T deobfEntry) { if (deobfEntry == null) { return null; diff --git a/src/cuchaz/enigma/bytecode/ClassProtectifier.java b/src/cuchaz/enigma/bytecode/ClassProtectifier.java new file mode 100644 index 00000000..49a62690 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/ClassProtectifier.java @@ -0,0 +1,41 @@ +package cuchaz.enigma.bytecode; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtField; +import javassist.bytecode.AccessFlag; +import javassist.bytecode.InnerClassesAttribute; + + +public class ClassProtectifier { + + public static CtClass protectify(CtClass c) { + + // protectify all the fields + for (CtField field : c.getDeclaredFields()) { + field.setModifiers(protectify(field.getModifiers())); + } + + // protectify all the methods and constructors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + behavior.setModifiers(protectify(behavior.getModifiers())); + } + + // protectify all the inner classes + InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (attr != null) { + for (int i=0; i T translateEntry(T entry) { if (entry instanceof ClassEntry) { -- cgit v1.2.3 From 28868f3fa73a1ad364ca3111323c563573e03821 Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Sat, 28 Mar 2015 10:45:26 -0400 Subject: fix source file attribute --- src/cuchaz/enigma/bytecode/ClassTranslator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 32518d9c..074da2bb 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -150,7 +150,7 @@ public class ClassTranslator { // translate the source file attribute too ClassEntry deobfClassEntry = m_translator.translateEntry(classEntry); if (deobfClassEntry != null) { - String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassName()) + ".java"; + String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java"; c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); } } -- cgit v1.2.3 From ee1532fb813b1bbc7bea11daed08526aca611a4b Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Sat, 28 Mar 2015 16:45:47 -0400 Subject: show bytecode line numbers for debugging --- src/cuchaz/enigma/Deobfuscator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 82ce6a2b..a7d8a671 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -88,6 +88,7 @@ public class Deobfuscator { m_settings.setMergeVariables(true); m_settings.setForceExplicitImports(true); m_settings.setForceExplicitTypeArguments(true); + m_settings.setShowDebugLineNumbers(true); // DEBUG //m_settings.setShowSyntheticMembers(true); -- cgit v1.2.3 From 698f4d025e0b9ee5bbd1bd701cc46c340b8b4d9c Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Sat, 28 Mar 2015 19:12:53 -0400 Subject: add methods for better runtime obfuscation in M3L --- src/cuchaz/enigma/analysis/TranslationIndex.java | 26 +++++++++++++++--------- src/cuchaz/enigma/mapping/Translator.java | 4 ++++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index 8651ebd6..bd77344c 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -84,6 +84,10 @@ public class TranslationIndex implements Serializable { } public void indexClass(CtClass c) { + indexClass(c, true); + } + + public void indexClass(CtClass c, boolean indexMembers) { ClassEntry classEntry = EntryFactory.getClassEntry(c); @@ -93,16 +97,18 @@ public class TranslationIndex implements Serializable { m_superclasses.put(classEntry, superclassEntry); } - // add fields - for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); - m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); - } - - // add behaviors - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); + if (indexMembers) { + // add fields + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); + m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); + } + + // add behaviors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); + } } } diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index dcb7435f..5d174947 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -43,6 +43,10 @@ public class Translator { m_index = index; } + public TranslationDirection getDirection() { + return m_direction; + } + public TranslationIndex getTranslationIndex() { return m_index; } -- cgit v1.2.3 From 3b57f50a1d9429966e7aced0b81b3b3d4cfa41c7 Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Mon, 30 Mar 2015 11:40:24 -0400 Subject: fix unintentional compile time transitive dependency on procyon --- .../analysis/SourceIndexBehaviorVisitor.java | 4 +- .../enigma/analysis/SourceIndexClassVisitor.java | 10 ++--- src/cuchaz/enigma/mapping/EntryFactory.java | 40 ------------------- src/cuchaz/enigma/mapping/ProcyonEntryFactory.java | 45 ++++++++++++++++++++++ 4 files changed, 52 insertions(+), 47 deletions(-) create mode 100644 src/cuchaz/enigma/mapping/ProcyonEntryFactory.java diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index eb120b66..3a176ffc 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -31,9 +31,9 @@ import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; -import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.ProcyonEntryFactory; import cuchaz.enigma.mapping.Signature; import cuchaz.enigma.mapping.Type; @@ -113,7 +113,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); if (def.getMethod() instanceof MethodDefinition) { MethodDefinition methodDef = (MethodDefinition)def.getMethod(); - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(methodDef); + BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(methodDef); ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); index.addDeclaration(node.getNameToken(), argumentEntry); } diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index f4202b5b..37a893d7 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -28,8 +28,8 @@ import com.strobel.decompiler.languages.java.ast.VariableInitializer; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; -import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.ProcyonEntryFactory; public class SourceIndexClassVisitor extends SourceIndexVisitor { @@ -67,7 +67,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(def); + BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def); AstNode tokenNode = node.getNameToken(); if (behaviorEntry instanceof ConstructorEntry) { @@ -84,7 +84,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); - ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(def); + ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def); index.addDeclaration(node.getNameToken(), constructorEntry); return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); } @@ -92,7 +92,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); - FieldEntry fieldEntry = EntryFactory.getFieldEntry(def); + FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); assert (node.getVariables().size() == 1); VariableInitializer variable = node.getVariables().firstOrNullObject(); index.addDeclaration(variable.getNameToken(), fieldEntry); @@ -104,7 +104,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { // treat enum declarations as field declarations FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); - FieldEntry fieldEntry = EntryFactory.getFieldEntry(def); + FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); index.addDeclaration(node.getNameToken(), fieldEntry); return recurse(node, index); diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index 69c1630f..af96eb35 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java @@ -11,9 +11,6 @@ import javassist.expr.FieldAccess; import javassist.expr.MethodCall; import javassist.expr.NewExpr; -import com.strobel.assembler.metadata.FieldDefinition; -import com.strobel.assembler.metadata.MethodDefinition; - import cuchaz.enigma.analysis.JarIndex; public class EntryFactory { @@ -55,14 +52,6 @@ public class EntryFactory { ); } - public static FieldEntry getFieldEntry(FieldDefinition def) { - return new FieldEntry( - new ClassEntry(def.getDeclaringType().getInternalName()), - def.getName(), - new Type(def.getErasedSignature()) - ); - } - public static FieldEntry getFieldEntry(String className, String name, String type) { return new FieldEntry(new ClassEntry(className), name, new Type(type)); } @@ -91,14 +80,6 @@ public class EntryFactory { ); } - public static MethodEntry getMethodEntry(MethodDefinition def) { - return new MethodEntry( - new ClassEntry(def.getDeclaringType().getInternalName()), - def.getName(), - new Signature(def.getErasedSignature()) - ); - } - public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { if (constructor.isClassInitializer()) { return new ConstructorEntry( @@ -126,19 +107,6 @@ public class EntryFactory { ); } - public static ConstructorEntry getConstructorEntry(MethodDefinition def) { - if (def.isTypeInitializer()) { - return new ConstructorEntry( - new ClassEntry(def.getDeclaringType().getInternalName()) - ); - } else { - return new ConstructorEntry( - new ClassEntry(def.getDeclaringType().getInternalName()), - new Signature(def.getErasedSignature()) - ); - } - } - public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { if (behavior instanceof CtMethod) { return getMethodEntry((CtMethod)behavior); @@ -178,14 +146,6 @@ public class EntryFactory { } } - public static BehaviorEntry getBehaviorEntry(MethodDefinition def) { - if (def.isConstructor() || def.isTypeInitializer()) { - return getConstructorEntry(def); - } else { - return getMethodEntry(def); - } - } - public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); } diff --git a/src/cuchaz/enigma/mapping/ProcyonEntryFactory.java b/src/cuchaz/enigma/mapping/ProcyonEntryFactory.java new file mode 100644 index 00000000..eb0563c2 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ProcyonEntryFactory.java @@ -0,0 +1,45 @@ +package cuchaz.enigma.mapping; + +import com.strobel.assembler.metadata.FieldDefinition; +import com.strobel.assembler.metadata.MethodDefinition; + + +public class ProcyonEntryFactory { + + public static FieldEntry getFieldEntry(FieldDefinition def) { + return new FieldEntry( + new ClassEntry(def.getDeclaringType().getInternalName()), + def.getName(), + new Type(def.getErasedSignature()) + ); + } + + public static MethodEntry getMethodEntry(MethodDefinition def) { + return new MethodEntry( + new ClassEntry(def.getDeclaringType().getInternalName()), + def.getName(), + new Signature(def.getErasedSignature()) + ); + } + + public static ConstructorEntry getConstructorEntry(MethodDefinition def) { + if (def.isTypeInitializer()) { + return new ConstructorEntry( + new ClassEntry(def.getDeclaringType().getInternalName()) + ); + } else { + return new ConstructorEntry( + new ClassEntry(def.getDeclaringType().getInternalName()), + new Signature(def.getErasedSignature()) + ); + } + } + + public static BehaviorEntry getBehaviorEntry(MethodDefinition def) { + if (def.isConstructor() || def.isTypeInitializer()) { + return getConstructorEntry(def); + } else { + return getMethodEntry(def); + } + } +} -- cgit v1.2.3 From 22cac8f1a25c7d34a94bc5e00c56c0532509071f Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Mon, 30 Mar 2015 22:00:54 -0400 Subject: add publifier --- src/cuchaz/enigma/CommandMain.java | 10 ++++ src/cuchaz/enigma/Deobfuscator.java | 75 +++++++++++++------------- src/cuchaz/enigma/bytecode/ClassPublifier.java | 41 ++++++++++++++ 3 files changed, 89 insertions(+), 37 deletions(-) create mode 100644 src/cuchaz/enigma/bytecode/ClassPublifier.java diff --git a/src/cuchaz/enigma/CommandMain.java b/src/cuchaz/enigma/CommandMain.java index 74c01913..41528fef 100644 --- a/src/cuchaz/enigma/CommandMain.java +++ b/src/cuchaz/enigma/CommandMain.java @@ -58,6 +58,8 @@ public class CommandMain { decompile(args); } else if (command.equalsIgnoreCase("protectify")) { protectify(args); + } else if (command.equalsIgnoreCase("publify")) { + publify(args); } else { throw new IllegalArgumentException("Command not recognized: " + command); } @@ -103,6 +105,14 @@ public class CommandMain { deobfuscator.protectifyJar(fileJarOut, new ConsoleProgressListener()); } + private static void publify(String[] args) + throws Exception { + File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); + File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); + Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn)); + deobfuscator.publifyJar(fileJarOut, new ConsoleProgressListener()); + } + private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) throws Exception { System.out.println("Reading jar..."); diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index a7d8a671..d9ded878 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -46,6 +46,7 @@ import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.bytecode.ClassProtectifier; +import cuchaz.enigma.bytecode.ClassPublifier; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; @@ -321,48 +322,48 @@ public class Deobfuscator { } public void writeJar(File out, ProgressListener progress) { - try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { - if (progress != null) { - progress.init(JarClassIterator.getClassEntries(m_jar).size(), "Translating classes..."); + final TranslatingTypeLoader loader = new TranslatingTypeLoader( + m_jar, + m_jarIndex, + getTranslator(TranslationDirection.Obfuscating), + getTranslator(TranslationDirection.Deobfuscating) + ); + transformJar(out, progress, new ClassTransformer() { + + @Override + public CtClass transform(CtClass c) throws Exception { + return loader.transformClass(c); } - - // prep the loader - TranslatingTypeLoader loader = new TranslatingTypeLoader( - m_jar, - m_jarIndex, - getTranslator(TranslationDirection.Obfuscating), - getTranslator(TranslationDirection.Deobfuscating) - ); - - int i = 0; - for (CtClass c : JarClassIterator.classes(m_jar)) { - if (progress != null) { - progress.onProgress(i++, c.getName()); - } - - try { - c = loader.transformClass(c); - outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); - outJar.write(c.toBytecode()); - outJar.closeEntry(); - } catch (Throwable t) { - throw new Error("Unable to deobfuscate class " + c.getName(), t); - } + }); + } + + public void protectifyJar(File out, ProgressListener progress) { + transformJar(out, progress, new ClassTransformer() { + + @Override + public CtClass transform(CtClass c) throws Exception { + return ClassProtectifier.protectify(c); } - if (progress != null) { - progress.onProgress(i, "Done!"); + }); + } + + public void publifyJar(File out, ProgressListener progress) { + transformJar(out, progress, new ClassTransformer() { + + @Override + public CtClass transform(CtClass c) throws Exception { + return ClassPublifier.publify(c); } - - outJar.close(); - } catch (IOException ex) { - throw new Error("Unable to write to Jar file!"); - } + }); } - public void protectifyJar(File out, ProgressListener progress) { + private interface ClassTransformer { + public CtClass transform(CtClass c) throws Exception; + } + private void transformJar(File out, ProgressListener progress, ClassTransformer transformer) { try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { if (progress != null) { - progress.init(JarClassIterator.getClassEntries(m_jar).size(), "Protectifying classes..."); + progress.init(JarClassIterator.getClassEntries(m_jar).size(), "Transforming classes..."); } int i = 0; @@ -372,12 +373,12 @@ public class Deobfuscator { } try { - c = ClassProtectifier.protectify(c); + c = transformer.transform(c); outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); outJar.write(c.toBytecode()); outJar.closeEntry(); } catch (Throwable t) { - throw new Error("Unable to protectify class " + c.getName(), t); + throw new Error("Unable to transform class " + c.getName(), t); } } if (progress != null) { diff --git a/src/cuchaz/enigma/bytecode/ClassPublifier.java b/src/cuchaz/enigma/bytecode/ClassPublifier.java new file mode 100644 index 00000000..d8162e46 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/ClassPublifier.java @@ -0,0 +1,41 @@ +package cuchaz.enigma.bytecode; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtField; +import javassist.bytecode.AccessFlag; +import javassist.bytecode.InnerClassesAttribute; + + +public class ClassPublifier { + + public static CtClass publify(CtClass c) { + + // publify all the fields + for (CtField field : c.getDeclaredFields()) { + field.setModifiers(publify(field.getModifiers())); + } + + // publify all the methods and constructors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + behavior.setModifiers(publify(behavior.getModifiers())); + } + + // publify all the inner classes + InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (attr != null) { + for (int i=0; i m_obfClassEntries; private TranslationIndex m_translationIndex; - private Multimap m_interfaces; private Map m_access; private Multimap m_fields; private Multimap m_behaviors; @@ -74,7 +73,6 @@ public class JarIndex { public JarIndex() { m_obfClassEntries = Sets.newHashSet(); m_translationIndex = new TranslationIndex(); - m_interfaces = HashMultimap.create(); m_access = Maps.newHashMap(); m_fields = HashMultimap.create(); m_behaviors = HashMultimap.create(); @@ -124,7 +122,6 @@ public class JarIndex { if (className.equals(interfaceName)) { throw new IllegalArgumentException("Class cannot be its own interface! " + className); } - m_interfaces.put(className, interfaceName); } for (CtBehavior behavior : c.getDeclaredBehaviors()) { indexBehavior(behavior); @@ -176,7 +173,6 @@ public class JarIndex { } EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); m_translationIndex.renameClasses(renames); - EntryRenamer.renameClassesInMultimap(renames, m_interfaces); EntryRenamer.renameClassesInMultimap(renames, m_methodImplementations); EntryRenamer.renameClassesInMultimap(renames, m_behaviorReferences); EntryRenamer.renameClassesInMultimap(renames, m_fieldReferences); @@ -637,11 +633,11 @@ public class JarIndex { interfaceMethodEntries.add(obfMethodEntry); } else { // get the interface class - for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) { + for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { // is this method defined in this interface? MethodEntry methodInterface = new MethodEntry( - new ClassEntry(interfaceName), + interfaceEntry, obfMethodEntry.getName(), obfMethodEntry.getSignature() ); @@ -745,31 +741,33 @@ public class JarIndex { return m_anonymousClasses.get(obfInnerClassName); } - public Set getInterfaces(String className) { - Set interfaceNames = new HashSet(); - interfaceNames.addAll(m_interfaces.get(className)); - for (ClassEntry ancestor : m_translationIndex.getAncestry(new ClassEntry(className))) { - interfaceNames.addAll(m_interfaces.get(ancestor.getName())); + public Set getInterfaces(String className) { + ClassEntry classEntry = new ClassEntry(className); + Set interfaces = new HashSet(); + interfaces.addAll(m_translationIndex.getInterfaces(classEntry)); + for (ClassEntry ancestor : m_translationIndex.getAncestry(classEntry)) { + interfaces.addAll(m_translationIndex.getInterfaces(ancestor)); } - return interfaceNames; + return interfaces; } public Set getImplementingClasses(String targetInterfaceName) { + // linear search is fast enough for now Set classNames = Sets.newHashSet(); - for (Map.Entry entry : m_interfaces.entries()) { - String className = entry.getKey(); - String interfaceName = entry.getValue(); - if (interfaceName.equals(targetInterfaceName)) { - classNames.add(className); - m_translationIndex.getSubclassNamesRecursively(classNames, new ClassEntry(className)); + for (Map.Entry entry : m_translationIndex.getClassInterfaces()) { + ClassEntry classEntry = entry.getKey(); + ClassEntry interfaceEntry = entry.getValue(); + if (interfaceEntry.getName().equals(targetInterfaceName)) { + classNames.add(classEntry.getClassName()); + m_translationIndex.getSubclassNamesRecursively(classNames, classEntry); } } return classNames; } public boolean isInterface(String className) { - return m_interfaces.containsValue(className); + return m_translationIndex.isInterface(new ClassEntry(className)); } public boolean containsObfClass(ClassEntry obfClassEntry) { diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index bd77344c..e0e66bf7 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -16,6 +16,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,6 +27,7 @@ import java.util.zip.GZIPOutputStream; import javassist.CtBehavior; import javassist.CtClass; import javassist.CtField; +import javassist.bytecode.Descriptor; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; @@ -36,8 +38,8 @@ import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; -import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Translator; public class TranslationIndex implements Serializable { @@ -47,11 +49,13 @@ public class TranslationIndex implements Serializable { private Map m_superclasses; private Multimap m_fieldEntries; private Multimap m_behaviorEntries; + private Multimap m_interfaces; public TranslationIndex() { m_superclasses = Maps.newHashMap(); m_fieldEntries = HashMultimap.create(); m_behaviorEntries = HashMultimap.create(); + m_interfaces = HashMultimap.create(); } public TranslationIndex(TranslationIndex other, Translator translator) { @@ -65,6 +69,15 @@ public class TranslationIndex implements Serializable { ); } + // translate the interfaces + m_interfaces = HashMultimap.create(); + for (Map.Entry mapEntry : other.m_interfaces.entries()) { + m_interfaces.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + // translate the fields m_fieldEntries = HashMultimap.create(); for (Map.Entry mapEntry : other.m_fieldEntries.entries()) { @@ -90,13 +103,24 @@ public class TranslationIndex implements Serializable { public void indexClass(CtClass c, boolean indexMembers) { ClassEntry classEntry = EntryFactory.getClassEntry(c); + if (isJre(classEntry)) { + return; + } // add the superclass ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); - if (!isJre(classEntry) && superclassEntry != null && !isJre(superclassEntry)) { + if (superclassEntry != null && !isJre(superclassEntry)) { m_superclasses.put(classEntry, superclassEntry); } + // add the interfaces + for (String interfaceClassName : c.getClassFile().getInterfaces()) { + ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); + if (!isJre(interfaceClassEntry)) { + m_interfaces.put(classEntry, interfaceClassEntry); + } + } + if (indexMembers) { // add fields for (CtField field : c.getDeclaredFields()) { @@ -134,6 +158,7 @@ public class TranslationIndex implements Serializable { } public List getSubclass(ClassEntry classEntry) { + // linear search is fast enough for now List subclasses = Lists.newArrayList(); for (Map.Entry entry : m_superclasses.entrySet()) { @@ -160,6 +185,18 @@ public class TranslationIndex implements Serializable { } } + public Collection> getClassInterfaces() { + return m_interfaces.entries(); + } + + public Collection getInterfaces(ClassEntry classEntry) { + return m_interfaces.get(classEntry); + } + + public boolean isInterface(ClassEntry classEntry) { + return m_interfaces.containsValue(classEntry); + } + public boolean entryExists(Entry entry) { if (entry instanceof FieldEntry) { return fieldExists((FieldEntry)entry); @@ -185,6 +222,21 @@ public class TranslationIndex implements Serializable { return (ClassEntry)entry; } + ClassEntry superclassEntry = resolveSuperclass(entry); + if (superclassEntry != null) { + return superclassEntry; + } + + ClassEntry interfaceEntry = resolveInterface(entry); + if (interfaceEntry != null) { + return interfaceEntry; + } + + return null; + } + + public ClassEntry resolveSuperclass(Entry entry) { + // this entry could refer to a method on a class where the method is not actually implemented // travel up the inheritance tree to find the closest implementation while (!entryExists(entry)) { @@ -203,6 +255,19 @@ public class TranslationIndex implements Serializable { return entry.getClassEntry(); } + public ClassEntry resolveInterface(Entry entry) { + + // the interfaces for any class is a forest + // so let's look at all the trees + for (ClassEntry interfaceEntry : m_interfaces.get(entry.getClassEntry())) { + ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry)); + if (resolvedClassEntry != null) { + return resolvedClassEntry; + } + } + return null; + } + private boolean isJre(ClassEntry classEntry) { String packageName = classEntry.getPackageName(); return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); -- cgit v1.2.3 From 808a855be801d101790e370e6fe0368117aba15d Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Sat, 11 Apr 2015 14:06:04 -0400 Subject: save more translation information for m3l --- src/cuchaz/enigma/analysis/TranslationIndex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index e0e66bf7..adfb3faf 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -109,7 +109,7 @@ public class TranslationIndex implements Serializable { // add the superclass ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); - if (superclassEntry != null && !isJre(superclassEntry)) { + if (superclassEntry != null) { m_superclasses.put(classEntry, superclassEntry); } -- cgit v1.2.3 From 51b92b780b57cc955e0618d09fbc3aa95ff47163 Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Sun, 19 Apr 2015 18:51:04 -0400 Subject: relicense Enigma as LGPL --- build.py | 2 +- license.GPL3.txt | 674 --------------------- license.LGPL3.txt | 165 +++++ readme.txt | 6 +- src/cuchaz/enigma/CommandMain.java | 10 + src/cuchaz/enigma/Constants.java | 8 +- src/cuchaz/enigma/ConvertMain.java | 10 + src/cuchaz/enigma/Deobfuscator.java | 9 +- src/cuchaz/enigma/ExceptionIgnorer.java | 10 + src/cuchaz/enigma/Main.java | 8 +- src/cuchaz/enigma/MainFormatConverter.java | 10 + src/cuchaz/enigma/TranslatingTypeLoader.java | 8 +- src/cuchaz/enigma/Util.java | 8 +- src/cuchaz/enigma/analysis/Access.java | 8 +- .../enigma/analysis/BehaviorReferenceTreeNode.java | 8 +- src/cuchaz/enigma/analysis/BridgeMarker.java | 10 + .../analysis/ClassImplementationsTreeNode.java | 8 +- .../enigma/analysis/ClassInheritanceTreeNode.java | 8 +- src/cuchaz/enigma/analysis/EntryReference.java | 8 +- src/cuchaz/enigma/analysis/EntryRenamer.java | 8 +- .../enigma/analysis/FieldReferenceTreeNode.java | 8 +- src/cuchaz/enigma/analysis/JarClassIterator.java | 8 +- src/cuchaz/enigma/analysis/JarIndex.java | 8 +- .../analysis/MethodImplementationsTreeNode.java | 8 +- .../enigma/analysis/MethodInheritanceTreeNode.java | 8 +- src/cuchaz/enigma/analysis/ReferenceTreeNode.java | 8 +- .../enigma/analysis/RelatedMethodChecker.java | 10 + src/cuchaz/enigma/analysis/SourceIndex.java | 8 +- .../analysis/SourceIndexBehaviorVisitor.java | 8 +- .../enigma/analysis/SourceIndexClassVisitor.java | 8 +- src/cuchaz/enigma/analysis/SourceIndexVisitor.java | 8 +- src/cuchaz/enigma/analysis/Token.java | 8 +- src/cuchaz/enigma/analysis/TranslationIndex.java | 8 +- src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 8 +- src/cuchaz/enigma/bytecode/CheckCastIterator.java | 8 +- src/cuchaz/enigma/bytecode/ClassProtectifier.java | 10 + src/cuchaz/enigma/bytecode/ClassPublifier.java | 10 + src/cuchaz/enigma/bytecode/ClassRenamer.java | 8 +- src/cuchaz/enigma/bytecode/ClassTranslator.java | 8 +- src/cuchaz/enigma/bytecode/ConstPoolEditor.java | 8 +- src/cuchaz/enigma/bytecode/InfoType.java | 8 +- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 8 +- .../enigma/bytecode/LocalVariableRenamer.java | 10 + .../enigma/bytecode/MethodParameterWriter.java | 8 +- .../enigma/bytecode/MethodParametersAttribute.java | 8 +- .../bytecode/accessors/ClassInfoAccessor.java | 8 +- .../bytecode/accessors/ConstInfoAccessor.java | 8 +- .../accessors/InvokeDynamicInfoAccessor.java | 8 +- .../bytecode/accessors/MemberRefInfoAccessor.java | 8 +- .../accessors/MethodHandleInfoAccessor.java | 8 +- .../bytecode/accessors/MethodTypeInfoAccessor.java | 8 +- .../accessors/NameAndTypeInfoAccessor.java | 8 +- .../bytecode/accessors/StringInfoAccessor.java | 8 +- .../bytecode/accessors/Utf8InfoAccessor.java | 8 +- src/cuchaz/enigma/convert/ClassForest.java | 10 + src/cuchaz/enigma/convert/ClassIdentifier.java | 10 + src/cuchaz/enigma/convert/ClassIdentity.java | 8 +- src/cuchaz/enigma/convert/ClassMatch.java | 10 + src/cuchaz/enigma/convert/ClassMatches.java | 10 + src/cuchaz/enigma/convert/ClassMatching.java | 8 +- src/cuchaz/enigma/convert/ClassNamer.java | 8 +- src/cuchaz/enigma/convert/FieldMatches.java | 10 + src/cuchaz/enigma/convert/MappingsConverter.java | 8 +- src/cuchaz/enigma/convert/MatchesReader.java | 10 + src/cuchaz/enigma/convert/MatchesWriter.java | 10 + src/cuchaz/enigma/convert/MemberMatches.java | 10 + src/cuchaz/enigma/gui/AboutDialog.java | 8 +- src/cuchaz/enigma/gui/BoxHighlightPainter.java | 8 +- src/cuchaz/enigma/gui/BrowserCaret.java | 8 +- src/cuchaz/enigma/gui/ClassListCellRenderer.java | 8 +- src/cuchaz/enigma/gui/ClassMatchingGui.java | 10 + src/cuchaz/enigma/gui/ClassSelector.java | 8 +- src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 8 +- .../enigma/gui/ClassSelectorPackageNode.java | 8 +- src/cuchaz/enigma/gui/CodeReader.java | 10 + src/cuchaz/enigma/gui/CrashDialog.java | 8 +- .../enigma/gui/DeobfuscatedHighlightPainter.java | 8 +- src/cuchaz/enigma/gui/Gui.java | 8 +- src/cuchaz/enigma/gui/GuiController.java | 8 +- src/cuchaz/enigma/gui/GuiTricks.java | 8 +- src/cuchaz/enigma/gui/MemberMatchingGui.java | 10 + .../enigma/gui/ObfuscatedHighlightPainter.java | 8 +- src/cuchaz/enigma/gui/OtherHighlightPainter.java | 8 +- src/cuchaz/enigma/gui/ProgressDialog.java | 8 +- src/cuchaz/enigma/gui/ReadableToken.java | 8 +- src/cuchaz/enigma/gui/RenameListener.java | 8 +- src/cuchaz/enigma/gui/ScoredClassEntry.java | 10 + .../enigma/gui/SelectionHighlightPainter.java | 8 +- src/cuchaz/enigma/gui/TokenListCellRenderer.java | 8 +- src/cuchaz/enigma/mapping/ArgumentEntry.java | 8 +- src/cuchaz/enigma/mapping/ArgumentMapping.java | 8 +- src/cuchaz/enigma/mapping/BehaviorEntry.java | 8 +- src/cuchaz/enigma/mapping/ClassEntry.java | 8 +- src/cuchaz/enigma/mapping/ClassMapping.java | 8 +- src/cuchaz/enigma/mapping/ClassNameReplacer.java | 10 + src/cuchaz/enigma/mapping/ConstructorEntry.java | 8 +- src/cuchaz/enigma/mapping/Entry.java | 8 +- src/cuchaz/enigma/mapping/EntryFactory.java | 10 + src/cuchaz/enigma/mapping/EntryPair.java | 8 +- src/cuchaz/enigma/mapping/FieldEntry.java | 8 +- src/cuchaz/enigma/mapping/FieldMapping.java | 8 +- .../enigma/mapping/IllegalNameException.java | 8 +- .../enigma/mapping/MappingParseException.java | 8 +- src/cuchaz/enigma/mapping/Mappings.java | 8 +- src/cuchaz/enigma/mapping/MappingsChecker.java | 10 + src/cuchaz/enigma/mapping/MappingsReader.java | 8 +- src/cuchaz/enigma/mapping/MappingsRenamer.java | 8 +- src/cuchaz/enigma/mapping/MappingsWriter.java | 8 +- src/cuchaz/enigma/mapping/MemberMapping.java | 10 + src/cuchaz/enigma/mapping/MethodEntry.java | 8 +- src/cuchaz/enigma/mapping/MethodMapping.java | 8 +- src/cuchaz/enigma/mapping/NameValidator.java | 8 +- src/cuchaz/enigma/mapping/ProcyonEntryFactory.java | 10 + src/cuchaz/enigma/mapping/Signature.java | 10 + src/cuchaz/enigma/mapping/SignatureUpdater.java | 8 +- .../enigma/mapping/TranslationDirection.java | 8 +- src/cuchaz/enigma/mapping/Translator.java | 8 +- src/cuchaz/enigma/mapping/Type.java | 10 + test/cuchaz/enigma/TestDeobfed.java | 10 + test/cuchaz/enigma/TestDeobfuscator.java | 9 +- test/cuchaz/enigma/TestEntryFactory.java | 9 +- test/cuchaz/enigma/TestInnerClasses.java | 9 +- .../enigma/TestJarIndexConstructorReferences.java | 8 +- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 8 +- test/cuchaz/enigma/TestJarIndexLoneClass.java | 9 +- test/cuchaz/enigma/TestSignature.java | 10 + test/cuchaz/enigma/TestSourceIndex.java | 9 +- test/cuchaz/enigma/TestTokensConstructors.java | 8 +- test/cuchaz/enigma/TestTranslator.java | 10 + test/cuchaz/enigma/TestType.java | 10 + test/cuchaz/enigma/TokenChecker.java | 8 +- test/cuchaz/enigma/inputs/Keep.java | 10 + .../enigma/inputs/constructors/BaseClass.java | 10 + test/cuchaz/enigma/inputs/constructors/Caller.java | 10 + .../inputs/constructors/DefaultConstructable.java | 10 + .../enigma/inputs/constructors/SubClass.java | 10 + .../enigma/inputs/constructors/SubSubClass.java | 10 + .../enigma/inputs/inheritanceTree/BaseClass.java | 10 + .../enigma/inputs/inheritanceTree/SubclassA.java | 10 + .../enigma/inputs/inheritanceTree/SubclassB.java | 10 + .../inputs/inheritanceTree/SubsubclassAA.java | 10 + .../enigma/inputs/innerClasses/A_Anonymous.java | 10 + .../innerClasses/B_AnonymousWithScopeArgs.java | 10 + .../inputs/innerClasses/C_ConstructorArgs.java | 10 + .../enigma/inputs/innerClasses/D_Simple.java | 10 + .../innerClasses/E_AnonymousWithOuterAccess.java | 10 + .../enigma/inputs/innerClasses/F_ClassTree.java | 10 + test/cuchaz/enigma/inputs/loneClass/LoneClass.java | 10 + test/cuchaz/enigma/inputs/translation/A_Basic.java | 10 + .../enigma/inputs/translation/B_BaseClass.java | 10 + .../enigma/inputs/translation/C_SubClass.java | 10 + .../inputs/translation/D_AnonymousTesting.java | 10 + .../enigma/inputs/translation/E_Bridges.java | 10 + .../enigma/inputs/translation/F_ObjectMethods.java | 10 + .../enigma/inputs/translation/G_OuterClass.java | 10 + .../enigma/inputs/translation/H_NamelessClass.java | 10 + .../enigma/inputs/translation/I_Generics.java | 10 + 157 files changed, 1129 insertions(+), 1064 deletions(-) delete mode 100644 license.GPL3.txt create mode 100644 license.LGPL3.txt diff --git a/build.py b/build.py index b28e36e9..cfaa7ffa 100644 --- a/build.py +++ b/build.py @@ -68,7 +68,7 @@ def buildDeobfTestJar(outPath, inPath): def applyReadme(dirTemp): ssjb.file.copy(dirTemp, "license.APL2.txt") - ssjb.file.copy(dirTemp, "license.GPL3.txt") + ssjb.file.copy(dirTemp, "license.LGPL3.txt") ssjb.file.copy(dirTemp, "readme.txt") def buildStandaloneJar(dirOut): diff --git a/license.GPL3.txt b/license.GPL3.txt deleted file mode 100644 index 20d40b6b..00000000 --- a/license.GPL3.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. \ No newline at end of file diff --git a/license.LGPL3.txt b/license.LGPL3.txt new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/license.LGPL3.txt @@ -0,0 +1,165 @@ + GNU LESSER 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. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser 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 +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/readme.txt b/readme.txt index 68d13303..47277d91 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ -Enigma v0.8 beta +Enigma v0.10.4 beta A tool for deobfuscation of Java bytecode Copyright Jeff Martin, 2015 @@ -7,7 +7,7 @@ Copyright Jeff Martin, 2015 LICENSE -Enigma is distributed under the GNU General Public license version 3 +Enigma is distributed under the GNU Lesser General Public license version 3 Enigma includes a modified version of Procyon which is distributed under the Apache license version 2. Procyon is copyrighted by Mike Strobel, 2013 @@ -16,7 +16,7 @@ Enigma includes unmodified versions of the following libraries which are also re Javassist JSyntaxPane -Copies of the GNU General Public license verion 3 and the Apache license v2 have been included in this distribution. +Copies of the GNU Lesser General Public license verion 3 and the Apache license v2 have been included in this distribution. USING ENIGMA diff --git a/src/cuchaz/enigma/CommandMain.java b/src/cuchaz/enigma/CommandMain.java index 41528fef..9a980d9b 100644 --- a/src/cuchaz/enigma/CommandMain.java +++ b/src/cuchaz/enigma/CommandMain.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma; import java.io.File; diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java index e8211ec2..951fa8f3 100644 --- a/src/cuchaz/enigma/Constants.java +++ b/src/cuchaz/enigma/Constants.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index 79caae44..17bd2f80 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma; import java.io.File; diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index d9ded878..08a974aa 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -1,10 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin.\ - * + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/ExceptionIgnorer.java b/src/cuchaz/enigma/ExceptionIgnorer.java index 1bb58243..d8726d13 100644 --- a/src/cuchaz/enigma/ExceptionIgnorer.java +++ b/src/cuchaz/enigma/ExceptionIgnorer.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma; public class ExceptionIgnorer { diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index acae94b1..4842a795 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/MainFormatConverter.java b/src/cuchaz/enigma/MainFormatConverter.java index 5db0e539..73ee41f4 100644 --- a/src/cuchaz/enigma/MainFormatConverter.java +++ b/src/cuchaz/enigma/MainFormatConverter.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma; import java.io.File; diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 184ba263..a2185e5c 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/Util.java b/src/cuchaz/enigma/Util.java index 7f04bda0..c7e509fa 100644 --- a/src/cuchaz/enigma/Util.java +++ b/src/cuchaz/enigma/Util.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/Access.java b/src/cuchaz/enigma/analysis/Access.java index 8d3409ac..1c8cfc48 100644 --- a/src/cuchaz/enigma/analysis/Access.java +++ b/src/cuchaz/enigma/analysis/Access.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java index 9adac5e9..353a4bf0 100644 --- a/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/BridgeMarker.java b/src/cuchaz/enigma/analysis/BridgeMarker.java index 28e35171..650b3a78 100644 --- a/src/cuchaz/enigma/analysis/BridgeMarker.java +++ b/src/cuchaz/enigma/analysis/BridgeMarker.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.analysis; import javassist.CtClass; diff --git a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java index 49aac5f0..cc70f51e 100644 --- a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java index 3eaa3912..7542bd9d 100644 --- a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/EntryReference.java b/src/cuchaz/enigma/analysis/EntryReference.java index d0a5c6af..85127239 100644 --- a/src/cuchaz/enigma/analysis/EntryReference.java +++ b/src/cuchaz/enigma/analysis/EntryReference.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java index 2f270494..f748274f 100644 --- a/src/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java index 2173eea6..4ed8fee2 100644 --- a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java index 72a99122..aa58e9ec 100644 --- a/src/cuchaz/enigma/analysis/JarClassIterator.java +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 6b3cf677..5c8ec1c4 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index 6cafc55b..aa0aeca6 100644 --- a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index 87182204..0da3c8c9 100644 --- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java index 2b08616a..4d81bf1c 100644 --- a/src/cuchaz/enigma/analysis/ReferenceTreeNode.java +++ b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/RelatedMethodChecker.java b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java index e231b807..e592a1c3 100644 --- a/src/cuchaz/enigma/analysis/RelatedMethodChecker.java +++ b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.analysis; import java.util.Map; diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index 8f751ef5..3c4ac464 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index 3a176ffc..a660a376 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 37a893d7..db0bc0b7 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java index 0d5bdc02..08698267 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/Token.java b/src/cuchaz/enigma/analysis/Token.java index 481d2f47..76d63276 100644 --- a/src/cuchaz/enigma/analysis/Token.java +++ b/src/cuchaz/enigma/analysis/Token.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index adfb3faf..a491cfce 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java index 23f80899..0a90bacc 100644 --- a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java +++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/CheckCastIterator.java b/src/cuchaz/enigma/bytecode/CheckCastIterator.java index 52845573..517b9d62 100644 --- a/src/cuchaz/enigma/bytecode/CheckCastIterator.java +++ b/src/cuchaz/enigma/bytecode/CheckCastIterator.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/ClassProtectifier.java b/src/cuchaz/enigma/bytecode/ClassProtectifier.java index 49a62690..f1ee4e77 100644 --- a/src/cuchaz/enigma/bytecode/ClassProtectifier.java +++ b/src/cuchaz/enigma/bytecode/ClassProtectifier.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.bytecode; import javassist.CtBehavior; diff --git a/src/cuchaz/enigma/bytecode/ClassPublifier.java b/src/cuchaz/enigma/bytecode/ClassPublifier.java index d8162e46..dbefd426 100644 --- a/src/cuchaz/enigma/bytecode/ClassPublifier.java +++ b/src/cuchaz/enigma/bytecode/ClassPublifier.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.bytecode; import javassist.CtBehavior; diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index e708b9d4..4d95f30e 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 074da2bb..74024598 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java index 2dec3b76..a00b86b5 100644 --- a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java +++ b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/InfoType.java b/src/cuchaz/enigma/bytecode/InfoType.java index deaf6232..08f2b3e2 100644 --- a/src/cuchaz/enigma/bytecode/InfoType.java +++ b/src/cuchaz/enigma/bytecode/InfoType.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 9cdfd685..bdb1b5df 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/LocalVariableRenamer.java b/src/cuchaz/enigma/bytecode/LocalVariableRenamer.java index 215afc5a..ae0455f9 100644 --- a/src/cuchaz/enigma/bytecode/LocalVariableRenamer.java +++ b/src/cuchaz/enigma/bytecode/LocalVariableRenamer.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.bytecode; import javassist.CtBehavior; diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java index 17e4a884..0bdf47a4 100644 --- a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java index 27f5b9b3..512e65a0 100644 --- a/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java +++ b/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java index d76f0567..9072c29a 100644 --- a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java index d00c1021..ede04738 100644 --- a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java index 0d780ea6..82af0b99 100644 --- a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java index 9fe945f7..71ee5b73 100644 --- a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java index 4c95b226..172b0c51 100644 --- a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java index e1511179..0099a843 100644 --- a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java index 6e82f3e9..3ecc1297 100644 --- a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java index 6665ffe4..f150612e 100644 --- a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java index 2abf60b4..38e8ff99 100644 --- a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java +++ b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/ClassForest.java b/src/cuchaz/enigma/convert/ClassForest.java index a5ea0568..0407730e 100644 --- a/src/cuchaz/enigma/convert/ClassForest.java +++ b/src/cuchaz/enigma/convert/ClassForest.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.convert; import java.util.Collection; diff --git a/src/cuchaz/enigma/convert/ClassIdentifier.java b/src/cuchaz/enigma/convert/ClassIdentifier.java index 2e6217b3..ee5e9033 100644 --- a/src/cuchaz/enigma/convert/ClassIdentifier.java +++ b/src/cuchaz/enigma/convert/ClassIdentifier.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.convert; import java.util.Map; diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index 35667b05..2e164ae7 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/ClassMatch.java b/src/cuchaz/enigma/convert/ClassMatch.java index eaaaa416..8c50a624 100644 --- a/src/cuchaz/enigma/convert/ClassMatch.java +++ b/src/cuchaz/enigma/convert/ClassMatch.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.convert; import java.util.Collection; diff --git a/src/cuchaz/enigma/convert/ClassMatches.java b/src/cuchaz/enigma/convert/ClassMatches.java index f8b2afdc..f70c1805 100644 --- a/src/cuchaz/enigma/convert/ClassMatches.java +++ b/src/cuchaz/enigma/convert/ClassMatches.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.convert; import java.util.ArrayList; diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java index d8973ac5..633d1ac7 100644 --- a/src/cuchaz/enigma/convert/ClassMatching.java +++ b/src/cuchaz/enigma/convert/ClassMatching.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/ClassNamer.java b/src/cuchaz/enigma/convert/ClassNamer.java index 6423a189..e8fa7303 100644 --- a/src/cuchaz/enigma/convert/ClassNamer.java +++ b/src/cuchaz/enigma/convert/ClassNamer.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/FieldMatches.java b/src/cuchaz/enigma/convert/FieldMatches.java index 2973356b..8439a84c 100644 --- a/src/cuchaz/enigma/convert/FieldMatches.java +++ b/src/cuchaz/enigma/convert/FieldMatches.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.convert; import java.util.Collection; diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index b3ac1cf4..b457d6c4 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/convert/MatchesReader.java b/src/cuchaz/enigma/convert/MatchesReader.java index 7c823054..7514e2a9 100644 --- a/src/cuchaz/enigma/convert/MatchesReader.java +++ b/src/cuchaz/enigma/convert/MatchesReader.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.convert; import java.io.BufferedReader; diff --git a/src/cuchaz/enigma/convert/MatchesWriter.java b/src/cuchaz/enigma/convert/MatchesWriter.java index 9e9ead02..42c6b61b 100644 --- a/src/cuchaz/enigma/convert/MatchesWriter.java +++ b/src/cuchaz/enigma/convert/MatchesWriter.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.convert; import java.io.File; diff --git a/src/cuchaz/enigma/convert/MemberMatches.java b/src/cuchaz/enigma/convert/MemberMatches.java index 1c162eac..29def159 100644 --- a/src/cuchaz/enigma/convert/MemberMatches.java +++ b/src/cuchaz/enigma/convert/MemberMatches.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.convert; import java.util.Collection; diff --git a/src/cuchaz/enigma/gui/AboutDialog.java b/src/cuchaz/enigma/gui/AboutDialog.java index 2476b564..3eba1e50 100644 --- a/src/cuchaz/enigma/gui/AboutDialog.java +++ b/src/cuchaz/enigma/gui/AboutDialog.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/cuchaz/enigma/gui/BoxHighlightPainter.java index db7c85b4..e5e05571 100644 --- a/src/cuchaz/enigma/gui/BoxHighlightPainter.java +++ b/src/cuchaz/enigma/gui/BoxHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/BrowserCaret.java b/src/cuchaz/enigma/gui/BrowserCaret.java index acee4833..6af4d248 100644 --- a/src/cuchaz/enigma/gui/BrowserCaret.java +++ b/src/cuchaz/enigma/gui/BrowserCaret.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ClassListCellRenderer.java b/src/cuchaz/enigma/gui/ClassListCellRenderer.java index d0f01e6a..cde3e4ca 100644 --- a/src/cuchaz/enigma/gui/ClassListCellRenderer.java +++ b/src/cuchaz/enigma/gui/ClassListCellRenderer.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ClassMatchingGui.java b/src/cuchaz/enigma/gui/ClassMatchingGui.java index 6f8feac3..89b19c3a 100644 --- a/src/cuchaz/enigma/gui/ClassMatchingGui.java +++ b/src/cuchaz/enigma/gui/ClassMatchingGui.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.gui; import java.awt.BorderLayout; diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java index 2a636754..11333a96 100644 --- a/src/cuchaz/enigma/gui/ClassSelector.java +++ b/src/cuchaz/enigma/gui/ClassSelector.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java index 6c1c9a0a..1219e890 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java index 5685abbf..7259f54d 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorPackageNode.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/CodeReader.java b/src/cuchaz/enigma/gui/CodeReader.java index d8fb394b..5033a2cd 100644 --- a/src/cuchaz/enigma/gui/CodeReader.java +++ b/src/cuchaz/enigma/gui/CodeReader.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.gui; import java.awt.Rectangle; diff --git a/src/cuchaz/enigma/gui/CrashDialog.java b/src/cuchaz/enigma/gui/CrashDialog.java index 360091ab..904273c1 100644 --- a/src/cuchaz/enigma/gui/CrashDialog.java +++ b/src/cuchaz/enigma/gui/CrashDialog.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java index 26a31639..57210a84 100644 --- a/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java +++ b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 27b06f97..f9192d31 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 552ee47c..66906227 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java index 7e539a12..5dc3ffb3 100644 --- a/src/cuchaz/enigma/gui/GuiTricks.java +++ b/src/cuchaz/enigma/gui/GuiTricks.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/MemberMatchingGui.java b/src/cuchaz/enigma/gui/MemberMatchingGui.java index 65e6dd9e..150eaadb 100644 --- a/src/cuchaz/enigma/gui/MemberMatchingGui.java +++ b/src/cuchaz/enigma/gui/MemberMatchingGui.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.gui; import java.awt.BorderLayout; diff --git a/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java index 177835f4..4c3714a9 100644 --- a/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java +++ b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/OtherHighlightPainter.java b/src/cuchaz/enigma/gui/OtherHighlightPainter.java index 4e9c8709..8d3fbe86 100644 --- a/src/cuchaz/enigma/gui/OtherHighlightPainter.java +++ b/src/cuchaz/enigma/gui/OtherHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ProgressDialog.java b/src/cuchaz/enigma/gui/ProgressDialog.java index b864fdbf..1c20f10b 100644 --- a/src/cuchaz/enigma/gui/ProgressDialog.java +++ b/src/cuchaz/enigma/gui/ProgressDialog.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ReadableToken.java b/src/cuchaz/enigma/gui/ReadableToken.java index 66bcbc2a..0741af39 100644 --- a/src/cuchaz/enigma/gui/ReadableToken.java +++ b/src/cuchaz/enigma/gui/ReadableToken.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/RenameListener.java b/src/cuchaz/enigma/gui/RenameListener.java index abeda0ce..8b515bbd 100644 --- a/src/cuchaz/enigma/gui/RenameListener.java +++ b/src/cuchaz/enigma/gui/RenameListener.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/ScoredClassEntry.java b/src/cuchaz/enigma/gui/ScoredClassEntry.java index dd7ba619..60704528 100644 --- a/src/cuchaz/enigma/gui/ScoredClassEntry.java +++ b/src/cuchaz/enigma/gui/ScoredClassEntry.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.gui; import cuchaz.enigma.mapping.ClassEntry; diff --git a/src/cuchaz/enigma/gui/SelectionHighlightPainter.java b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java index 5e189d2e..4165da4a 100644 --- a/src/cuchaz/enigma/gui/SelectionHighlightPainter.java +++ b/src/cuchaz/enigma/gui/SelectionHighlightPainter.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/gui/TokenListCellRenderer.java b/src/cuchaz/enigma/gui/TokenListCellRenderer.java index a49be37b..e4f7c873 100644 --- a/src/cuchaz/enigma/gui/TokenListCellRenderer.java +++ b/src/cuchaz/enigma/gui/TokenListCellRenderer.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java index aa222652..9d99016e 100644 --- a/src/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ArgumentMapping.java b/src/cuchaz/enigma/mapping/ArgumentMapping.java index 9f366a04..a0055a63 100644 --- a/src/cuchaz/enigma/mapping/ArgumentMapping.java +++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/BehaviorEntry.java b/src/cuchaz/enigma/mapping/BehaviorEntry.java index 535788f2..031d2670 100644 --- a/src/cuchaz/enigma/mapping/BehaviorEntry.java +++ b/src/cuchaz/enigma/mapping/BehaviorEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 4d977b1b..373203f0 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 6e7fd17b..0b0105ec 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ClassNameReplacer.java b/src/cuchaz/enigma/mapping/ClassNameReplacer.java index bf984fd3..f00d811e 100644 --- a/src/cuchaz/enigma/mapping/ClassNameReplacer.java +++ b/src/cuchaz/enigma/mapping/ClassNameReplacer.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.mapping; public interface ClassNameReplacer { diff --git a/src/cuchaz/enigma/mapping/ConstructorEntry.java b/src/cuchaz/enigma/mapping/ConstructorEntry.java index 5f3760f3..7cde8f65 100644 --- a/src/cuchaz/enigma/mapping/ConstructorEntry.java +++ b/src/cuchaz/enigma/mapping/ConstructorEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/Entry.java b/src/cuchaz/enigma/mapping/Entry.java index 39e1507d..3c94a95a 100644 --- a/src/cuchaz/enigma/mapping/Entry.java +++ b/src/cuchaz/enigma/mapping/Entry.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index af96eb35..03d97ba1 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.mapping; import javassist.CtBehavior; diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java index 60411c40..82b28cd1 100644 --- a/src/cuchaz/enigma/mapping/EntryPair.java +++ b/src/cuchaz/enigma/mapping/EntryPair.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java index 7517254c..e4a74f4f 100644 --- a/src/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java index 463d901c..28557406 100644 --- a/src/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java index aacaf3b0..f62df7c4 100644 --- a/src/cuchaz/enigma/mapping/IllegalNameException.java +++ b/src/cuchaz/enigma/mapping/IllegalNameException.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MappingParseException.java b/src/cuchaz/enigma/mapping/MappingParseException.java index 1974c222..73fca94a 100644 --- a/src/cuchaz/enigma/mapping/MappingParseException.java +++ b/src/cuchaz/enigma/mapping/MappingParseException.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index d802139d..11ed5d0c 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MappingsChecker.java b/src/cuchaz/enigma/mapping/MappingsChecker.java index b9feee4e..b25ea3cf 100644 --- a/src/cuchaz/enigma/mapping/MappingsChecker.java +++ b/src/cuchaz/enigma/mapping/MappingsChecker.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.mapping; import java.util.Map; diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java index bfb27319..0a4b117e 100644 --- a/src/cuchaz/enigma/mapping/MappingsReader.java +++ b/src/cuchaz/enigma/mapping/MappingsReader.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index ad6c8785..47e5738c 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java index 8b62db8c..1ebefefa 100644 --- a/src/cuchaz/enigma/mapping/MappingsWriter.java +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MemberMapping.java b/src/cuchaz/enigma/mapping/MemberMapping.java index d8e2ee3a..83782975 100644 --- a/src/cuchaz/enigma/mapping/MemberMapping.java +++ b/src/cuchaz/enigma/mapping/MemberMapping.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.mapping; diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java index 057e02b9..eb9e2043 100644 --- a/src/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index bef232e3..055e1fe1 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java index 35a17f90..12520e12 100644 --- a/src/cuchaz/enigma/mapping/NameValidator.java +++ b/src/cuchaz/enigma/mapping/NameValidator.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/ProcyonEntryFactory.java b/src/cuchaz/enigma/mapping/ProcyonEntryFactory.java index eb0563c2..777a12e4 100644 --- a/src/cuchaz/enigma/mapping/ProcyonEntryFactory.java +++ b/src/cuchaz/enigma/mapping/ProcyonEntryFactory.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.mapping; import com.strobel.assembler.metadata.FieldDefinition; diff --git a/src/cuchaz/enigma/mapping/Signature.java b/src/cuchaz/enigma/mapping/Signature.java index d1e89b20..8f2b6b2e 100644 --- a/src/cuchaz/enigma/mapping/Signature.java +++ b/src/cuchaz/enigma/mapping/Signature.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.mapping; import java.io.Serializable; diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java index 3477cd56..eb53233e 100644 --- a/src/cuchaz/enigma/mapping/SignatureUpdater.java +++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/TranslationDirection.java b/src/cuchaz/enigma/mapping/TranslationDirection.java index d1b14cd5..bc3aaa13 100644 --- a/src/cuchaz/enigma/mapping/TranslationDirection.java +++ b/src/cuchaz/enigma/mapping/TranslationDirection.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 5d174947..41c7d7cc 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/src/cuchaz/enigma/mapping/Type.java b/src/cuchaz/enigma/mapping/Type.java index 3f441e22..f86a5ccc 100644 --- a/src/cuchaz/enigma/mapping/Type.java +++ b/src/cuchaz/enigma/mapping/Type.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.mapping; import java.io.Serializable; diff --git a/test/cuchaz/enigma/TestDeobfed.java b/test/cuchaz/enigma/TestDeobfed.java index ca349d7c..f4093eef 100644 --- a/test/cuchaz/enigma/TestDeobfed.java +++ b/test/cuchaz/enigma/TestDeobfed.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma; diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java index 26d492d8..bb2a4a5b 100644 --- a/test/cuchaz/enigma/TestDeobfuscator.java +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -1,10 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin.\ - * + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestEntryFactory.java b/test/cuchaz/enigma/TestEntryFactory.java index 754f3081..4aa773b6 100644 --- a/test/cuchaz/enigma/TestEntryFactory.java +++ b/test/cuchaz/enigma/TestEntryFactory.java @@ -1,10 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin.\ - * + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java index 014a4613..41dee4d9 100644 --- a/test/cuchaz/enigma/TestInnerClasses.java +++ b/test/cuchaz/enigma/TestInnerClasses.java @@ -1,10 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java index e1a30226..1c6ead17 100644 --- a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 6e2c1ad3..04d4fb23 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index 0c126ad0..2889241f 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -1,10 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestSignature.java b/test/cuchaz/enigma/TestSignature.java index 649b76dd..8537adfb 100644 --- a/test/cuchaz/enigma/TestSignature.java +++ b/test/cuchaz/enigma/TestSignature.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma; import static org.hamcrest.MatcherAssert.*; diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java index 94bf941d..9329b711 100644 --- a/test/cuchaz/enigma/TestSourceIndex.java +++ b/test/cuchaz/enigma/TestSourceIndex.java @@ -1,10 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java index a563f832..07a37caa 100644 --- a/test/cuchaz/enigma/TestTokensConstructors.java +++ b/test/cuchaz/enigma/TestTokensConstructors.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/TestTranslator.java b/test/cuchaz/enigma/TestTranslator.java index 1b617405..668fe05f 100644 --- a/test/cuchaz/enigma/TestTranslator.java +++ b/test/cuchaz/enigma/TestTranslator.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma; import static cuchaz.enigma.TestEntryFactory.*; diff --git a/test/cuchaz/enigma/TestType.java b/test/cuchaz/enigma/TestType.java index 71aaaee2..01c235b8 100644 --- a/test/cuchaz/enigma/TestType.java +++ b/test/cuchaz/enigma/TestType.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma; import static cuchaz.enigma.TestEntryFactory.*; diff --git a/test/cuchaz/enigma/TokenChecker.java b/test/cuchaz/enigma/TokenChecker.java index a72c2fc8..7afb4cfe 100644 --- a/test/cuchaz/enigma/TokenChecker.java +++ b/test/cuchaz/enigma/TokenChecker.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2014 Jeff Martin. + * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation diff --git a/test/cuchaz/enigma/inputs/Keep.java b/test/cuchaz/enigma/inputs/Keep.java index 390e82fb..f04875f5 100644 --- a/test/cuchaz/enigma/inputs/Keep.java +++ b/test/cuchaz/enigma/inputs/Keep.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs; public class Keep { diff --git a/test/cuchaz/enigma/inputs/constructors/BaseClass.java b/test/cuchaz/enigma/inputs/constructors/BaseClass.java index 93453086..65e782a2 100644 --- a/test/cuchaz/enigma/inputs/constructors/BaseClass.java +++ b/test/cuchaz/enigma/inputs/constructors/BaseClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.constructors; // none/a diff --git a/test/cuchaz/enigma/inputs/constructors/Caller.java b/test/cuchaz/enigma/inputs/constructors/Caller.java index 57278751..75096ec1 100644 --- a/test/cuchaz/enigma/inputs/constructors/Caller.java +++ b/test/cuchaz/enigma/inputs/constructors/Caller.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.constructors; // none/b diff --git a/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java index 26a3ddbb..655f4da3 100644 --- a/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java +++ b/test/cuchaz/enigma/inputs/constructors/DefaultConstructable.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.constructors; public class DefaultConstructable { diff --git a/test/cuchaz/enigma/inputs/constructors/SubClass.java b/test/cuchaz/enigma/inputs/constructors/SubClass.java index fecfa2b5..b0fb3e9b 100644 --- a/test/cuchaz/enigma/inputs/constructors/SubClass.java +++ b/test/cuchaz/enigma/inputs/constructors/SubClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.constructors; // none/d extends none/a diff --git a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java index ab84161b..50314050 100644 --- a/test/cuchaz/enigma/inputs/constructors/SubSubClass.java +++ b/test/cuchaz/enigma/inputs/constructors/SubSubClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.constructors; // none/e extends none/d diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java index 5b416c41..4f9c5b0e 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.inheritanceTree; // none/a diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java index 7a99d516..140d2a81 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.inheritanceTree; // none/b extends none/a diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java index c9485d31..99d149bb 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.inheritanceTree; // none/c extends none/a diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java index afd03ac4..2e414b75 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.inheritanceTree; // none/d extends none/b diff --git a/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java b/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java index f7118f63..f6444396 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java +++ b/test/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; public class A_Anonymous { diff --git a/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java b/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java index 42fba9a8..d78be847 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java +++ b/test/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; public class B_AnonymousWithScopeArgs { diff --git a/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java b/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java index 8fa6c5b8..eb03489d 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java +++ b/test/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; @SuppressWarnings("unused") diff --git a/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java b/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java index c4fc0ef9..0e9bf827 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java +++ b/test/cuchaz/enigma/inputs/innerClasses/D_Simple.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; public class D_Simple { diff --git a/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java b/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java index e1de53cb..255434d1 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java +++ b/test/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; public class E_AnonymousWithOuterAccess { diff --git a/test/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java b/test/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java index 6552d8a6..7d1dab41 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java +++ b/test/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.innerClasses; diff --git a/test/cuchaz/enigma/inputs/loneClass/LoneClass.java b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java index 18c716e9..bf264fa5 100644 --- a/test/cuchaz/enigma/inputs/loneClass/LoneClass.java +++ b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.loneClass; public class LoneClass { diff --git a/test/cuchaz/enigma/inputs/translation/A_Basic.java b/test/cuchaz/enigma/inputs/translation/A_Basic.java index 89307630..26acac8a 100644 --- a/test/cuchaz/enigma/inputs/translation/A_Basic.java +++ b/test/cuchaz/enigma/inputs/translation/A_Basic.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.translation; public class A_Basic { diff --git a/test/cuchaz/enigma/inputs/translation/B_BaseClass.java b/test/cuchaz/enigma/inputs/translation/B_BaseClass.java index 44fbc8db..035e3299 100644 --- a/test/cuchaz/enigma/inputs/translation/B_BaseClass.java +++ b/test/cuchaz/enigma/inputs/translation/B_BaseClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.translation; public class B_BaseClass { diff --git a/test/cuchaz/enigma/inputs/translation/C_SubClass.java b/test/cuchaz/enigma/inputs/translation/C_SubClass.java index 8fe0b799..6026a8d5 100644 --- a/test/cuchaz/enigma/inputs/translation/C_SubClass.java +++ b/test/cuchaz/enigma/inputs/translation/C_SubClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.translation; public class C_SubClass extends B_BaseClass { diff --git a/test/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java b/test/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java index a1166e20..a1827f98 100644 --- a/test/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java +++ b/test/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.translation; import java.util.ArrayList; diff --git a/test/cuchaz/enigma/inputs/translation/E_Bridges.java b/test/cuchaz/enigma/inputs/translation/E_Bridges.java index dac50d39..769eb70e 100644 --- a/test/cuchaz/enigma/inputs/translation/E_Bridges.java +++ b/test/cuchaz/enigma/inputs/translation/E_Bridges.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.translation; import java.util.Iterator; diff --git a/test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java b/test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java index 4e091797..32c246cb 100644 --- a/test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java +++ b/test/cuchaz/enigma/inputs/translation/F_ObjectMethods.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.translation; public class F_ObjectMethods { diff --git a/test/cuchaz/enigma/inputs/translation/G_OuterClass.java b/test/cuchaz/enigma/inputs/translation/G_OuterClass.java index 0856afed..a2e0dafb 100644 --- a/test/cuchaz/enigma/inputs/translation/G_OuterClass.java +++ b/test/cuchaz/enigma/inputs/translation/G_OuterClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.translation; diff --git a/test/cuchaz/enigma/inputs/translation/H_NamelessClass.java b/test/cuchaz/enigma/inputs/translation/H_NamelessClass.java index 5802d789..1b718a54 100644 --- a/test/cuchaz/enigma/inputs/translation/H_NamelessClass.java +++ b/test/cuchaz/enigma/inputs/translation/H_NamelessClass.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.translation; diff --git a/test/cuchaz/enigma/inputs/translation/I_Generics.java b/test/cuchaz/enigma/inputs/translation/I_Generics.java index 191931a8..3490f9d9 100644 --- a/test/cuchaz/enigma/inputs/translation/I_Generics.java +++ b/test/cuchaz/enigma/inputs/translation/I_Generics.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma.inputs.translation; import java.util.List; -- cgit v1.2.3 From c7a3de184dc51526ee157d47981a74ffba284f5d Mon Sep 17 00:00:00 2001 From: Cuchaz Date: Sun, 24 May 2015 11:23:36 -0400 Subject: fix broken tests, and one broken function. =) --- src/cuchaz/enigma/analysis/JarIndex.java | 4 +++- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 27 +++++++++++++++------- test/cuchaz/enigma/TestJarIndexLoneClass.java | 10 ++++---- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 5c8ec1c4..7e3c1b59 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -567,7 +567,9 @@ public class JarIndex { List ancestry = Lists.newArrayList(); ancestry.add(obfClassEntry.getName()); for (ClassEntry classEntry : m_translationIndex.getAncestry(obfClassEntry)) { - ancestry.add(classEntry.getName()); + if (containsObfClass(classEntry)) { + ancestry.add(classEntry.getName()); + } } ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 04d4fb23..d3c23eaf 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -10,9 +10,19 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.TestEntryFactory.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; +import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByConstructor; +import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod; +import static cuchaz.enigma.TestEntryFactory.newClass; +import static cuchaz.enigma.TestEntryFactory.newConstructor; +import static cuchaz.enigma.TestEntryFactory.newField; +import static cuchaz.enigma.TestEntryFactory.newFieldReferenceByConstructor; +import static cuchaz.enigma.TestEntryFactory.newFieldReferenceByMethod; +import static cuchaz.enigma.TestEntryFactory.newMethod; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; import java.util.Collection; import java.util.Set; @@ -33,6 +43,7 @@ public class TestJarIndexInheritanceTree { private JarIndex m_index; + private ClassEntry m_objectClass = newClass("java/lang/Object"); private ClassEntry m_baseClass = newClass("none/a"); private ClassEntry m_subClassA = newClass("none/b"); private ClassEntry m_subClassAA = newClass("none/d"); @@ -63,8 +74,8 @@ public class TestJarIndexInheritanceTree { TranslationIndex index = m_index.getTranslationIndex(); // base class - assertThat(index.getSuperclass(m_baseClass), is(nullValue())); - assertThat(index.getAncestry(m_baseClass), is(empty())); + assertThat(index.getSuperclass(m_baseClass), is(m_objectClass)); + assertThat(index.getAncestry(m_baseClass), contains(m_objectClass)); assertThat(index.getSubclass(m_baseClass), containsInAnyOrder( m_subClassA, m_subClassB @@ -72,17 +83,17 @@ public class TestJarIndexInheritanceTree { // subclass a assertThat(index.getSuperclass(m_subClassA), is(m_baseClass)); - assertThat(index.getAncestry(m_subClassA), contains(m_baseClass)); + assertThat(index.getAncestry(m_subClassA), contains(m_baseClass, m_objectClass)); assertThat(index.getSubclass(m_subClassA), contains(m_subClassAA)); // subclass aa assertThat(index.getSuperclass(m_subClassAA), is(m_subClassA)); - assertThat(index.getAncestry(m_subClassAA), contains(m_subClassA, m_baseClass)); + assertThat(index.getAncestry(m_subClassAA), contains(m_subClassA, m_baseClass, m_objectClass)); assertThat(index.getSubclass(m_subClassAA), is(empty())); // subclass b assertThat(index.getSuperclass(m_subClassB), is(m_baseClass)); - assertThat(index.getAncestry(m_subClassB), contains(m_baseClass)); + assertThat(index.getAncestry(m_subClassB), contains(m_baseClass, m_objectClass)); assertThat(index.getSubclass(m_subClassB), is(empty())); } diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index 2889241f..c97b2efa 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -52,10 +52,10 @@ public class TestJarIndexLoneClass { @Test public void translationIndex() { - assertThat(m_index.getTranslationIndex().getSuperclass(new ClassEntry("none/a")), is(nullValue())); - assertThat(m_index.getTranslationIndex().getSuperclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(nullValue())); - assertThat(m_index.getTranslationIndex().getAncestry(new ClassEntry("none/a")), is(empty())); - assertThat(m_index.getTranslationIndex().getAncestry(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty())); + assertThat(m_index.getTranslationIndex().getSuperclass(new ClassEntry("none/a")), is(new ClassEntry("java/lang/Object"))); + assertThat(m_index.getTranslationIndex().getSuperclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(new ClassEntry("java/lang/Object"))); + assertThat(m_index.getTranslationIndex().getAncestry(new ClassEntry("none/a")), contains(new ClassEntry("java/lang/Object"))); + assertThat(m_index.getTranslationIndex().getAncestry(new ClassEntry("cuchaz/enigma/inputs/Keep")), contains(new ClassEntry("java/lang/Object"))); assertThat(m_index.getTranslationIndex().getSubclass(new ClassEntry("none/a")), is(empty())); assertThat(m_index.getTranslationIndex().getSubclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty())); } @@ -152,7 +152,7 @@ public class TestJarIndexLoneClass { } @Test - public void contains() { + public void testContains() { assertThat(m_index.containsObfClass(newClass("none/a")), is(true)); assertThat(m_index.containsObfClass(newClass("none/b")), is(false)); assertThat(m_index.containsObfField(newField("none/a", "a", "Ljava/lang/String;")), is(true)); -- cgit v1.2.3